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思维拓展
1、java内置的观察者模式。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());
}
}
运行结果