案例分析:设计模式与代码的结构特性

释放双眼,带上耳机,听听看~!

请选择一种我们课程中介绍的设计模式,用您熟悉的编程语言提供一个典型的应用范例,并分析其代码结构特性。完成一篇研究报告,具体要求如下:

  •  引用关键代码(引用代码是为解释说明服务的,不要贴对解释问题无关的代码)解释该设计模式在该应用场景中的适用性;
  •  引入该设计模式后对系统架构和代码结构带来了哪些好处;
  •  解释其中用到的多态机制;
  •  说明模块抽象封装的方法;
  •  分析各个模块的内聚度和模块之间的耦合度;
  •  提供该应用范例完整的源代码包括构建部署的操作过程,建议以github版本库URL的方式提供源代码,其中README.md中说明构建部署的操作过程。

观察者模式

1、观察者模式的定义:

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

 

2、观察者模式的结构:

观察者模式的结构中主要有以下四个构成角色:

 

(1)Subject:抽象主题,抽象主题提供一个接口,可以增加和删除观察者对象。

(2)ConcreteSubject:具体主题,他把所有观察者对象保存在一个集合里,可以有任意数量的观察者。该角色还将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

(3)Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

(4)ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

从类图的角度来看,四个角色的关系如下:

案例分析:设计模式与代码的结构特性

 

 

3、具体案例分析:

现在有这样一种情况:气象局要求设计一套系统,这个系统有两个公告显示牌,每当后台的天气数据发生更新,两个公告牌的显示内容也要跟着变化。一个公告牌显示的是当前的天气,另一个是显示未来几天的天气。

按照观察者模式进行设计,和设计模式有关的代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1public interface Subject {
2
3    /**
4     * 注册观察者
5     */
6    void registerObserver(Observer observer);
7
8    /**
9     * 移除观察者
10     */
11    void removeObserver(Observer observer);
12
13    /**
14     * 通知观察者
15     */
16    void notifyObservers();
17}
18

这是主题接口Subject,是一个抽象接口类,是对我们的被观察者的抽象,具体类的实现会重写其中的方法。


1
2
3
4
5
1public interface Observer {
2
3    void update();
4}
5

这是观察者接口,它定义了一个更新接口,使得在得到主题更改通知时更新自己。


1
2
3
4
5
1public interface DisplayElement {
2
3    void display();
4}
5

公告牌用于显示的公共接口。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
1public class WeatherData implements Subject {
2
3    private List<Observer> observers;
4
5    private float temperature;//温度
6    private float humidity;//湿度
7    private float pressure;//气压
8    private List<Float> forecastTemperatures;//未来几天的温度
9
10    public WeatherData() {
11        this.observers = new ArrayList<Observer>();
12    }
13
14    @Override
15    public void registerObserver(Observer observer) {
16        this.observers.add(observer);
17    }
18
19    @Override
20    public void removeObserver(Observer observer) {
21        this.observers.remove(observer);
22    }
23
24    @Override
25    public void notifyObservers() {
26        for (Observer observer : observers) {
27            observer.update();
28        }
29    }
30
31    public void measurementsChanged() {
32        notifyObservers();
33    }
34
35    public void setMeasurements(float temperature, float humidity, float pressure, List<Float> forecastTemperatures) {
36        this.temperature = temperature;
37        this.humidity = humidity;
38        this.pressure = pressure;
39        this.forecastTemperatures = forecastTemperatures;
40        measurementsChanged();
41    }
42
43    public float getTemperature() {
44        return temperature;
45    }
46
47    public float getHumidity() {
48        return humidity;
49    }
50
51    public float getPressure() {
52        return pressure;
53    }
54
55    public List<Float> getForecastTemperatures() {
56        return forecastTemperatures;
57    }
58}
59

以上是WeatherData类,它继承了抽象接口Subjects,在这个类中,保存了Observer接口的对象列表。WeatherData相当于一个具体主题角色,它重写了注册、删除观察者,也因为它用观察者列表保存了观察者,所以可以在天气数据发送变化时通知所有观察者。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1public class CurrentConditionsDisplay implements Observer, DisplayElement {
2
3    private WeatherData weatherData;
4
5    private float temperature;//温度
6    private float humidity;//湿度
7    private float pressure;//气压
8
9    public CurrentConditionsDisplay(WeatherData weatherData) {
10        this.weatherData = weatherData;
11        this.weatherData.registerObserver(this);
12    }
13
14    @Override
15    public void display() {
16        System.out.println("当前温度为:" + this.temperature + "℃");
17        System.out.println("当前湿度为:" + this.humidity);
18        System.out.println("当前气压为:" + this.pressure);
19    }
20
21    @Override
22    public void update() {
23        this.temperature = this.weatherData.getTemperature();
24        this.humidity = this.weatherData.getHumidity();
25        this.pressure = this.weatherData.getPressure();
26        display();
27    }
28}
29

