设计模式读书笔记(一)

"Head First 设计模式第一、二章"

Posted by jhljx on September 2, 2016

目录

1. 策略模式
2. 观察者模式

1. 策略模式

讲解背景:从模拟鸭子应用来引入策略模式的思想。在鸭子游戏中会出现各种鸭子,不同的鸭子有不同的叫声(quack),游泳方式(swim),不同的外观(display)。而现在需要给鸭子添加飞行的功能,不同的鸭子可能会飞也可能不会飞。

最初的想法是设计一个鸭子的父类,它具有quack(),swim(),display()方法,然后不同种类的鸭子继承该父类。在添加fly()功能的时候,只用将这个方法添加给父类,这样所有的鸭子子类都会继承飞行fly()方法,即具有飞行功能。

但是出现了问题,不该具有fly()功能的鸭子却具有了fly()功能,比如橡皮鸭子。这就很不符合常理了。所以从设计角度考虑,这种做法很鲁莽,是错误的。

如果对于不会飞或者不会叫的鸭子,那么我们覆盖父类的quack()和fly()方法,在函数体内不做任何实现,这样仿佛也能实现不会飞和不会叫的功能,不过既然不会飞,不会叫,它却有quack()和fly()方法,这样虽然可以实现功能,但是逻辑很混乱。而且当子类很多时,需要频繁考虑是否覆盖fly(),quack()方法,覆盖了以后是否实现该方法等问题,这样会给开发带来噩梦。

继承行不通,那就改用接口让程序变得更灵活。

如果把fly()方法从父类中抽取出来,变成flyable接口,让子类去实现这个接口。如果鸭子会飞,即实现flyable接口,如果不会飞,则不去实现这个接口。这样就解决了逻辑混乱的问题。但是它也存在很大的隐患,如果两种鸭子的飞行方式很像,则会产生很多重复(冗余)代码。如果有很多个子类,并且需要对所有子类的fly方法做一些小的修改,则会改动很多代码,重复代码导致改动变得很困难

这里使用接口存在的最大问题就是:继承(实现)接口无法达到代码复用的目的

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

把会变的部分取出并“封装”起来,好让其他部分不会受影响。结果如何?代码变化引起的不经意后果变少,系统变得更有弹性。

通过分析可知,duck类中变化的部分是fly()和quack()方法,会随着子类的不同而改变。因此将这些不同的行为封装成类。为了保证程序的灵活性,应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态地改变鸭子的飞行行为。

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

现在,我们让FlyBehavior类和QuackBehavior类分别实现Flying和Quacking接口。这样对于fly和quack的具体细节便不会出现在鸭子的子类中,因此在修改的时候也会显得很方便。

这时候相当于是鸭子的子类有指针指向它具有的行为,而具体的行为它不需要知道。采用组合的方法使得程序的耦合性降低,灵活性增大。

“针对接口编程”的真正含义是“针对超类型(supertype)”编程。接口是一个概念,针对接口编程的关键就在于多态

设计原则3:多用组合,少用继承

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

2. 观察者模式

讲解背景:气象监控应用中有三个部分,分别是气象站(获取实际气象数据的物理装置),WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。

因此这个项目的工作是:建立一个应用,利用WeatherData对象获取数据,并更新三个布告板:目前状况、气象统计和天气预报。

WeatherData类中有getTemperature(),getHumidity(),getPressure(),measurementsChanged()。

一种错误的做法是,在measurementsChanged()方法中分别调用currentConditionsDisplay,statisticsDisplay,forecastDisplay类的update(temp,humidity,pressure)的方法。

这种针对具体实现编程的习惯一定得纠正!!

将观察者模式与报纸和杂志的订阅进行类比,出版者+订阅者=观察者模式。我们将观察者模式中的两个角色分别称为主题(Subject)和观察者(Observer)。

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
特别注意观察者模式定义了一系列对象之间的一对多关系。

观察者模式一对多之间的依赖如何产生?因为主题是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这样比起许多对象控制同一份数据来,可以得到更干净的OO设计。

观察者模式提供了一种对象设计,让主题和观察者之间松耦合

设计原则:为了交互对象之间的松耦合设计而努力

松耦合的设计之所以能够让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖降到了最低。

Java也有内置的观察者模式,java.util包(package)内包含最基本的Observer接口与Observable类,这和自己实现的Subject接口与Observer接口很相似。WeatherData类需要继承Observable类,布告板需要实现Observer接口。但是这样也会出现一些问题。这里需要注意到主题即WeatherData继承的是父类而不是接口。这就限制了Observable类的复用潜力,因此在必要的时候需要实现自己的观察者模式来提高灵活性,增强复用性。

需要注意setChanged()方法在notifyObservers()方法之前调用。

在JDK中的各种listener的实现也属于观察者模式。