美文网首页
面向对象设计的六大原则(SOLID+迪米特原则)

面向对象设计的六大原则(SOLID+迪米特原则)

作者: crazyhank | 来源:发表于2019-09-28 20:16 被阅读0次

    1. 单一职责原则(SRP)

    定义:一个类只应承担一种责任。换句话说,让一个类只做一件事。如果需要承担更多的工作,那么分解这个类。

    举例:
    订单和账单上都有流水号、业务时间等字段。如果只用一个类表达,赋予其双重职责,后果:

    特有属性和共有属性相互掺杂,难以理解;
    修改一个场景可能会影响另一个场景。
    正确的做法是拆成两个独立的类。

    2. 开闭原则(OCP)

    定义: 实体应该对扩展是开放的,对修改是封闭的。即,可扩展(extension),不可修改(modification)。

    举例:
    一个商户接入了多个付款方式,支付宝和微信支付,如果将调用支付API的类写成:

    public class PayHandler {
    
        public Result<T> pay(Param param) {
            if(param.getType() == "ALIPAY") {
                // 支付宝付款调用
                ...
            } else if(param.getType() == "WeChatPay") {
               // 微信支付付款调用
               ...
            }
        }
    }
    

    那么每次新加一种支付方式,或者修改原有的其中一种支付方式,都要修改PayHandler这个类,可能会影响现有代码。

    比较好的做法是将不同的行为(支付方式)抽象,如下:

    public class PayHandler {
    
        private Map<String, PayProcessor> processors;
    
        public Result<T> pay(Param param) {
            PayProcessor payProcessor = processors.get(param.getType());
            // 异常处理略
            return payProcessor.handle(param);
        }
    }
    
    interface PayProcessor {
        Result<T> handle(Param param);
    }
    
    public class AlipayProcessor implements PayProcessor {
        ...
    }
    
    public class WeChatPayProcessor implements PayProcessor {
        ...
    }
    

    这样,新增支付方式只需要新增类,如果使用的是spring等容器,在xml配置对应key-value关系即可;修改已有的支付方式只需要修改对应的类。最大化地避免了对已有实体的修改。

    3. 里氏替换原则(LSP)

    定义: 一个对象在其出现的任何地方,都可以用子类实例做替换,并且不会导致程序的错误。

    举例:
    经典的例子: 正方形不是长方形的子类。原因是正方形多了一个属性“长 == 宽”。这时,对正方形类设置不同的长和宽,计算面积的结果是最后设置那项的平方,而不是长*宽,从而发生了与长方形不一致的行为。如果程序依赖了长方形的面积计算方式,并使用正方形替换了长方形,实际表现与预期不符。
    扩展:
    不能用继承关系(is-a),但可以用委派关系(has-a)表达。上例中,可以使用正方形类包装一个长方形类。或者,将正方形和长方形作进一步抽象,使用共有的抽象类。
    逸闻:
    “里氏”指的是芭芭拉·利斯科夫(Barbara Liskov,1939年-),是美国第一个计算机科学女博士,图灵奖、冯诺依曼奖得主,参与设计并实现了OOP语言CLU,而CLU语言对现代主流语言C++/Java/Python/Ruby/C#都有深远影响。其项目中提炼出来的数据抽象思想,已成为软件工程中最重要的精髓之一。

    4. 接口分离原则(ISP)

    定义: 客户(client)不应被强迫依赖它不使用的方法。即,一个类实现的接口中,包含了它不需要的方法。将接口拆分成更小和更具体的接口,有助于解耦,从而更容易重构、更改。

    举例:
    仍以商家接入移动支付API的场景举例,支付宝支持收费和退费;微信接口只支持收费。

    interface PayChannel {
        void charge();
        void refund();
    }
    
    class AlipayChannel implements PayChannel {
        public void charge() {
            ...
        }
        
        public void refund() {
            ...
        }
    }
    
    class WeChatChannel implements payChannel {
        public void charge() {
            ...
        }
        
        public void refund() {
            // 没有任何代码
        }
    }
    

    第二种支付渠道,根本没有退款的功能,但是由于实现了PayChannel,又不得不将refund()实现成了空方法。那么,在调用中,这个方法是可以调用的,实际上什么都没有做!

    5. 依赖倒置原则(DIP)

    定义:
    (1) 高层次的模块不应依赖低层次的模块,他们都应该依赖于抽象。
    (2) 抽象不应依赖于具体实现,具体实现应依赖抽象。
    实际上,依赖倒置是实现开闭原则的方法。

    举例:

    public class PayHandler {
    
        public Result<T> pay(Param param) {
            if(param.getType() == "ALIPAY") {
                AlipayProcessor processor = new AlipayProcessor();
                processor.hander(param);
                ...
            } else if(param.getType() == "WeChatPay") {
                WeChatPayProcessor processor = new WeChatPayProcessor();
                processor.hander(param);
               ...
            }
        }
    }
    
    public class AlipayProcessor { ... }
    
    public class WeChatPayProcessor { ... }
    

    这种实现方式,PayHandler的功能(高层次模块)依赖了两个支付Processor(低层次模块)的实现。

    6. 迪米特原则(LOD)

    定义:一个对象应该对其他对象的内部实现有最少的了解。通俗的说,一个类应该对自己需要耦合或调用的类知道的越少越好,类的内部如何实现调用者或依赖着每关系。

    相关文章

      网友评论

          本文标题:面向对象设计的六大原则(SOLID+迪米特原则)

          本文链接:https://www.haomeiwen.com/subject/bqitpctx.html