以上是显示当前天气的公告牌,它是一个具体类,继承了两个公共接口:观察者Observer和显示信息DisplayElement。在这个类中,重写了display和update函数,当被观察者WeatherData发生变化时,这个公告牌作为观察者,被观察者WeatherData将会遍历到这个观察者对象,调用其中的两个函数,完成公告牌的更新。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1public class CurrentConditionsDisplay implements Observer, DisplayElement {
2
3    private WeatherData weatherData;
4
5    private float temperature;//温度
6    private float humidity;//湿度
7    private float pressure;//气压
8
9    public CurrentConditionsDisplay(WeatherData weatherData) {
10        this.weatherData = weatherData;
11        this.weatherData.registerObserver(this);
12    }
13
14    @Override
15    public void display() {
16        System.out.println("当前温度为:" + this.temperature + "℃");
17        System.out.println("当前湿度为:" + this.humidity);
18        System.out.println("当前气压为:" + this.pressure);
19    }
20
21    @Override
22    public void update() {
23        this.temperature = this.weatherData.getTemperature();
24        this.humidity = this.weatherData.getHumidity();
25        this.pressure = this.weatherData.getPressure();
26        display();
27    }
28}
29

和显示当前天气的公告牌类似,这是一个显示未来天气的公告牌,这个类和当前天气公告牌类是并列的逻辑关系,都是天气数据的观察者。其中的display和update函数的实现与当前天气公告牌类中的实现不同,体现了角色相同,但功能不同。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1public class ObserverPatternTest {
2
3    public static void main(String[] args) {
4
5        WeatherData weatherData = new WeatherData();
6        CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
7        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
8
9        List<Float> forecastTemperatures = new ArrayList<Float>();
10        forecastTemperatures.add(22f);
11        forecastTemperatures.add(-1f);
12        forecastTemperatures.add(9f);
13        forecastTemperatures.add(23f);
14        forecastTemperatures.add(27f);
15        forecastTemperatures.add(30f);
16        forecastTemperatures.add(10f);
17        weatherData.setMeasurements(22f, 0.8f, 1.2f, forecastTemperatures);
18    }
19}
20

最后,这就是天气数据发生一次变化后的场景模拟,两个公告牌类在创建时就”订阅“或者”注册“了天气数据,天气数据类相当于拥有一个”注册表“,一旦天气数据成员变量发生变化,就notify所有注册的观察者。所以程序的结果会打印出最新更新的天气内容。

 

4、引入该设计模式后对系统架构和代码结构带来了哪些好处?

降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系;目标与观察者之间建立了一套触发机制。

具体来说,在上例中,好处在于,不需要在天气数据类中一开始就确定要更新的公告牌,因为考虑到可扩展性,后期可能会有很多很多的公告牌需要更新,采用观察者模式,就避免了程序的修改。

 

5、解释其中用到的多态机制,说明模块抽象封装的方法

这里多态机制主要体现在抽象接口的使用,在设计观察者时,先使用了一个观察者的抽象接口类Observer,这样在天气数据类中,不论是什么样的具体的观察者,都被看做是抽象观察者Observer,调用抽象观察者的update函数,再根据不同的具体观察者调用不同的update实现。这样的抽象非常重要,保证了被观察者中处理观察者方法上的一致性。

 

6、分析各个模块的内聚度和模块之间的耦合度

观察者模式将观察者和主题(被观察者)彻底解耦,主题只知道观察者实现了某一接口(也就是Observer接口)。并不需要观察者的具体类是谁、做了些什么或者其他任何细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现了Observer接口的对象列表。总而言之,具体观察者与被观察者没有耦合,抽象观察者与被观察者有一定耦合关系,但因为抽象类自身太简单,没有实现,所以也是很低的耦合。

 

  github地址:https://github.com/BaronZ88/DesignPatterns


1
2

2

给TA打赏
共{{data.count}}人
人已打赏
安全运维

基于spring boot和mongodb打造一套完整的权限架构(二)【MAVEN依赖以及相应配置】

2021-12-11 11:36:11

安全运维

Ubuntu上NFS的安装配置

2021-12-19 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索