大话设计模式之观察者模式总结-java实现

注:示例来自《大话设计模式》

现有如下需求 公司员工想要利用工作时间炒股票 老板经常外出 怕被老板看到 于是拜托前台小姐姐 老板回来的时候打个电话通知他们 初步代码实现如下

前台秘书类

package Test14;

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

//前台秘书类
public class Secretary {

    //同事列表
    private List observers = new ArrayList();
    //前台状态
    private String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    //增加
    public void Attach(StockObserver observer)
    {
        observers.add(observer);
    }

    //减少
    public void Detach(StockObserver observer)
    {
        observers.remove(observer);
    }

    //通知
    public void Notify()
    {
        for (StockObserver o : observers) {
            o.Update();
        }
    }

}

看股票同事类

package Test14;

//看股票的同事
public class StockObserver {

    private String name;
    private Secretary sub;

    public StockObserver(String name, Secretary sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public void Update()
    {
        System.out.println(sub.getAction()+" "+name+" "+" 关闭股票行情,继续工作!");
    }

}

客户端代码

package Test14;

public class Program {

    public static void main(String[] args)
    {

        //前台小姐童子喆
        Secretary tongzizhe = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);
        StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);

        //前台记下了两位同事
        tongzizhe.Attach(tongshi1);
        tongzizhe.Attach(tongshi2);
        //发现老板回来
        tongzizhe.setAction("老板回来了!");
        //通知两个同事
        tongzizhe.Notify();

    }

}

上面的代码 前台类与同事类互相耦合 前台类要增加同事类 同事类需要前台的状态 如果还有人是想看NBA的网上直播 该怎么办呢 下面进行解耦

增加了抽象的观察者

package Test14;

//抽象观察者
public abstract class Observer {

    protected String name;
    protected Secretary sub;

    public Observer(String name, Secretary sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public abstract void Update();

}

两个具体观察者

package Test14;

//看股票的同事
public class StockObserver extends Observer {

    public StockObserver(String name, Secretary sub) {
        super(name, sub);

    }

    public void Update()
    {
        System.out.println(sub.getAction()+" "+name+" "+" 关闭股票行情,继续工作!");
    }

}
package Test14;

//看NBA的同事
public class NBAObserver extends Observer {

    public NBAObserver(String name, Secretary sub) {
        super(name, sub);

    }

    @Override
    public void Update() {

        System.out.println(sub.getAction()+" "+name+" "+" 关闭NBA直播,继续工作!");

    }

}

前台秘书类

package Test14;

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

//前台秘书类
public class Secretary {

    //同事列表
    private List observers = new ArrayList();
    //前台状态
    private String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    //增加
    public void Attach(Observer observer)
    {
        observers.add(observer);
    }

    //减少
    public void Detach(Observer observer)
    {
        observers.remove(observer);
    }

    //通知
    public void Notify()
    {
        for (Observer o : observers) {
            o.Update();
        }
    }

}

客户端代码

package Test14;

public class Program {

    public static void main(String[] args)
    {

        //前台小姐童子喆
        Secretary tongzizhe = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", tongzizhe);

        //前台记下了两位同事
        tongzizhe.Attach(tongshi1);
        tongzizhe.Attach(tongshi2);
        //发现老板回来
        tongzizhe.setAction("老板回来了!");
        //通知两个同事
        tongzizhe.Notify();

    }

}

上面的代码 观察者与前台类还是存在耦合 前台类是一个具体的类 也应该抽象出来
如果前台秘书有事情没有通知到 那么就会被老板发现 这时老板也是通知者
另外 如果某一个同事和前台有矛盾 不再通知这位同事 此时应该把这个对象从观察者列表中删除 下面继续进行重构

增加抽象通知者接口

package Test14;

//通知者接口
public interface Subject {

    void Attach(Observer observer);
    void Detach(Observer observer);
    void Notify();
    String getAction();   

}

秘书与老板分别实现接口

package Test14;

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

//前台秘书类
public class Secretary implements Subject {

    //同事列表
    private List observers = new ArrayList();
    //前台状态
    private String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    //增加
    public void Attach(Observer observer)
    {
        observers.add(observer);
    }

    //减少
    public void Detach(Observer observer)
    {
        observers.remove(observer);
    }

    //通知
    public void Notify()
    {
        for (Observer o : observers) {
            o.Update();
        }
    }

}
package Test14;

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

//老板类
public class Boss implements Subject {

    //同事列表
    private List observers = new ArrayList();
    //老板状态
    private String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    //增加
    public void Attach(Observer observer)
    {
        observers.add(observer);
    }

    //减少
    public void Detach(Observer observer)
    {
        observers.remove(observer);
    }

    //通知
    public void Notify()
    {
        for (Observer o : observers) {
            o.Update();
        }
    }

}

抽象观察者

package Test14;

//抽象观察者
public abstract class Observer {

    protected String name;
    protected Subject sub;

    public Observer(String name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public abstract void Update();

}

具体观察者

package Test14;

//看股票的同事
public class StockObserver extends Observer {

    public StockObserver(String name, Subject sub) {
        super(name, sub);

    }

    public void Update()
    {
        System.out.println(sub.getAction()+" "+name+" "+" 关闭股票行情,继续工作!");
    }

}
package Test14;

//看NBA的同事
public class NBAObserver extends Observer {

    public NBAObserver(String name, Subject sub) {
        super(name, sub);

    }

