zoukankan      html  css  js  c++  java
  • 面向对象的三大特性和五大设计原则

    面向对象的 SOLID 原则

    特性

    封装

    隐藏对象的属性和实现细节,通过对外暴露的接口控制程序中属性的读写。

    封装需要把所有属性私有化,对每个属性提供 getter 和 setter 方法。如果有一个带参的构造函数的话,还需要写一个不带参的构造函数。

    继承

    通过继承可以实现代码的复用。

    缺点是提高了类之间的耦合性

    多态

    基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。

    要想实现多态,需要在继承+覆盖或重载。

    • 覆盖,是指子类重新定义父类的虚函数的做法。
    • 重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    SOLID 设计原则

    面向对象的 SOLID 设计原则:

    • S-单一职责原则
    • O-开放封闭原则
    • L-里氏替换原则
    • I-接口隔离原则(Interface segregation principles,ISP)
    • D-依赖倒置原则(Dependency inversion principle,DIP)

    单一职责原则

    一个类只做一件事。

    比如订单和账单,都包括订单号、创建时间等信息,但是如果只用一个类来表达,会导致:

    • 特有属性和共有属性相互掺杂,难以理解
    • 修改一个场景可能会影响另一个场景

    开放封闭原则

    实体应该对扩展是开放的,对修改是封闭的。即可扩展而不可修改。哪里可能变化,就在哪里抽象。

    例如,对于支付场景,如果将多种支付方式整合到一个类中,每次添加一种新的支付方式都需要变更这个类,并可能导致已有的支付方式受到影响而无法使用。可以将其抽象为抽象类或接口,每种支付方式继承抽象类或实现接口。

    interface PayProcessor {
        Result<T> handle(Param param);
    }
    
    public class AlipayProcessor implements PayProcessor {
        ...
    }
    
    public class WeChatPayProcessor implements PayProcessor {
        ...
    }

    里式替换原则

    在对象出现的任何地方,都可以用其子类实例进行替换,而不会导致程序错误,且软件功能不受影响。

    对于继承关系,只有满足里式替换原则才算合理。里氏替换原则的关键在于不能覆盖父类的非抽象方法。父类实现好的方法设定了一系列规范和契约,虽然它不强制要求所有的子类必须遵从这些规范,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。

    例如,B 在继承 A 时,修改了其非抽象方法:

    class A { 
      public int func1(int a, int b) { 
        return a-b; 
      } 
    }
    
    class B extends A { 
      public int func1(int a, int b) { 
        return a+b; 
      } 
    
      public int func2(int a, int b) { 
        return func1(a,b)+100; 
      } 
    }
    
    public class Client {
      public static void main(String[] args) {
        A a = new A();
        System.out.println("100-50="+a.func1(100, 50));
    
        B b = new B();
        System.out.println("100-50="+b.func1(100, 50));
        System.out.println("100+20+100="+b.func2(100, 20));
      } 
    } 

    接口隔离原则

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

    例如,假设对于支付接口,支付宝支持收费和退费,微信只支持收费。如果只抽象一个支付接口,则必须包括收费和退费两个方法:

    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() {
            // 没有任何代码
        }
    }

    此时微信不得不将退费实现成空方法。

    要满足接口隔离原则,可以将支付接口拆成各包含一个方法的收费和退费两个接口。支付宝需要同时实现这两个接口,而微信只需要实现收费接口。

    依赖倒置原则

    • 高层次的模块不应依赖低层次的模块,他们应该都依赖于抽象。
    • 抽象不应依赖于具体实现,具体实现应依赖抽象。


    可以用于将业务和底层模块解耦(例如底层逻辑经常变化,或预计会在一段时间后替换底层模块)。

    台灯和按钮的例子

    Button 对象用于感知外部环境。Lamp 对象用于影响外部环境。

    下图直接使用继承。此时 Lamp 对象的任何修改都会影响到 Button 对象。这个模型可以通过反转 Lamp 对象的依赖性来改进。

    public class Button {
        private Lamp lamp;
        public void Poll() {
            if (/*some condition*/) {
                lamp.TurnOn(); 
            }
        }
    }

    下图使用了依赖倒置原则。Button 对象现在拥有一个与 ButtonServer 相关的关联,它提供了 Button 可以用来打开或关闭某些东西的接口。Lamp 实现 ButtonServer 接口。此时,Button 对象可以控制所有实现了 ButtonServer 接口的对象,更灵活,同时还可以控制目前尚未创建的对象。

    替换数据库

    假设目前使用 MySQL,但是后面可能换成其它数据库。针对 MySQL 的实现类 MySQLDBUtil 编程的话,后续假设又创建了 OracleDBUtil,则替换的工作量相当大。而如果加一个抽象层接口 DBUtil,所有的数据库类(MySQLDBUtil、OracleDBUtil)都实现 MySQLDBUtil 接口,就可以轻松替换。

    原设计,直接继承数据库操作类:
    这里写图片描述

    新设计,增加抽象层接口。注意,这里用关联关系代替了继承关系,具体参考下面的合成复用原则:
    这里写图片描述

    其他设计原则

    合成复用原则

    合成复用原则(Composite Reuse Principle, CRP):尽量使用对象组合和聚合关系,而不是继承。组合和聚合关系可以降低类之间的耦合度。如果使用继承,需要严格遵循里氏替换原则。

    数据库工具类:

    class DBUtil {
    
    }

    继承关系:

    class UserDAO extends DBUtil {
        ...
    }

    聚合关系:

    class UserDAO {
        private DBUtil util;
        ...
    }

    迪米特法则

    迪米特法则又叫最少知道原则:一个对象应该对其他对象保持最少的了解。

    迪米特法则特点:

    • 类之间解耦,降低耦合度
    • 会产生大量的中转类,导致系统的复杂度提高。

    举个例子:连长、班长、士兵之间布置任务,连长只需要传达给其直接下属班长,班长再传达给其负责的士兵(下图左侧示例)。如果违反了迪米特法则,将导致混乱及高耦合度(下图右侧示例)。

    这里写图片描述

  • 相关阅读:
    vc++编程之在程序中加入网址链接
    VC++编程之对话框贴图
    软考(软件设计师)注意事项(攻略)
    解决SQLite数据库中文乱码问题
    计算机专业中经典书籍(程序猿和大学生必读)
    VC++编程中为程序加入启动画面功能
    动态规划的详细解析(01背包问题)
    动态规划之深入灵魂的解读(非常好)
    UML类图详解
    团队冲刺——第四天
  • 原文地址:https://www.cnblogs.com/kika/p/10851581.html
Copyright © 2011-2022 走看看