幻滑法域免安装绿色中文版
761M · 2025-11-02
在软件开发中,代码量随着项目规模的增长而膨胀,如果没有良好的架构设计,项目就会变得难以维护。面向对象六大设计原则 是指导我们编写高质量代码的重要法则。它们并不是晦涩的规则,而是帮助我们在实际开发中 降低耦合、增强复用、提高可维护性 的经验总结。
一个类只做一件事。 如果一个类承担了过多功能,它的变化就会牵一发而动全身。例如一个类既负责用户登录,又负责日志记录,那么登录逻辑变更时,日志模块也可能被意外影响。 我们应尽量把不同的功能拆分成多个独立的类或模块,使得每个类有且只有一个导致它变化的原因。
UserService 既处理业务又负责日志与持久化
public class UserService {
public void register(String name) {
// 业务逻辑
System.out.println("Validate and create user: " + name);
// 持久化
System.out.println("Saving user to DB: " + name);
// 日志
System.out.println("Log: user registered " + name);
}
}
职责拆分
public class UserService {
private final UserRepository userRepository;
private final Logger logger;
public UserService(UserRepository userRepository, Logger logger) {
this.userRepository = userRepository;
this.logger = logger;
}
public void register(String name) {
userRepository.save(new User(name));
logger.log("User registered: " + name);
}
}
interface UserRepository {
void save(User user);
}
interface Logger {
void log(String message);
}
class User {
String name;
User(String name) { this.name = name; }
}
对扩展开放,对修改关闭。 我们希望在新增功能时,不要去修改已有的类,而是通过扩展(继承、接口、多态等方式)来实现。这样做的好处是,原有逻辑不被破坏,风险大大降低。 假如有一个图形绘制系统,最开始只有 Circle,后来要增加 Rectangle。如果按照开闭原则,我们不去修改绘制类内部,而是通过新增 Rectangle 类并实现绘制接口即可。
支付方法硬编码到 PaymentProcessor
public class PaymentProcessor {
public void pay(String type, double amount) {
if ("WeChat".equals(type)) {
} else if ("AliPay".equals(type)) {
}
}
}
对新增支付方式开放实现类
public interface PaymentStrategy {
void pay(double amount);
}
public class WeChatPay implements PaymentStrategy {
public void pay(double amount) {
}
}
public class AliPay implements PaymentStrategy {
public void pay(double amount) {
}
}
public class PaymentProcessor {
public void process(PaymentStrategy strategy, double amount) {
strategy.pay(amount);
}
}
用策略扩展新支付方式时,不需要改 PaymentProcessor,只需新增实现类。
子类必须能够替换其基类出现的地方,且程序行为不被破坏。 也就是说,如果父类能做的事情,子类也必须能做;子类不能违背父类的契约。 如果 Bird 有 fly() 方法,而我们让 Penguin 继承 Bird 却无法飞,这就破坏了里氏替换。正确做法是把 Bird 抽象成 Animal,把会飞的鸟作为 FlyingBird,避免不合理的继承。
方圆问题
class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int area() { return width * height; }
}
class Square extends Rectangle {
public void setWidth(int w) {
super.width = w;
super.height = w; // 覆盖行为:修改一个属性影响另一个
}
public void setHeight(int h) {
super.width = h;
super.height = h;
}
}
若把 Square 当作 Rectangle 使用,会破坏对 setWidth/setHeight 的预期。
更合理的抽象
interface Shape {
int area();
}
class Rectangle implements Shape {
private int width, height;
public Rectangle(int w, int h) { this.width = w; this.height = h; }
public int area() { return width * height; }
}
class Square implements Shape {
private int side;
public Square(int side) { this.side = side; }
public int area() { return side * side; }
}
用更合适的抽象(Shape)来避免不恰当的继承,保证替换安全。
高层模块不依赖底层模块,二者共同依赖抽象;抽象不依赖细节,细节依赖抽象。 简单说,就是我们应该面向接口编程,而不是面向实现编程。 支付系统中不应该直接依赖 WeChatPay 或 AliPay,而是依赖一个通用的 PayInterface。这样在切换或新增支付方式时,只需实现接口,而不用修改业务逻辑。
违反示例(高层依赖具体实现)
public class OrderService {
private MySqlOrderRepository repository = new MySqlOrderRepository();
// 直接new导致OrderService强耦合于MySQL实现
}
改进示例(依赖抽象并注入)
public interface OrderRepository {
void save(Order order);
}
public class MySqlOrderRepository implements OrderRepository {
public void save(Order order) {
}
}
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository; // 依赖注入,灵活注入OrderRepository的实现类
}
public void place(Order order) {
repository.save(order);
}
}
OrderService 只依赖 OrderRepository 抽象,测试时可注入 mock,切换实现也方便。
一个接口应该只包含客户需要的方法。 如果接口过于臃肿,会迫使实现类去实现一些它们根本不需要的方法,造成冗余。 例如,如果我们定义了一个 Animal 接口,里面有 fly()、run()、swim(),那么企鹅实现它时就会很尴尬。正确的做法是把大接口拆成更小、更专注的接口,比如 Flyable、Runnable、Swimmable。
臃肿接口 Printer
interface Printer {
void print(Document d);
void scan(Document d);
void fax(Document d);
}
class SimplePrinter implements Printer {
public void print(Document d) {
// all right
}
public void scan(Document d) { throw new UnsupportedOperationException(); }
public void fax(Document d) { throw new UnsupportedOperationException(); }
}
SimplePrinter 被迫实现不需要的方法,产生空实现或异常。
接口拆分
interface Printable { void print(Document d); }
interface Scannable { void scan(Document d); }
interface Faxable { void fax(Document d); }
class SimplePrinter implements Printable {
public void print(Document d) {
}
}
客户端只依赖它需要的接口,职责更清晰也更灵活。
也叫最少知道原则,强调对象之间应尽量少地相互了解。 简单说就是不要和陌生人说话。一个对象只与直接朋友(自己创建的对象、参数、成员变量)通信,而不是跨层级去操作别的对象的内部细节。 例如:A.getB().getC().doSomething() 就违反了迪米特法则,因为 A 不应该知道 C 的存在,最好通过 B 提供一个方法来间接完成操作。
链式调用的违反示例
class Engine {
public OilTank getOilTank() { return new OilTank(); }
}
class Car {
private Engine engine;
public Engine getEngine() { return engine; }
}
Car car = new Car();
car.getEngine().getOilTank().drain(); // 违反LoD:car不该知道oilTank的存在
通过封装提供高层方法以改进
class Engine {
private OilTank oilTank;
public void drainOil() { oilTank.drain(); }
}
class Car {
private Engine engine;
public void drainEngineOil() { engine.drainOil(); }
}
Car car = new Car();
car.drainEngineOil(); // Car的调用只涉及自己的直接朋友
把对内部组件的操作通过高层方法封装,减少耦合,便于演化与测试。
六大设计原则并不是孤立存在的,而是相互联系、共同作用的。掌握并灵活运用这六大原则,就能在项目中写出更清晰、更健壮、更易扩展的代码。