【笔记】Head First 设计模式

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

设计原则
针对接口编程,而不是针对实现编程。

问:用一个类代表一个行为,感觉似乎有点奇怪。
类不是应该代表某种“东西”吗?类不是应该同时具备
状态“与”行为吗?
答:在OO系统中,是的,类代表的东西一般都
是既有状态(实例变量)又有方法。只是在本例中,碰
巧“东西”是个行为。但是即使是行为,也仍然可以有
状态和方法,例如,飞行的行为可以具有实例变量,记
录飞行行为的属性(每秒翅膀拍动几下、最大高度和速
度等)。

问:Duck是不是也该设计成一个接口?
答:在本例中,这么做并不恰当。如你所见的,
我们已经让一切都整合妥当,而且让Duck成为一个具
体类,这样可以让衍生的特定类(例如绿头鸭)具有
Duck共同的属性和方法。我们已经 从Duck的继承结构中
删除了变化的部分,原先的问题都已经解决了,所以不
需要把Duck设计成接口。

测试Duck的代码
1,输入并编译下面的Duck类(Duck.java)以及两页前的
MallardDuck类(MallardDuck.java)。

public abstract class Duck {
   FlyBehavior flyBehavior;//为行为接口类型声明两个引
   QuackBehavior quackBehavior;//用变量,所有鸭子子类(在同一个zackage中)都继承它们
   public Duck() {
   } 
   
   public abstract void display();
   public void performFly() {
      flyBehavior.fly();//委托给行为类
   }
   public void performQuack() {
      quackBehavior.quack();//委托给行为类
   } 
   public void swim() {
      System.out.println(“All ducks float, even decoys!”);
   }
}

2,输入并编译FlyBehavior接口(FlyBehavior.java)与两个行为实现

类(FlyWithWings.java与FlyNoWay.java)。

public interface FlyBehavior {
   public void fly();//所有飞行行为类必须实现的接口。
}
public class FlyWithWings implements FlyBehavior {   
   public void fly() {
  System.out.println(“I’m flying!!”);//这是飞行行为的实现,给“真会”飞的鸭子用……
   }
}
public class FlyNoWay implements FlyBehavior {
   public void fly() {
       System.out.println(“I can’t fly”);//这 是 飞 行 行 为 的 实 现 ,给“不会”飞的鸭子用(包括橡皮鸭和诱饵鸭)。
   }
}

3.输入并编译QuackBehavior接口(QuackBehavior.java)及其三个实现类

(Quack.java、MuteQuack.java、Squeak.java)。

public interface QuackBehavior {
   public void quack();
}
public class Quack implements QuackBehavior {
   public void quack() {
      System.out.println(“Quack”);
   }
}
public class MuteQuack implements QuackBehavior {
   public void quack() {
  System.out.println(“<< Silence >>”);
   }
}
public class Squeak implements QuackBehavior {
   public void quack() {
  System.out.println(“Squeak”);
   }
}

4,输入并编译测试类(MiniDuckSimulator.java)

public class MiniDuckSimulator {
   public static void main(String[] args) {
      Duck mallard = new MallardDuck();
      mallard.performQuack();//这会调用MallardDuck继承来的performQuack() 方法,进而委托给该对象的QuackBehavior对象处理(也就是说,调用继承来的quackBehavior引用对象的quack())。
      mallard.performFly();
   }
}

 动态设定行为

在鸭子里建立了一堆动态的功能没有用到,就太可惜了!假设我们想在鸭子子类中通
过“设定方法(setter method)”来设定鸭子的行为,而不是在鸭子的构造器内实例化。

1,在Duck类中,加入两个新方法:

public void setFlyBehavior(FlyBehavior fb) {
    flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
    quackBehavior = qb;
}

2,制造一个新的鸭子类型:模型鸭(ModelDuck.java)

public class ModelDuck extends Duck {   
    public ModelDuck() {
       flyBehavior = new FlyNoWay();//一开始,我们的模型鸭是不会飞的。
  quackBehavior = new Quack();
    }
    public void display() {
       System.out.println(“I’m a model duck”);
    }
}

3,建立一个新的FlyBehavior 类型(FlyRocketPowered.java)

public class FlyRocketPowered implements FlyBehavior {
    public void fly() {
       System.out.println(“I’m flying with a rocket!”);
       //没关系,我们建立一个利用火箭动力的飞行行为。
    }
}

4,改变测试类(MiniDuckSimulator.java),加上模型鸭,并使模型鸭具有火箭动力。

public class MiniDuckSimulator {
   public static void main(String[] args) {
      Duck mallard = new MallardDuck();
      mallard.performQuack();
      mallard.performFly();
   
          Duck model = new ModelDuck();
          model.performFly();//第一次调用performFly() 会被委托给flyBehavior对象(也就是FlyNoWay实例),该对象是在模型鸭构造器中设置的。
          model.setFlyBehavior(new FlyRocketPowered());//这会调用继承来的setter方法,把火箭动力飞行的行为设定到模型鸭中。哇!模型鸭突然具有了火箭动力飞行能力!
          model.performFly();  //如果成功了,就意味着模型鸭可以动态地改变它的飞行行为。如果把行为的实现绑死在鸭子类中,可就无法做到这样了。
   }
}

 二,观察者(Observer)模式

先看一个错误示范
这是第一个可能的实现:我们依照Weather-O-Rama气象站开发人员的暗示,在
measurementsChanged()方法中添加我们的代码:

public class WeatherData {
    // 实例变量声明
    public void measurementsChanged() {
        //调用 WeatherData  的三个getXxx()方法,以取得最近的测量值。这些getXxx()方法已经实现好了。
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        //现在,更新布告板……
        currentConditionsDisplay.update(temp, humidity, pressure);
        statisticsDisplay.update(temp, humidity, pressure);
        forecastDisplay.update(temp, humidity, pressure);
        //不对的地方:
        //1.针对具体实现编程,会导致我们以后在增加或删除布告板时必须修改程序。
        //2.至少,这里看起来像是一个统一的接口,布告板的方法名称都是update(),参数都是温度、湿度、气压。
    }
    // 这里是其他WeatherData方法
}

认识观察者模式
我们看看报纸和杂志的订阅是怎么回事:
1.报社的业务就是出版报纸。
2.向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
3.当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。
4.只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。

出版者+订阅者=观察者模式

如果你了解报纸的订阅是怎么回事,其实就知道观察者模式是怎么回事,只是名称不太一样:

出版者改称为“主题”(Subject),订阅者改称为“观察者”(Observer)。

观察者模式的一天

原文地址:https://www.cnblogs.com/FH-cnblogs/p/4306747.html