观察者模式与装饰者模型

114 阅读3分钟

观察者模式(Observer)

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

设计原则

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

实例

自己实现

WeatherData的布告板

  • WeatherData主题
public class WeatherData implements Subject {
    /**
     * WeatherData实现了Subject接口
     *    registerObserver、removeObserver和notifyObservers
     * ArrayList记录观察者
     */
    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        observers = new ArrayList();
    }

    /**
     * 注册入队
     */
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    /**
     * 订阅者取消订阅则出队
     */
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i >= 0) {
            observers.remove(i);
        }
    }

    /**
     * 观察者实现update
     */
    public void notifyObservers() {
        for(int i = 0; i < observers.size(); i++) {
            Observer observer = (Observer)observer.get(i);
            observer.update(temperature, humidity, pressure);
        }
    }

    /**
     * 气象站更新->观察者更新
     */
    public void measurementsChanged() {
        notifyObsservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    //WeatherData的其他方法
}
  • 布告板
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    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");
    }
}

使用Java内置类

  • Observable
public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    /**
     * 不需要再建立队列了
     */
    public WeatherData() {}

    public void measurementsChanged() {
        setChanged();
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }    
}
  • Observable
import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay implements Observer, DisplayElement {
    Observer observer;
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    public void update(Observable obs, Object arg) {
        if(obs instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)obs;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }

    public void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }
}

装饰者模式

继承 vs 扩展

  • 利用继承设计子类的行为,是在编译时静态决定的
  • 利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

设计原则

  • 类应该对扩展开放,对修改关闭

装饰者模式的特点

  • 装饰者和被装饰对象有相同的超类型
  • 可以用一个或多个装饰者包装一个对象
  • 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它
  • 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的
  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

实例

  • Beverage
public abstract class Beverage {
    String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}
  • Condiment类 也是抽象类
public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}
  • 饮料类 意式浓缩:
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;
    }
}

摩卡

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 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 Soy(beverage3);
        beverage3 = new Mocha(beverage3);
        beverage3 = new Whip(beverage3);
        System.out.println(beverage3.getDescription()
                + " $" + beverage3.cost());
    }
}

装饰者模式——java内置I/O

public class LowerCaseInputStream extends FilterInputStream {
    public LowerCaseInputStream(InputStream in) {
        super(in);
    }

    public int read() throws IOException {
        int c = super.read();
        return (c == -1 ? c : Character.toLowerCase((char)c));
    }

    public int read(byte[] b, int offset, int len) throws IOException {
        int result = super.read(b, offset, len);
        for(int i = offset; i < offset + result; i++) {
            b[i] = (byte)Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}