设计模式基础入门

7/9/2021

# 继承

使用继承来提供子类的行为,会导致一些缺点

  • 运行时的行为不容易改变
  • 改变会牵一发而动全身,造成其他子类不想要的改变

但如果全部使用接口的方式进行实现,接口不具有实现代码,会导致代码无法复用。

# 设计原则

  1. 找出应用中可能 需要变化 之处,把它们 独立 出来, 不要和那些 不需要变化的代码混在一起

    • 把会变化的部分取出来,封装起来,以后可以轻易地改动或扩充此不符,不影响不需要变化的其他部分。
    • 所有的设计模式都提供了一套方法,让系统中的某部分改变而不会影响其他部分。
  2. 针对 接口编程, 而不是针对 实现编程

    • 利用接口来代表每个行为,若要实现一个行为,不要在子类中去实现这个接口,而是专门制造一组其他类来专门实现这些接口,由行为类,而不是对象类来实现行为接口。

      Dog d = new Dog();
      d.bark(); // 针对实现编程
      
      Animal animal = new Dog();
      animal.makeSound(); // 针对接口编程
      
      1
      2
      3
      4
      5
    • 增加一个新的行为,应该怎么做: 应该定义一个新的类(FlyWithRocket),实现 FlyBehavior 接口,实现 fly() 方法

  3. 重要原则:多用组合,少用继承

    • 使用组合建立的系统有很大的弹性,可以 将算法族封装成类,更可以在运行时动态地改变行为

# 策略模式

定义并封装一组算法,让他们之间可以相互替换,策略模式让算法的变化独立于使用算法的客户。

# 具体实现

  1. 在父类(Duck)类中,加入实例变量,声明为接口类型(而不是具体实现类型)

  2. 具体的行为,不在父类中具体实现,而是通过调用实例变量,委托给该变量引用的对象来完成

    class Duck {
      FlyBehavior flyBehavior;
      public void performFly() {
         flyBehavior.fly() // 我们不在乎 flyBehavior 到底是什么,只知道该对象知道怎么去飞就够了
      }
    }
    
    1
    2
    3
    4
    5
    6
  3. 对于一个具体的类,在初始化时,对实例变量进行初始化

    public class MallardDuck extends Duck {
        public MallardDuck() {
            flyBehavior = new FlyWithWings();
        }
    }
    
    1
    2
    3
    4
    5

# 动态设定行为(运行时改变对象的行为)

  1. 增加对实例变量的设定方法 setFlyBehavior()
  2. 在对象实例(duck)的生命周期中,可以 随时调用该方法,对行为的具体实现进行设定