    @Override
    public void Update() {

        System.out.println(sub.getAction()+" "+name+" "+" 关闭NBA直播,继续工作!");

    }

}

客户端代码

package Test14;

public class Program {

    public static void main(String[] args)
    {

        //老板胡汉三
        Boss huhansan = new Boss();

        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);

        huhansan.Attach(tongshi1);
        huhansan.Attach(tongshi2);

        huhansan.Detach(tongshi1);

        //老板回来
        huhansan.setAction("我胡汉三回来了!");
        //发出通知
        huhansan.Notify();

    }

}

由于魏关姹没有被通知到 所以他被当场抓获
上面的代码做到了两者都不耦合了 这就是观察者模式

观察者模式又叫做发布-订阅模式 观察者模式定义了一种一对多的依赖关系 让多个观察者对象同时监听某一个主题对象 这个主题对象在状态发生改变时 会通知所有观察者对象 使它们能够自动更新自己

将一个系统分割成一系列相互协作的类有一个很不好的副作用 那就是需要维护相关对象间的一致性 我们不希望为了维持一致性而使各类紧密耦合 这样会给维护 扩展和重用都带来不便

当一个对象的改变需要同时改变其他对象 而且它不知道具体有多少对象有待改变时 应该考虑使用观察者模式
当一个抽象模型有两个方面 其中一方面依赖于另一方面 这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用
总的来讲 观察者模式所做的工作其实就是在解除耦合 让耦合的双方都依赖于抽象 而不是依赖于具体 从而使得各自的变化都不会影响另一边的变化

缺点 虽然观察者模式提取出了抽象类 让类与类之间不互相依赖 共同依赖于抽象接口 这符合依赖倒转原则 但他们仍然依赖着抽象接口,而且有些时候不能提取出抽象的观察者(比如引用jar包)

下面我们使用委托机制进行重构
委托机制的实现不再需要提取观察者抽象类 观察者和通知者互不依赖 java利用反射即可实现 代码如下

事件类

package Test14;

import java.lang.reflect.Method;

public class Event {
    private Object object;

    private String methodName;

    private Object[] params;

    private Class[] paramTypes;

    public Event(Object object,String method,Object...args)
    {
        this.object = object;
        this.methodName = method;
        this.params = args;
        contractParamTypes(this.params);
    }

    private void contractParamTypes(Object[] params)
    {
        this.paramTypes = new Class[params.length];
        for (int i=0;i<params.length;i++)
        {
            this.paramTypes[i] = params[i].getClass();
        }
    }

    public void invoke() throws Exception
    {
        Method method = object.getClass().getMethod(this.methodName, this.paramTypes);//判断是否存在这个函数
        if (null == method)
        {
            return;
        }
        method.invoke(this.object, this.params);//利用反射机制调用函数
    }
}

事件管理类

package Test14;

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

public class EventHandler {

    private List objects;

    public EventHandler()
    {
        objects = new ArrayList();
    }

    public void addEvent(Object object, String methodName, Object...args)
    {
        objects.add(new Event(object, methodName, args));
    }

    public void Notify() throws Exception
    {
        for (Event event : objects)
        {
            event.invoke();
        }
    }
}

通知者抽象类

package Test14;

//通知者抽象类
public abstract class Subject {

    //通知者状态
    private String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    private EventHandler eventHandler = new EventHandler();

    public EventHandler getEventHandler()
    {
        return eventHandler;
    }

    public void setEventHandler(EventHandler eventHandler)
    {
        this.eventHandler = eventHandler;
    }

    public abstract void addListener(Object object,String methodName, Object...args);

    public abstract void Notify();

}

通知者实现类

package Test14;

//老板类
public class Boss extends Subject {

    //通知
    @Override
    public void Notify()
    {
        try {
            this.getEventHandler().Notify();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void addListener(Object object, String methodName, Object... args) {
        this.getEventHandler().addEvent(object, methodName, args);

    }

}

观察者类

package Test14;

//看股票的同事
public class StockObserver {

    private String name;
    private Subject sub;
    public StockObserver(String name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    //关闭股票行情
    public void CloseStockMarket()
    {
        System.out.println(sub.getAction()+" "+name+" "+" 关闭股票行情,继续工作!");
    }

}
package Test14;

//看NBA的同事
public class NBAObserver {

    private String name;
    private Subject sub;
    public NBAObserver(String name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    //关闭NBA直播
    public void CloseNBADirectSeeding() {

        System.out.println(sub.getAction()+" "+name+" "+" 关闭NBA直播,继续工作!");

    }

}

客户端代码

package Test14;

public class Program {

    public static void main(String[] args)
    {

        //老板胡汉三
        Boss huhansan = new Boss();

        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);

        huhansan.addListener(tongshi1, "CloseStockMarket");
        huhansan.addListener(tongshi2, "CloseNBADirectSeeding");

        //老板回来
        huhansan.setAction("我胡汉三回来了!");
        //发出通知
        huhansan.Notify();

    }

}

委托就是一种引用方法的类型 一旦为委托分配了方法 委托将与该方法具有完全相同的行为 委托方法的使用可以像其他任何方法一样 具有参数和返回值 委托可以看做是对函数的抽象 是函数的类 委托的实例将代表一个具体的函数

一个委托可以搭载多个方法 所有方法被依次唤起 更重要的是 它可以使得委托对象所搭载的方法并不需要属于同一个类

你可能感兴趣的:(设计模式)