java设计模式-观察者模式,装饰者模式

1.1定义

慨念:定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新

即:主题和观察者定义了一对多的关系,观察者依赖于主题,只要主题发生变化,观察者就会被通知。

目的:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

1.2 底层机制

将一个状态会发生改变的对象定义成主题,所有的依赖对象定义为观察者。建立主题接口,对象使用该接口注册为观察者,或者把自己从观察者中删除。定义一个具体的主题类实现主题接口。建立观察者接口,声明更新状态的方法,具体的观察者都要实现此接口。当主题状态发生改变时,通过观察者接口将改变发送给具体的观察者。

1.3实现方式

1、观察者模式必须包含两个角色:主题和观察者。业务数据是主题,用户界面是观察者。当主题数据发生改变是会通知观察者,观察者做出的相应响应。

2、实现观察者模式的方法不止一种,最为直接的一种为:注册、通知、撤销。

1.4体现的设计原则

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

2、找出程序中会变化的方面,然后将其和固定不变的方面相分离。

3、针对接口编程,不针对实现编程。

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

1.5优缺点

优点:

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

2、建立了一套触发机制。

缺点:

1、如果主题有很多的直接或间接的观察者,将改变通知到所有的观察者需要花费很长时间。

2、主题不知道观察者得细节,只知道观察者实现了主题的接口。

3、如果主题和观察之间存在循环依赖,可会导致系统崩溃。

1.6 类图

1.7 案例:气象观测站

简介:建立一个应用,利用WeatherData对象取得数据,并且更新三个布告板:目前状况,气象统计和天气预报。

1.7.1 类图

1.7.2代码逻辑

 1、首先我们从接口开始建立,建立主题接口和观察者接口,以及界面显示接口。

 2、在weatherData中实现主题接口。

 3、建立布告板。

 4、编写测试程序。

1.7.3代码实现

1、实现主题接口()

public interface Subject {

public void registerObserver(Observer o);//注册

public void removeObserver(Observer o);//删除

public void notifyObservers();//通知

   }

2、实现观察者接口

public interface Observer {

public void update(float temp,float humidity,float pressure);//数据更新

}

3、实现布告板接口

public interface DisplayElement {

public void display();//显示界面

}

 

4、完成WeatherData类实现主题接口

public class WeatherDate implements Subject {

private ArrayList observers;//记录观察者,ArrayList是在构造器中建立的。

private float temperature;

private float humidity;

private float pressure;

 

public WeatherDate() {

observers = new ArrayList();

}

//subject接口的实现

//注册观察者

public void registerObserver(Observer o) {

observers.add(o);

}

//取消注册

public void removeObserver(Observer o) {

int i = observers.indexOf(o);

if (i >= 0) {

observers.remove(i);

}

}

//通知观察者数据改变

public void notifyObservers() {

for (int i = 0; i < observers.size(); i++) {

Observer observer = (Observer)observers.get(i);

observer.update(temperature, humidity, pressure);

}

}

//检测到数据更新时,通知观察者

public void measurementsChanged() {

notifyObservers();

}

 

public void setMeasurements(float temperature, float humidity, float pressure) {

this.temperature = temperature;

this.humidity = humidity;

this.pressure = pressure;

measurementsChanged();

}

}

5、建立显示当前观测值得布告板

public class CurrentConditionsDisplay implements Observer,DisplayElement{

 

private float temperature;

private float humidity;

private Subject weatherDate;

 

public void update(float temperature, float humidity, float pressure) {

this.temperature=temperature;

this.humidity=humidity;

display();

}

 

public void display() {

System.out.println("Current conditions:"+ temperature+"f degrees and "+humidity+"% humidity");

}

 

public CurrentConditionsDisplay(Subject weatherDate){

this.weatherDate=weatherDate;

weatherDate.registerObserver(this);

}

}

6、建立根据气压计显示天气预报

 

public class ForecastDisplay implements Observer, DisplayElement {

private float currentPressure = 29.92f;  

private float lastPressure;

private WeatherDate weatherData;

 

public ForecastDisplay(WeatherDate weatherData) {

this.weatherData = weatherData;

weatherData.registerObserver(this);

}

 

public void update(float temp, float humidity, float pressure) {

                lastPressure = currentPressure;

currentPressure = pressure;

 

display();

}

 

public void display() {

System.out.print("Forecast: ");

if (currentPressure > lastPressure) {

System.out.println("Improving weather on the way!");

} else if (currentPressure == lastPressure) {

System.out.println("More of the same");

} else if (currentPressure < lastPressure) {

System.out.println("Watch out for cooler, rainy weather");

}

  }

}

7、建立跟踪最小值,平均值,最大的观测值并显示它们的布告板。

 

public class StatisticsDisplay implements Observer, DisplayElement {

private float maxTemp = 0.0f;

private float minTemp = 200;

private float tempSum= 0.0f;

private int numReadings;

private WeatherDate weatherData;

 

public StatisticsDisplay(WeatherDate weatherData) {

this.weatherData = weatherData;

 weatherData.registerObserver(this);

}

 

public void update(float temp, float humidity, float pressure) {

tempSum += temp;

numReadings++;

 

if (temp > maxTemp) {

maxTemp = temp;

}

 

if (temp < minTemp) {

minTemp = temp;

}

 

display();

}

 

public void display() {

System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)

+ "/" + maxTemp + "/" + minTemp);

}

}

 

