《大话设计模式》之总结

  1. 对象: 用一组可识别的特性和行为来标识的实体;
    类: 具有享用属性和功能的对象的抽象的集合;
    属性: 一个方法或一对方法,有 get()\set()调用;
    字段: 与类相关的变量, 通常是私有的;
  2. 封装:
    每个对象都包含它能进行操作所需要的所有信息.
    一般无需对外界公开的方法都应该设置成 private, 这才有利于"封装".
    优点:
        - 减少耦合
        - 类内部的实现可以自由地修改
        - 类具有清晰的对外接口
    PS: 房子, 门
    
  3. 继承:
    - 子类拥有父类非 private 的属性和功能
    - 子类具有自己的属性和功能, 即子类可以扩展父类没有的属性和功能
    - 子类可以重写父类的方法
    构造方法不能被继承,只能被调用.
    优点: - 使得所有子类公共的部分都放在了父类, 使得代码得到了共享, 避免重复
          - 可使得修改或扩展继承而来的实现都比较容易
    缺点: - 父类变, 则子类不得不变
          - 会破坏包装, 父类实现细节暴露给子类
    使用场景: 两个类之间是 "is-a" 的关系
    PS: 动物 - 猫\狗
    
  4. 多态:
    注意:
        - 子类以父类的身份出现
        - 子类在工作时以自己的方式来实现
        - 子类以父亲的身份出现时, 子类特有的属性和方法不可以使用
        PS: 京剧, 儿子替生病的父亲上台唱戏
    使用: 为了使子类的实例完全解体来自父类的类成员, 父类需要将该成员声明称抽象的.
    实现: 对象的声明必须是父类, 而不是子类, 实例化的对象是子类, 这才能实现多态.
    原理: 当方法被调用时, 无论对象是否被转换为其父类, 都只有位于对象继承链最末端的方法实现才会被调用.也就是说, 抽象方法是按照其运行时类型而非编译时类型进行动态绑定调用的.
    
  1. 抽象类:
    注意:
        - 抽象类不能实例化
        - 抽象方法时必须被子类重写的
        - 如果类中包含抽象方法, 那么这个类就必须定义为抽象类
    让抽象类拥有尽可能多的共同代码, 拥有尽可能少的数据.
    一个继承关系形成的等级结构里面, 树叶节点应当是具体类, 树枝节点均应该是抽象类.
    
  2. 接口:
    把隐式公共方法和属性结合起来, 以封装特定功能的一个集合.
    一旦类实现了接口, 类就可以支持接口所指定的所有属性和成员.
    声明接口在语法上与声明抽象类完全相同, 但不允许提供接口中任何成员的执行方式.
    实现接口的类必须要实现接口中的所有方法和属性.
    一个类可以支持多个接口, 多个类也可以支持相同的接口.
    PS: 叮当猫和孙悟空都会变出东西, 两个不相干的类, 都实现了"变出东西"这样的接口, 再利用接口的多态性, 完成两个不同对象不可能完成的"变出东西".
    
  3. 抽象类和接口的区别:
    • 类是对对象的抽象; 抽象类是对类的抽象; 接口是对行为的抽象
    • 如果行为跨越不同类的对象, 可使用接口; 对于一些相似的类对象, 用继承抽象类
    • 从设计角度讲, 抽象类是子类中发现了公共的东西, 泛化出了父类, 然后子类继承父类; 而接口是跟不知子类的存在, 方法如何实现不确定, 预先定义 (PS: 猫 -> 动物; 动物策划方商量飞得最远等行为)
  4. 面向对象的好处:
    • 可维护
    • 可复用
    • 可扩展
    • 灵活性好
  5. 简单工厂模式
    面对的问题: 结合计算器加减乘除为例, 到底要实例化谁, 将来会不会增加实例化的对象, 这是很容易变化的地方
    思考解决过程: 考虑用一个单独的类来做这个创造实例的过程, 这就是工厂
    最终解决方案: 在工厂中实例化合适的对象, 通过多态, 返回父类的方式实现
    具体实现: 在工厂内根据客户给的参数, 实例化合适对象, 最终将该对象以父类的方式返回给客户. (就是多态的实现)
    
  6. 策略模式
    面对的问题: 结合商场收银促销例子, 打折、返利都是算法, 用工厂生成算法对象没问题,但是算法本身就是一中策略, 最重要的事这些算法是随时可能互相替换的, 这就是变化点
    思考过程: 客户不关心具体的算法细节, 只需要知道是哪种算法, 及结果; 所以将具体算法的实例化和客户做隔离.
    最终解决方案: 定义好算法对象, 封装起来(变化点), 让他们之间可以相互替换, 从而不影响算法的客户
    具体实现: 定义一个 Context 类, 类中维护一个算法父类的引用, 构造函数中得到这个引用对象, 暴露一个接口即该引用的获得结果的方法
    PS: 可以将简单工厂模式和策略模式相结合, 用在策略模式的构造函数中, 这样就可以是具体算法和客户做隔离
    特点: - 策略模式就是封装算法的, 定义的 Context 为这些算法抽取了一系列可供重用的算法或行为, 减少了中算法之间的耦合;
         - 同时, 策略模式结合了简单工厂模式, 可以将具体算法的实现交给 Context, 从而减轻了客户端的职责
    简单工厂模式和策略模式使用的区别:
            - 简单工厂模式, Factory 创建算法实例是静态方法; 策略模式, Context 是在构造函数中传进来具体算法实例
            - 简单工厂模式,Factory 内部不持有算法实例, 只是根据外部传进来的参数, 创建具体的算法实例并传给调用者, 由调用者持有这个算法实例; 策略模式, Context 内部持有算法实例, 调用者创建但不持有具体的实例, 而是传给 Context 内部
            - 简单工厂模式, 调用者直接调用算法实例的方法; 策略模式, 调用者调用的是 Context 的实例方法, 在 Context 的实例方法中调用的是算法实例的方法
    
  7. 单一职责原则
    概念: 就一个类而言, 应该仅有一个引起它变化的原因
    原因: 如果一个类承担的职责过多, 就等一把这些职责耦合在一起, 一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力. 当发生变化时, 设计会遭受意想不到的破坏.
    实现: 软件设计真正要做的许多内容, 就是发现职责并把哪些职责相互分离.
    优点: 易维护、易扩展、易复用、灵活多样
    PS: 手机没拍摄到 UFO
    
  8. 开放-封闭原则
    概念: 软件实体应该是可以扩展的, 但是不能修改
    实现: 设计时猜测出最优可能发生的变化种类, 然后构造抽象来隔离那些变化.
    PS: 计算器
    
  9. 依赖倒置原则
    概念: - 高层模块不应该依赖低层模块, 两个都应该依赖抽象
          - 抽象不应该依赖细节, 细节应该依赖抽象
    PS: CPU\主板 不互相依赖, 只依赖接口; 简单工厂类与分支耦合, 所以抽象出一个创造抽象产品的工厂方法.
    
  10. 里氏代换原则
    概念: 子类型必须能替换掉它们的父类型 (把父类都替换成它的子类, 程序的行为没有变化)
            只有当子类可以替换掉父类, 软件对象的功能不受到影响时, 父类才能真正被复用, 而子类也能够在父类的基础上增加新的行为.
            正是由于子类型的可替换性才使得父类类型的模块在无需修改的情况下就可以扩展.
    PS: 企鹅不会飞不能继承鸟, 猫是动物的一种
    
  11. 装饰模式
    概念: 动态地给一个对象添加一些额外的职责, 就增加功能来说, 装饰模式比生成子类更加灵活 (简单来说, 就是把所需的功能按照正确的顺序串联起来进行控制)
    实现: 先定义一个 Component 对象接口, 可以给这些对象动态地添加职责; 再通过定义 Decorator 类, 持有 Component 对象, 定义属于它的接口实现方法并调用 Component 对象的接口, 最终通过继承 Decorator 类类实现对 Component 类的扩展.
    面对的问题: 往一个旧的类中添加新的代码, 这些新的代码通常装饰了原有类的核心职责或主要方法行为, 它会在主类中加入新的字段、新的方法、新的逻辑, 从而增加了主类的复杂度, 然而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要. 通过装饰模式解决, 它把每个需要装饰的功能放在单独的类中, 并让这个类包装它所要装饰的对象, 当需要执行特殊行为时, 客户代码就可以有选择地使用装饰包装功能包装对象了.
    优点: 将类中的装饰功能区分开来了, 并且去除相关类中重复的装饰逻辑.
    
  12. 代理模式
    定义: 通过代理来访问对象的函数
    实现: 定义代理和对象的通用接口, 代理和对象分别实现该接口; 代理中初始化并持有该对象, 再使用对象的地方, 使用代理, 最终在代理中真正实现的是该对象的方法.
    优点: 可以将对象生成实例的处理等耗时操作推迟到实际调用方法时再进行, 通过设置代理人的方式, 减轻本人的工作负担.
    
  13. 工厂方法模式
    定义: 定义一个用于创建对象的接口, 让子类决定实例化哪一个类, 工厂方法使一个类的实例化延迟到其子类.
    对比简单工厂模式: 简单工厂模式最大优点在于工厂类中包含了必要的逻辑判断, 根据客户端的选择条件动态实例化相关的类, 对于客户端来说, 去除了与具体产品的依赖; 但是如果给工厂类添加新的case, 就违背了 开放-封闭原则, 因此产生了工厂方法模式.
    
  14. 原型模式
    定义: 用原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象.
    使用前提: 一般是在初始化的信息不发生变化的情况下.
    优点: 既隐藏了对象创建的细节, 又对性能是大大的提高.
    PS: clone()
    
  15. 模板方法模式
    模板方法: 当我们要完成在某一细节层次一致的一个过程或一系列步骤, 但其个别步骤在更详细的层次上的实现可能不同时, 通常考虑用模板方法来处理.
    定义: 定义一个算法中的算法的骨架, 将一些步骤延迟到子类.
    优点: 把不变的行为搬移到父类, 去除子类中的重复代码.
    
  16. 迪米特法则:
    定义: 如果两个类不必彼此直接通信, 那么这两个类就不应当发生直接的相互作用; 如果其中一个类需要调用另一个类的某一个方法的话, 可以通过第三者转发这个调用.
    根本思想: 强调了类之间的松耦合; 类之间的耦合越弱, 越有利于复用, 一个处于弱耦合的类被修改, 不会对有关系的类造成波及.
    
  17. 外观模式:
    定义: 为子系统中的一组接口提供一个一致的界面, 此模式定义了一个高层接口, 这个接口使得这一子系统更加容易使用.
    使用场景: 1. 在设计初期阶段考虑将数据访问层和业务逻辑层、业务逻辑层和表示层之间建立外部Facade, 降低耦合度.
            2.在开发阶段, 增加 Facade 可以提供一个简单的接口, 减少很小的类之间的依赖.
            3. 维护遗留的大型项目时, 为新系统开发一个外观 Facade 类, 让新系统与 Facade 对象交互, Facade 与遗留代码交互所有的复杂工作.
    
  18. 建造者模式:
    定义: 将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不用的表示.