1. 程式人生 > >大話設計模式之觀察者模式總結-java實現

大話設計模式之觀察者模式總結-java實現

注:示例來自《大話設計模式》

現有如下需求 公司員工想要利用工作時間炒股票 老闆經常外出 怕被老闆看到 於是拜託前臺小姐姐 老闆回來的時候打個電話通知他們 初步程式碼實現如下

前臺祕書類

package Test14;

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

//前臺祕書類
public class Secretary {

    //同事列表
    private List<StockObserver> observers = new ArrayList<StockObserver>();
    //前臺狀態
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<Observer> observers = new ArrayList<Observer>();
    //前臺狀態
    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<Observer> observers = new ArrayList<Observer>();
    //前臺狀態
    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<Observer> observers = new ArrayList<Observer>();
    //老闆狀態
    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<Event> objects;

    public EventHandler()
    {
        objects = new ArrayList<Event>();
    }

    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();

    }

}

委託就是一種引用方法的型別 一旦為委託分配了方法 委託將與該方法具有完全相同的行為 委託方法的使用可以像其他任何方法一樣 具有引數和返回值 委託可以看做是對函式的抽象 是函式的類 委託的例項將代表一個具體的函式

一個委託可以搭載多個方法 所有方法被依次喚起 更重要的是 它可以使得委託物件所搭載的方法並不需要屬於同一個類