zoukankan      html  css  js  c++  java
  • 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());

    }

    }

    运行结果

  • 相关阅读:
    如何动态调用WebServices
    Cache及(HttpRuntime.Cache与HttpContext.Current.Cache)
    SQL创建索引(转)
    TSQL用法四:OpenDataSource, OpenRowSet
    AppDomain动态加载程序集
    hdu 2544 最短路
    hdu 1151 Air Raid
    hdu3790 最短路径问题
    hdu 1548 A strange lift
    对于 前K短路径问题 和 A*算法 的一些小小总结
  • 原文地址:https://www.cnblogs.com/Sweethoney/p/6721396.html
Copyright © 2011-2022 走看看