UML软件工程组织

 

 

《Head.First设计模式》的学习笔记
 
2008-03-27作者:wxj1020 来自:cnblogs.com
 

书中列举了一些让大脑就范的原则,其实这也是我们在学习中应该遵循的原则。这些原则如下:

1、慢一点,你理解的越多,需要记的就越少。

2、勤做练习,自己记笔记。

3、上床睡觉之前不要再看别的书了,或者至少不再看其他有难度的东西。

4、要喝水,而且要多喝点水。

5、大声说出来。

6、听听你的大脑怎么说。

7、要有点感觉!

8、设计一点东西!

书中列举的设计原则:

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

2、针对接口编程,而不是针对实现编程。

举例说明:

假设有一个抽象类Animal,有两个具体的实现(Dog与Cat)继承Animal。

“针对实现编程”的做法:Dog d = new Dog();d.bark();

“针对接口编程”的做法:Animal animal = new Dog();animal.makeSound();

或者:Animal animal = getAnimal();animal.makeSound();

3、多用组合,少用继承。

原因:

a、继承会使类无限膨大,可能会使类变得臃肿。

b、子类可能会继承父类中那些无用甚至有害的方法。

c、组合比继承更灵活,可以实现在执行中动态改变对象的功能。

4、为了交互对象之间的松耦合设计而努力。

先对策略模式有一个总体认识。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

结构:

下面通过鸭子模拟器的设计来具体介绍。

公司需要设计一套鸭子模拟器系统,该系统的第一次需求为:鸭子能够戏水;鸭子能够呱呱叫。根据该需求系统设计如下:

这个设计主要用了父类鸭子和子类绿头鸭、红头鸭,这样设计的目的是为了达到代码的复用。

过了一段时间,公司希望该系统能够满足新的需求:有些鸭子会飞。因此该系统需要进行修改,修改后的系统可能如下:

该系统在父类中加了“fly()”方法(在父类中加该方法是为了实现代码的复用)。这里就出现了两个问题:

(1)、所有的鸭子都会飞了。

(2)、所有鸭子的叫声都一样,都是“呱呱”叫。

注:这两个问题可以通过子类中方法覆盖来解除,但这样处理不是很好。鸭子的类别越多,这种处理的缺点就越明显。

其实这个系统的变化点是鸭子的叫声和鸭子的飞行能力,因此我们很容易想到把鸭子的叫声和鸭子的飞行能力做成接口,把这些变化的地方封装起来,这样上面的两个问题都可以解决,所以系统可能被修改为下面的样子:

MallardDuck和RedheadDuck既会飞又会叫,RubberDuck只会叫不会飞,DecoyDuck不会飞也不会叫。应该说这个系统没有问题了,从表面看这种设计完全符合需求,而且完全符合面向对象的理念,但是当鸭子的类别很多时,你会发现这种设计缺乏代码的复用,这两个独立出来的接口似乎没有任何意义,根本无法减轻工作量,还不如原来的设计呢。所以你可能会想到问题的关键是接口中的方法没有实现,那我们该怎么办呢?

我们的做法是把接口做成类,运用组合的方法来实现需求。考虑到“针对接口编程,而不是针对实现编程”的设计原则,我们的系统可能就会设计成如下的结构,这个结构就是应用了“策略”模式。

源代码下载:/Files/wxj1020/MiniSimuDuck.rar

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

结构:

例子:

下面以模拟气象站系统来加以说明。

需求分析:

该系统的需求如下:

1、气象站能够追踪目前的天气状况,包括温度、湿度、气压、

2、气象站能够提供三种布告板,分别显示目前天气状况、气象统计和简单的预报。

3、布告板上的数据必须实时更新。

4、气象站必须提供一组API,供其他开发人员开发其他的布告板。

设计部分:

基于以上需求,该系统可以设计成3部分:气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前的天气状况给用户看)。效果图如下:

错误的类图设计(即没有学过设计模式时的第一感觉)可能如下:

相应的代码实现部分:

 1public void MeasurementsChanged()
 2        {
 3            temperature = this.GetTemperature();   // 获得温度
 4            humidity = this.GetHumidity();         // 获得湿度
 5            pressure = this.GetPressure();         // 获得气压 
 6
 7            MyCurrentConditionsDisplay.Update(temperature, humidity, pressure);     // 更新目前天气状态板
 8            MyStatisticsDisplay.Update(temperature, humidity, pressure);            // 更新气象统计板
 9            MyForcastDisplay.Update(temperature, humidity, pressure);               // 更新天气预报板            
10        }

这个类图设计的缺点:

1)、该设计是针对具体实现编程,而非针对接口。

2)、对于每个新的布告板,我们都得修改代码。

3)、我们无法在运动时动态得增加或删除布告板。

4)、我们尚未封装改变的部分。

那么如何改正这些缺点呢?

首先我们必须明白这些缺点的根源在哪里。很明显,我们在类图设计时依赖关系错了,应该依赖倒置。CurrrentConditionsDisplay类、StatisticsDisplay类和ForcastDisplay类应该依赖WeatherData类,而不是相反,这样就可以起到解耦的目的。

其次,CurrrentConditionsDisplay类、StatisticsDisplay类和ForcastDisplay类都有一个Update()方法,因此应该提炼一个接口,这样可以实现“针对接口编程”,使代码更加灵活,也方便其他开发人员开发其他的布告板。

进一步思考:

1)、改正这些缺点后,我们的类图已经与观察者模式的结构有点类似了。

2)、我们的气象站系统的最大问题其实就是一对多的依赖引起的,而观察者模式正是解除一对多关系的不二法门,因此我们有必要采用观察者模式。

采用了观察者模式后设计的类图应该是这样:

WeatherDatea实现ISubject接口,CurrentConditionsDisplay、ForcastDisplay、StatisticsDisplay实现IObserver接口,ISubject调用IObserver,CurrentConditionsDisplay、ForcastDisplay、StatisticsDisplay调用ISubject。

相应的代码实现部分:


 
 1public class WeatherData : ISubject
 2    {
 3        private float temperature;
 4        private float humidity;
 5        private float pressure;
 6        private List<IObserver> myList = new List<IObserver>();
 7
 8        public void SetWeatherData(float paramTemp, float paramHumidity, float paramPressure)
 9        {
10            this.temperature = paramTemp;
11            this.humidity = paramHumidity;
12            this.pressure = paramPressure;
13            MeasurementsChanged();
14        }

15
16        public void MeasurementsChanged()
17        {
18            this.NotifyObservers();
19        }

20       
21        public void RegisterObserver(IObserver paramIObserver)
22        {
23            myList.Add(paramIObserver);
24        }

25
26        public void RemoveObserver(IObserver paramIObserver)
27        {
28            myList.Remove(paramIObserver);
29        }

30
31        public void NotifyObservers()
32        {
33            foreach (IObserver observer in myList)
34            {
35                observer.Update(temperature, humidity, pressure);
36            }

37        }

38    }

源代码下载:/Files/wxj1020/WeatherStation.rar

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号