Master the Seven Design Principles Before Tackling Design Patterns
The article explains that design patterns rely on solid design principles—SRP, OCP, LSP, DIP, ISP, LoD, and CARP—using a flawed order‑processing example to show each principle, how to refactor the code, and why over‑engineering must be avoided.
What design patterns fight
Design patterns are useful only when requirements change and we need to reduce coupling . If code never changes, a single monolithic method would be sufficient.
Bad example: a runnable but ugly OrderService.createOrder
The initial implementation mixes four responsibilities—price calculation, payment, persistence, and notification—into one method.
public class OrderService {
public void createOrder(long userId, long skuId, int count, String payType) {
// 1. Calculate price directly via DB
Connection conn = DriverManager.getConnection("jdbc:mysql://...", "root", "123456");
double amount = queryPrice(conn, skuId) * count;
// 2. Payment with a chain of if‑else
if (payType.equals("wechat")) {
System.out.println("Call WeChat Pay " + amount);
} else if (payType.equals("alipay")) {
System.out.println("Call Alipay Pay " + amount);
} else if (payType.equals("unionpay")) {
System.out.println("Call UnionPay " + amount);
}
// 3. Persist directly
String sql = "INSERT INTO orders ...";
// 4. Build SMS content
String content = "Your order is created, amount " + amount + " yuan";
System.out.println("Send SMS: " + content);
}
}Although it runs, any change to payment channel, database, SMS text, or pricing rule forces a modification of the same method, violating all seven SOLID‑related principles.
1. Single Responsibility Principle (SRP)
SRP requires a class to have only one reason to change. The original createOrder has four reasons. Refactoring extracts four dedicated classes— PriceCalculator, PaymentService, OrderRepository, and NotifyService —and lets OrderService merely orchestrate them.
class PriceCalculator { /* only calculates price */ }
class PaymentService { /* only handles payment */ }
class OrderRepository { /* only persists orders */ }
class NotifyService { /* only sends notifications */ }
public class OrderService {
private final PriceCalculator priceCalculator = new PriceCalculator();
private final PaymentService paymentService = new PaymentService();
private final OrderRepository orderRepository = new OrderRepository();
private final NotifyService notifyService = new NotifyService();
public void createOrder(long userId, long skuId, int count, String payType) {
double amount = priceCalculator.calculate(skuId, count);
paymentService.pay(payType, amount);
orderRepository.save(/* order data */);
notifyService.notify(userId, amount);
}
}Now a change to payment logic touches only PaymentService, isolating the impact.
Honest reminder: OCP cannot be applied 100 % because future changes are unpredictable. Abstract only where a high probability of extension is identified; otherwise avoid premature abstraction.
2. Open‑Closed Principle (OCP)
OCP states that software entities should be open for extension but closed for modification . The payment part is refactored to depend on an abstract Payment interface; each channel implements this interface.
public interface Payment { void pay(double amount); }
class WechatPayment implements Payment { public void pay(double amount) { /* … */ } }
class AlipayPayment implements Payment { public void pay(double amount) { /* … */ } }
class UnionPayment implements Payment { public void pay(double amount) { /* … */ } }
// Adding a new channel (e.g., digital‑RMB) requires only a new class:
class DcepPayment implements Payment { public void pay(double amount) { /* … */ } } PaymentServicenow works with any Payment implementation without changing existing code.
3. Liskov Substitution Principle (LSP)
LSP requires that subclasses be replaceable for their base class without altering program behavior. The article shows a broken example where PreSaleOrder overrides setDiscount to throw an exception, breaking callers that expect the original contract.
class Order {
void setDiscount(double d) {
if (d < 0 || d > 1) throw new IllegalArgumentException("Invalid discount");
this.discount = d;
}
}
class PreSaleOrder extends Order {
@Override
void setDiscount(double d) {
throw new UnsupportedOperationException("Pre‑sale cannot have discount");
}
}The fix is to avoid inheritance when the “is‑a” relationship does not hold, extracting a common abstraction instead.
4. Dependency Inversion Principle (DIP)
DIP states that high‑level modules should depend on abstractions, not concrete details. The original OrderService directly instantiated WechatPayment, tying business logic to a low‑level implementation. Refactoring introduces a Payment abstraction and injects the concrete implementation via constructor injection.
public class OrderService {
private final Payment payment; // depends on abstraction
public OrderService(Payment payment) { this.payment = payment; }
// business method uses payment.pay(...)
}
// Usage examples:
new OrderService(new WechatPayment());
new OrderService(new AlipayPayment());
new OrderService(new MockPayment()); // for testsThis mirrors Spring’s IoC/DI mechanism.
5. Interface Segregation Principle (ISP)
ISP advises small, client‑specific interfaces. A “fat” OrderOperation forces implementations like VirtualOrder to provide irrelevant methods (e.g., ship()), leading to empty bodies or exceptions. The solution splits the interface into focused ones such as Payable, Shippable, and Commentable.
interface Payable { void create(); void pay(); void refund(); }
interface Shippable { void ship(); }
interface Commentable { void comment(); }
class PhysicalOrder implements Payable, Shippable, Commentable { /* … */ }
class VirtualOrder implements Payable, Commentable { /* no ship */ }6. Law of Demeter (LoD)
LoD (least knowledge) says an object should only talk to its immediate friends. The bad code order.getAddress().getUser().getPhone() makes OrderService aware of deep object graphs. Refactoring moves the lookup into Order itself via getReceiverPhone(), so callers only invoke order.getReceiverPhone().
public class Order {
public String getReceiverPhone() { return address.getUser().getPhone(); }
}
// Caller:
notify(order.getReceiverPhone());Don’t over‑apply: LoD does not forbid fluent APIs that return the same object; it forbids reaching into another object’s internals.
7. Composite Reuse Principle (CARP)
CARP prefers composition over inheritance. The article shows a naïve inheritance example LoggingOrder extends Order that breaks encapsulation and leads to class explosion. Using composition, LoggingOrder holds an Order instance and delegates calls.
// Inheritance (bad)
class LoggingOrder extends Order { /* add logging */ }
// Composition (good)
class LoggingOrder {
private final Order order;
public LoggingOrder(Order order) { this.order = order; }
public void pay(double amount) {
System.out.println("[log] start pay");
order.pay(amount);
System.out.println("[log] end pay");
}
}When a true “is‑a” relationship exists and LSP holds, inheritance remains appropriate; otherwise, favor composition.
Putting the seven principles together
SRP : one class, one responsibility – avoids “fat” classes.
OCP : open for extension, closed for modification – protects stable code.
LSP : subclasses must honor parent contracts – prevents runtime surprises.
DIP : depend on abstractions – decouples high‑level logic from low‑level details.
ISP : small, focused interfaces – stops forced implementation of unused methods.
LoD : talk only to direct friends – limits knowledge of internal structures.
CARP : prefer composition – avoids inheritance pitfalls.
All seven converge on two goals: handle change and reduce coupling . The common tool is abstraction : identify volatile points, introduce stable abstractions, and keep the rest insulated.
Cold‑water warning: Principles are meant for balance, not dogma. Over‑applying any principle leads to extremes—hundreds of single‑method classes, needless abstraction layers, or an explosion of interfaces—resulting in over‑engineering.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
