hololive滚滚山免安装绿色中文版
995M · 2025-10-31
在软件开发中,代码量随着项目规模的增长而膨胀,如果没有良好的架构设计,项目就会变得难以维护。面向对象六大设计原则 是指导我们编写高质量代码的重要法则。它们并不是晦涩的规则,而是帮助我们在实际开发中 降低耦合、增强复用、提高可维护性 的经验总结。
一个类只做一件事。 如果一个类承担了过多功能,它的变化就会牵一发而动全身。例如一个类既负责用户登录,又负责日志记录,那么登录逻辑变更时,日志模块也可能被意外影响。 我们应尽量把不同的功能拆分成多个独立的类或模块,使得每个类有且只有一个导致它变化的原因。
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的调用只涉及自己的直接朋友
把对内部组件的操作通过高层方法封装,减少耦合,便于演化与测试。
六大设计原则并不是孤立存在的,而是相互联系、共同作用的。掌握并灵活运用这六大原则,就能在项目中写出更清晰、更健壮、更易扩展的代码。
 
                     
                            995M · 2025-10-31
 
                            90.9M · 2025-10-31
 
                            478M · 2025-10-31