8、建立测试类

 

public class WeatherSation {

 

public static void main(String[] args) {

WeatherDate weatherDate=new WeatherDate();

CurrentConditionsDisplay currentDisplay=new CurrentConditionsDisplay(weatherDate);

StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherDate);

ForecastDisplay forecastDisplay=new ForecastDisplay(weatherDate);

 

weatherDate.setMeasurements(80, 65, 30.4f);

weatherDate.setMeasurements(82, 70, 29.2f);

weatherDate.setMeasurements(78, 90, 29.2f);

 

}

 

}

9、运行结果

1.8思维拓展

1java内置的观察者模式。java.util包内包含最基本的Observer接口与Observable类,利用java内置的观察者模式我们只需要扩展(继承)Observable,并告诉它何时该通知观察者,剩下的API会帮我们做好。

1.9应用场景

1、有多个子类共有的方法,且逻辑相同。

2、存在数据实时更新的情况下。

3、监听器

2.1应用实例

1、报纸和杂志的订阅。

2、拍卖会上,拍卖师观察最高标价,然后,通知其他竞价者。

3、护士将患者每天的检查数据告知患者。

2.2注意事项

1、有多个观察者时,不可以依赖特定的通知次序。(原因:一旦观察者的实现有所改变,通知的次序就会改变,很可能会产生错误的结果。)

2、Java中已经有了对观察者模式支持的类,包括通用的java.util.Observable

3、避免循环引用。

2.装饰者模式

1.1定义

装饰者模式:动态地将责任附加到对象身上。若要扩展功能,装饰者提供了比继承更有弹性·的替代方案。

1.2 底层机制

利用组合和委托实现在运行时具有继承的效果。我们将装饰者和组件进行组合,就是在加入新的行为,装饰者和组件将更有弹性的加以混合和匹配。

1.3体现的设计原则

1、类应该对扩展开放,对修改关闭。

1.4优缺点

优点:

1、通过动态组合对象,可以写新的代码添加新的功能,而无需修改现有代码。

2、装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

3、可以透明的插入装饰者,客户程序甚至不需要知道在和装饰者打交道。

缺点:

1、多层修饰比较复杂。

2、遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度。

3、有些代码会依赖特定的类型,而这样的代码一导入装饰者,就出现错误。

4、采用装饰者在实例化组件时,将增加代码的复杂度,一旦使用装饰者模式,不只需要实例化组件,还要把此组件包装进装饰者中。天晓得有几个组件。

 
   

1.5类图

 

1.6案例:星巴克咖啡

 1.6.1类图

1.6.2代码逻辑

1、拿一个咖啡(DarkRoast)对象

2、以摩卡(Mocha)对象装饰它

3、以奶泡(Whip)对象修饰它

4、调用cost()方法。并依赖委托将调料的价钱加上去

1.6.3代码实现

1、实现Beverage类,其为一个抽象类。

 

public abstract class Beverage {

String description = "Unknown Beverage";

  

public String getDescription() {

return description;

}

 

public abstract double cost();

}

 

2、实现调料CondimentDecorator抽象类,即:装饰者类

 

public abstract class CondimentDecorator extends Beverage {

public abstract String getDescription();

}

 

3、写具体组件,饮料类的代码

浓缩咖啡

public class Espresso extends Beverage {

  

public Espresso() {

description = "Espresso";

}

public double cost() {

return 1.99;

}

}

综合咖啡:

public class HouseBlend extends Beverage {

public HouseBlend() {

description = "House Blend Coffee";

}

 

public double cost() {

return .89;

}

}

4、编写调料类代码,即具体的装饰者类

摩卡:

public class Mocha extends CondimentDecorator {

Beverage beverage;//用一个实例变量记录被装饰者

    //将被装饰者记录到实例变量中

public Mocha(Beverage beverage) {

this.beverage = beverage;

}

 

public String getDescription() {

return beverage.getDescription() + ", Mocha";

}

 

public double cost() {

return .20 + beverage.cost();

}

}

奶泡:

public class Whip extends CondimentDecorator {

Beverage beverage;

 

public Whip(Beverage beverage) {

this.beverage = beverage;

}

 

public String getDescription() {

return beverage.getDescription() + ", Whip";

}

 

public double cost() {

return .10 + beverage.cost();

}

}

5、编写测试代码:

public class StarbuzzCoffee {

 

public static void main(String args[]) {

Beverage beverage = new Espresso();

System.out.println(beverage.getDescription()

+ " $" + beverage.cost());

 

Beverage beverage2 = new DarkRoast();

beverage2 = new Mocha(beverage2);

beverage2 = new Mocha(beverage2);

beverage2 = new Whip(beverage2);

System.out.println(beverage2.getDescription()

+ " $" + beverage2.cost());

 

Beverage beverage3 = new HouseBlend();

beverage3 = new Mocha(beverage3);

beverage3 = new Whip(beverage3);

System.out.println(beverage3.getDescription()

+ " $" + beverage3.cost());

}

}

 

运行结果

 

posted @ 2017-04-17 09:36  巨蟹糖  阅读(355)  评论(0编辑  收藏  举报