# 观察者模式

## 模式说明

当一个对象变化时，所有与他有关联关系的对象都将因为这个变化而变化，将前者称为目标对象，后者称为观察者对象。例如以人行道上的红绿灯为目标对象，行人和车辆为观察者对象。当红绿灯从红灯变为绿灯时，行人因为这个变化而从等待转变为过马路，而汽车则从通过转变为等待，这些观察者都对目标的变化做出了反映。此类场景可以用观察者模式实现。

本示例以人民币汇率为目标对象，进口公司和出口公司作为观察者对象。演示目标对象变化时，如何通知所有观察者做出其响应的动作。

\\

## 结构

**抽象目标类**: 观察者需要观察的对象，类内实现添加和删除观察者的普通方法，定义一个通知所有观察者的抽象方法。 **具体目标类**: 继承抽象目标类，实现抽象方法。 **抽象观察者类**: 持有一个`name`属性，并有对应的`getter/setter`。定义了一个响应目标对象变化的抽象方法`action`，被目标类通知时执行。 **具体观察者类**: 继承抽象观察者类，实现抽象方法`action`。

\\

## 代码演示

```java
package com.yukiyama.pattern.behavior;

import java.util.ArrayList;
import java.util.List;

/**
 * 观察者模式
 */
public class ObserverDemo {

    public static void main(String[] args) {
        // 声明一个具体目标对象，人民币汇率
        Rate rate = new RMBRate();
        // 声明两个不同的观察者对象，进口公司和出口公司
        Company watcher1 = new ImportCompany();
        Company watcher2 = new ExportCompany();
        // 将观察者都加入到目标对象中，以便目标能够在自身产生变化时通知所有观察者
        rate.add(watcher1);
        rate.add(watcher2);
        // 当汇率升高3.5%时，进出口公司分别对这个变化做出应对
        rate.change(0.035);
        // 当汇率下降6.8%时，进出口公司分别对这个变化做出应对
        rate.change(-0.068);
    }

}

/**
 * 抽象目标类(被观察事物)
 * 持有一个观察者类实例的集合，实现两个非抽象方法，add方法用来添加观察者
 * 实例，remove方法用来删除观察者实例。另有一个抽象方法change，传入变化。
 */
abstract class Rate{
    protected List<Company> companies = new ArrayList<>();
    
    public void add(Company company) {
        companies.add(company);
    }
    public void remove(Company company) {
        companies.remove(company);
    }
    public abstract void change(double changedRate);
}

/**
 * 具体目标类
 * 继承抽象目标类，实现抽象方法change，在方法内遍历其持有的所有观察者，
 * 传入变化，执行观察者的action方法。
 */
class RMBRate extends Rate{
    @Override
    public void change(double changedRate) {
        System.out.printf("人民币汇率变动%.1f%%\n", changedRate*100);
        for(Company company : this.companies) {
            company.action(changedRate);
        }
    }
}

/**
 * 抽象观察者类
 * 持有一个name属性，并有对应的getter/setter。定义了一个action抽象方法。
 * 下例以公司为抽象观察者类。
 */
abstract class Company{
    private String name;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public abstract void action(double changedRate);
}

/**
 * 具体观察者类
 * 通过无参构造器初始化name。实现抽象观察者类中的抽象方法action。
 * 下例是进口公司观察者，action内实现当人民币汇率降低时减少相应比例
 * 的进口量，升高时增加相应比例的进口量。
 */
class ImportCompany extends Company{
    public ImportCompany() {
        this.setName("进口公司");
    }
    @Override
    public void action(double changedRate) {
        System.out.printf("%s动作：\n", this.getName());
        if(changedRate < 0) {
            System.out.printf("人民币汇率下降了%.1f%%，减少%.1f%%进口量。\n", 
                    -changedRate*100, -changedRate*100);
        } else {
            System.out.printf("人民币汇率上升了%.1f%%，增加%.1f%%进口量。\n", 
                    changedRate*100, changedRate*100);
        }
    }
}

/**
 * 具体观察者类
 * 下例是出口公司观察者，action内实现当人民币汇率降低时增加相应比例
 * 的进口量，升高时减少相应比例的进口量。
 */
class ExportCompany extends Company{
    public ExportCompany() {
        this.setName("出口公司");
    }
    @Override
    public void action(double changedRate) {
        System.out.printf("%s动作：\n", this.getName());
        if(changedRate < 0) {
            System.out.printf("人民币汇率下降了%.1f%%，增加%.1f%%出口量。\n", 
                    -changedRate*100, -changedRate*100);
        } else {
            System.out.printf("人民币汇率上升了%.1f%%，下降%.1f%%出口量。\n", 
                    changedRate*100, changedRate*100);
        }
    }
}
```
