buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

OOP五大设计原则--SOLID

OOP遵照:单一职责原则--SRP

OOP遵照:开放封闭原则--OCP

OOP遵照:依赖倒置原则(DIP):面向接口编程

 

**核心思想**:高层模块不应该依赖低层模块,二者都应该依赖于抽象。

 

**传统做法**:

class ICBCBankPayment {
    public PayResult pay(double amount, String orderId) {
        // 调用工商银行特定的 API 进行支付
        System.out.println("调用工商银行接口,订单" + orderId + "支付金额:" + amount);
        // 返回银行特定的结果,并适配为统一的 PayResult
        return new PayResult(true, "ICBC-200", "支付成功");
    }
}

class OrderService {
    private ICBCBankPayment bankPayment; // 直接依赖具体实现
    public OrderService() {
        this.bankPayment = new ICBCBankPayment(); // 硬编码依赖
    }
    public void process(Order order) {
        // ... 其他订单处理逻辑(如校验、保存订单等)
        
        // 调用支付,限定了中国工商银行
        PayResult result = bankPayment.pay(order.getAmount(), order.getId());
        
        // ... 根据支付结果处理后续业务
        if (result.isSuccess()) {
            updateOrderStatus(order.getId(), OrderStatus.PAID);
        } else {
            // 处理支付失败
        }
    }
}
View Code

一旦要扩充或切换新的银行通道,`OrderService`就需要修改。面对这种业务变化,正是依赖倒置原则所要解决的核心问题。

 

**依赖倒置**:

public interface BankPayment {
    PayResult pay(double amount, String orderId);
}

class ICBCBankPayment implements BankPayment {
    public PayResult pay(double amount, String orderId) { /*...*/ } 
}
class ABCBankPayment implements BankPayment {
    public PayResult pay(double amount, String orderId) { /*...*/ } 
}

class OrderService {
    private BankPayment bankPayment; // 依赖抽象
    public BusinessService(BankPayment bankPayment) { // 依赖注入(Dependency Injection, DI):通过构造函数传入具体实现
        this.bankPayment = bankPayment;
    }
    
    public void process(Order order) {
        // ... 其他订单处理逻辑(如校验、保存订单等)
        
        // 调用支付,无需关心是哪个银行
        PayResult result = bankPayment.pay(order.getAmount(), order.getId());
        
        /*...*/
    }
}
View Code

**实际意义**:DIP是**框架设计的核心原则**,它使得高层业务逻辑与低层实现解耦,提高了系统的可测试性和可维护性。

 

 

OOP遵照:接口隔离原则ISP(ISP--Interface Segregation Principle):

使用多个专门的接口比使用单一的总接口要好。
一个类对另外一个类的依赖性应当是建立在最小的接口上的。
一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

 

“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。

OOP遵照:Liskov替换原则--LSP

 定义:如果对于类型S的每一个对象o1,都有一个类型T的对象o2,使对于任意用类型T定义的程序P,将o2替换为o1P的行为保持不变,则称ST的一个子类型。
子类型必须能够替换它的基类型。LSP又称里氏替换原则。
对于这个原则,通俗一些的理解就是,父类的方法都要在子类中实现或者重写。

LSP优点:
1、保证系统或子系统有良好的扩展性。只有子类能够完全替换父类,才能保证系统或子系统在运行期内识别子类就可以了,因而使得系统或子系统有了良好的扩展性。
2、实现运行期内绑定,即保证了面向对象多态性的顺利进行。这节省了大量的代码重复或冗余。避免了类似instanceof这样的语句,或者getClass()这样的语句,这些语句是面向对象所忌讳的。
3、有利于实现契约式编程。契约式编程有利于系统的分析和设计,指我们在分析和设计的时候,定义好系统的接口,然后再编码的时候实现这些接口即可。在父类里定义好子类需要实现的功能,而子类只要实现这些功能即可。
 
使用LSP注意点:
1、此原则和OCP的作用有点类似,其实这些面向对象的基本原则就2条:1:面向接口编程,而不是面向实现;2:用组合而不主张用继承
2、LSP是保证OCP的重要原则
3、这些基本的原则在实现方法上也有个共同层次,就是使用中间接口层,以此来达到类对象的低耦合,也就是抽象耦合!
4、派生类的退化函数:派生类的某些函数退化(变得没有用处),Base的使用者不知道不能调用f,会导致替换违规。在派生类中存在退化函数并不总是表示违反了LSP,但是当存在这种情况时,应该引起注意。
5、从派生类抛出异常:如果在派生类的方法中添加了其基类不会抛出的异常。如果基类的使用者不期望这些异常,那么把他们添加到派生类的方法中就可以能会导致不可替换性。


重点提醒:原则是工具,不是枷锁

  1. 适度原则:在简单场景中,过度应用SOLID原则会导致代码过度工程化。当逻辑足够简单时,可以在方法级别保持单一职责,而不必拆分类

  2. 演进式设计:不要试图在项目开始就应用所有原则。当变化第一次出现时进行重构,比预测所有变化更实际。

  3. 综合权衡:这些原则相互促进,但也需要权衡。比如过度接口隔离可能增加系统复杂度,需要根据实际情况平衡。

记住,这些原则的最终目标是让代码更易理解、更易维护、更易扩展。当原则与实际需求冲突时,要以实际需求为准。

最好的代码不是完美符合所有原则的代码,而是在当前业务场景下最合适的代码

posted on 2012-03-20 14:53  buguge  阅读(827)  评论(0)    收藏  举报