Fundamentals 9 min read

Master SOLID Principles: Why Understanding Principles Beats Memorizing Patterns

This article explains why developers should first grasp the seven SOLID principles before diving into design patterns, illustrating each principle with clear Java examples, before‑and‑after code, and practical benefits such as reduced risk, easier testing, looser coupling, and greater extensibility.

ITFLY8 Architecture Home
ITFLY8 Architecture Home
ITFLY8 Architecture Home
Master SOLID Principles: Why Understanding Principles Beats Memorizing Patterns

01 Why You Must Grasp Principles Before Patterns?

Many beginners jump straight into memorizing 23 design patterns and end up more confused.

All design patterns revolve around the seven SOLID principles—master them and patterns become simple tools.

02 The Seven Principles, One by One

① Single Responsibility Principle (SRP)

One class should do only one thing.

Key hint: If a class name contains “And” or “Or”, it likely violates SRP.

Before (bad example)

class UserServiceAndReport {
    void register() { /* registration logic */ }
    void exportPDF() { /* report export logic */ }
}

After (SRP compliant)

// Only handles user registration
class UserService {
    void register(User user) { /* registration logic */ }
}

// Only handles report export
class ReportService {
    void exportPDF(User user) { /* export logic */ }
}

Notes:

Split registration and report into two classes with single responsibilities.

Modifying registration logic no longer affects report functionality, reducing risk.

Unit tests become more granular, making bug location easier.

② Open/Closed Principle (OCP)

Open for extension, closed for modification.

Key hint: Add new code instead of changing existing code.

Scenario: Order discount system

// Abstract strategy
interface DiscountStrategy {
    BigDecimal apply(BigDecimal price);
}

// New requirement: Double Eleven discount
class DoubleElevenDiscount implements DiscountStrategy {
    public BigDecimal apply(BigDecimal price) {
        return price.multiply(new BigDecimal("0.8"));
    }
}

// Client
class OrderService {
    BigDecimal pay(BigDecimal price, DiscountStrategy strategy) {
        return strategy.apply(price);
    }
}

Notes:

Separate registration and report into two classes, responsibilities stay single.

Adding a new discount does not touch OrderService.

All discount implementations share the same interface, making extension points clear and reducing regression‑test cost.

③ Liskov Substitution Principle (LSP)

Subtypes must be replaceable for their base types without altering correctness.

Key hint: Do not force a square into a rectangle’s interface.

Correct example

// Define interface
interface Shape {
    int area();
}

// Rectangle
class Rectangle implements Shape {
    int width, height;
    public int area() { return width * height; }
}

// Square (satisfies LSP)
class Square implements Shape {
    int side;
    public int area() { return side * side; }
}

Notes:

Square does not inherit from Rectangle, avoiding width‑set side effects.

Both shapes implement the same interface, so any Shape can be used uniformly.

Adhering to LSP eliminates awkward runtime type checks.

④ Interface Segregation Principle (ISP)

Clients should not depend on methods they do not use.

Key hint: Fat interfaces lead to fat bugs.

Before (fat interface)

interface Machine {
    void print();
    void scan();
    void fax();
}

After (segregated)

interface Printer { void print(); }
interface Scanner { void scan(); }
interface Fax { void fax(); }

class AllInOne implements Printer, Scanner, Fax {
    /* actual implementations */
}

Notes:

Old printer only implements Printer, no need to provide empty scan/fax.

Smaller interfaces reduce mental load on implementers.

Following ISP improves code maintainability.

⑤ Dependency Inversion Principle (DIP)

High‑level modules should not depend on low‑level modules; both should depend on abstractions.

Key hint: Program to interfaces, not implementations.

Example: Notification system

// Abstraction
interface Notifier {
    void send(String msg);
}

// Low‑level implementation
class EmailNotifier implements Notifier {
    public void send(String msg) { /* send email */ }
}

// High‑level business
class OrderService {
    private final Notifier notifier; // depends on abstraction
    OrderService(Notifier n) { this.notifier = n; }
    void finishOrder() { notifier.send("Order completed"); }
}

Notes:

OrderService only knows Notifier, not whether it is email or SMS.

Switching to SmsNotifier requires no changes to OrderService.

System becomes loosely coupled.

⑥ Law of Demeter (LoD)

Talk only to your immediate friends.

Key hint: Chained calls like a.getB().getC().do() are a red flag.

Bad example

class Wallet { public Money money; }
class Person { public Wallet wallet; }
// Client
person.getWallet().money.reduce(100);

Good example

class Person {
    private Wallet wallet;
    public void pay(int amount) { wallet.pay(amount); }
}
// Client
person.pay(100);

Notes:

Client only interacts with Person, unaware of Wallet.

Internal changes to Wallet do not affect callers.

Adhering to LoD makes code more readable and safer.

⑦ Composite Reuse Principle (CRP)

Prefer composition over inheritance.

Key hint: Inheritance is a “son”, composition is an “adopted son” and is more flexible.

Inheritance vs Composition

// Inheritance – tight coupling
class Bird extends Flyable { }

// Composition – loose coupling
class Bird {
    private FlyBehavior flyBehavior; // composition
    void fly() { flyBehavior.fly(); }
}

Notes:

Composition lets Bird change its flying strategy at runtime.

Avoids the “class explosion” of deep inheritance hierarchies.

Reflects CRP, maximizing system extensibility.

Summary

Single Responsibility → Split!
Open/Closed → Add!
Liskov Substitution → Replace!
Interface Segregation → Slim!
Dependency Inversion → Abstract!
Law of Demeter → Speak less!
Composite Reuse → Compose!

Mnemonic: “Split, add, replace, slim, abstract, speak less, compose” — stay on the right architectural path!

Source: Translated from a public article on the “程序语言” public account.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javasoftware architectureCode examplesOOPDesign PrinciplesSOLID
ITFLY8 Architecture Home
Written by

ITFLY8 Architecture Home

ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.