设计原则
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
设计原则
针对接口编程,而不是针对实现编程。
问:用一个类代表一个行为,感觉似乎有点奇怪。
类不是应该代表某种“东西”吗?类不是应该同时具备
状态“与”行为吗?
答:在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)。
观察者模式的一天