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 { // 处理支付失败 } } }
一旦要扩充或切换新的银行通道,`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()); /*...*/ } }
**实际意义**:DIP是**框架设计的核心原则**,它使得高层业务逻辑与低层实现解耦,提高了系统的可测试性和可维护性。
OOP遵照:接口隔离原则ISP(ISP--Interface Segregation Principle):
使用多个专门的接口比使用单一的总接口要好。
一个类对另外一个类的依赖性应当是建立在最小的接口上的。
一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。
“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。
OOP遵照:Liskov替换原则--LSP
定义:如果对于类型S的每一个对象o1,都有一个类型T的对象o2,使对于任意用类型T定义的程序P,将o2替换为o1,P的行为保持不变,则称S为T的一个子类型。
子类型必须能够替换它的基类型。LSP又称里氏替换原则。
对于这个原则,通俗一些的理解就是,父类的方法都要在子类中实现或者重写。
LSP优点:
1、保证系统或子系统有良好的扩展性。只有子类能够完全替换父类,才能保证系统或子系统在运行期内识别子类就可以了,因而使得系统或子系统有了良好的扩展性。
2、实现运行期内绑定,即保证了面向对象多态性的顺利进行。这节省了大量的代码重复或冗余。避免了类似instanceof这样的语句,或者getClass()这样的语句,这些语句是面向对象所忌讳的。
3、有利于实现契约式编程。契约式编程有利于系统的分析和设计,指我们在分析和设计的时候,定义好系统的接口,然后再编码的时候实现这些接口即可。在父类里定义好子类需要实现的功能,而子类只要实现这些功能即可。
使用LSP注意点:
1、此原则和OCP的作用有点类似,其实这些面向对象的基本原则就2条:1:面向接口编程,而不是面向实现;2:用组合而不主张用继承
2、LSP是保证OCP的重要原则
3、这些基本的原则在实现方法上也有个共同层次,就是使用中间接口层,以此来达到类对象的低耦合,也就是抽象耦合!
4、派生类的退化函数:派生类的某些函数退化(变得没有用处),Base的使用者不知道不能调用f,会导致替换违规。在派生类中存在退化函数并不总是表示违反了LSP,但是当存在这种情况时,应该引起注意。
5、从派生类抛出异常:如果在派生类的方法中添加了其基类不会抛出的异常。如果基类的使用者不期望这些异常,那么把他们添加到派生类的方法中就可以能会导致不可替换性。
重点提醒:原则是工具,不是枷锁
-
适度原则:在简单场景中,过度应用SOLID原则会导致代码过度工程化。当逻辑足够简单时,可以在方法级别保持单一职责,而不必拆分类。
-
演进式设计:不要试图在项目开始就应用所有原则。当变化第一次出现时进行重构,比预测所有变化更实际。
-
综合权衡:这些原则相互促进,但也需要权衡。比如过度接口隔离可能增加系统复杂度,需要根据实际情况平衡。
记住,这些原则的最终目标是让代码更易理解、更易维护、更易扩展。当原则与实际需求冲突时,要以实际需求为准。
最好的代码不是完美符合所有原则的代码,而是在当前业务场景下最合适的代码。
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/articles/2407953.html
浙公网安备 33010602011771号