混合模式:觀察者模式+中介者模式

有一個產品它有多個觸發事件,它產生的時候觸發一個創建事件,修改的時候觸發修改事件,刪除的時候觸發刪除事件,這就類似於我們的文本框,初始化(也就是創建)的時候要觸發一個onLoad或onCreate事件,修改的時候觸發onChange事件,雙擊(類似於刪除)的時候又觸發onDbClick事件。

主要的設計模式:

  • 工廠方法模式:負責產生產品對象,方便產品的修改和擴展,並且實現產品和工廠的緊耦合,避免產品隨意被創建而無觸發事件的情況發生。
  • 橋樑模式:在產品事件兩個對象的關係中使用橋樑模式,這樣兩者都可以自由地擴展而不會破壞原有的封裝
  • 觀察者模式:使用觀察者模式,使事件的變化通知處理者,而且觀察者模式可以有多個觀察者,也就是說這個架構是可以有多層級、多分類的處理者。想重新擴展一個新類型(新接口)的觀察者,只需要擴展ProductEvent即可。
  • 中介者模式:使用中介者模式處理事件與事件和處理者與處理者之間的耦合關係。

(1)構建產品和產品工廠

  • 產品要有創建、修改、銷燬的動作,使用工廠方法模式
  • 產品也可以通過克隆方式產生,使用原型模式
  • 產品只能由工廠類創建,不能被其他對象通過new方式創建,用單來源調用(Single Call)方法,即一個對象只能由固定的對象初始化。

 
public class Product implements Cloneable {
	private String name;	//產品名稱
	private boolean canChanged = false;	//屬性是否可以變更
	public Product(ProductManager manager,String name) {
		if (manager.isCreateProduct()) {
			canChanged = true;
			this.name = name;
		}
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		if (canChanged) {
			this.name = name;
		}		
	}
	@Override
	public Product clone() {
		Product product = null;
		try {
			product = (Product)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return product;
	}
}
import com.sfq.impl.ProductEventType;
public class ProductManager {
	private boolean isPermittedCreate = false;
	//建立一個產品
	public Product createProduct(String name) {
		isPermittedCreate = true;
		Product product = new Product(this, name);
		return product;
	}
	//廢棄一個產品
	public void abandonProduct(Product p) {
		p = null;	//銷燬掉一個產品,例如刪除數據庫記錄
	}
	//修改一個產品
	public void editProduct(Product p,String name) {
		p.setName(name);
	}
	//獲得是否可以創建一個產品
	public boolean isCreateProduct() {
		return isPermittedCreate;
	}
	//克隆一個產品
	public Product clone(Product p) {
		return p.clone();
	}
}

單來源調用(Single Call):在工廠類ProductManager中定義了一個私有變量isCreateProduct,該變量只有在工廠類的createProduct函數中才能設置爲true,在創建產品的時候,產品類Product的構造函數要求傳遞工廠對象,然後判斷是否能夠創建產品。

注意:採用單來源調用的兩個對象一般是組合關係,兩者有相同的生命期,它通常適用於有單例模式工廠方法模式的場景中。

(2)觸發事件

  • 產品新建要觸發事件,還要有人去處理這個事件,使用觀察者模式
  • 觀察者爲EventDispatch類,它使用了單例模式,避免對象膨脹,但同時也帶來了性能及線程安全隱患。
//定義了4個事件類型,分別是新建、修改、刪除以及克隆
public enum ProductEventType {
	NEW_PRODUCT(1),
	DEL_PRODUCT(2),
	EDIT_PRODUCT(3),
	CLONE_PRODUCT(4);
	private int value = 0;
	private ProductEventType(int value) {
		this.value = value;
	}
	public int getValue() {
		return value;
	}
}
import java.util.Observable;
import com.sfq.impl.ProductEventType;
public class ProductEvent extends Observable {
	private Product source;		//事件起源
	private ProductEventType type;	//事件類型
	//傳入事件的源頭,默認爲新建類型
	public ProductEvent(Product source) {
		this(source,ProductEventType.NEW_PRODUCT);
	}
	//事件源頭及事件類型
	public ProductEvent(Product source, ProductEventType type) {
		this.source = source;
		this.type = type;
		notifyEventDispatch();
	}
	//獲得事件起源
	public Product getSource() {
		return source;
	}
	//獲得事件類型
	public ProductEventType getEventType() {
		return this.type;
	}	
	//通知事件處理中心
	private void notifyEventDispatch() {
		super.addObserver(EventDispatch.getEventDispatch());
		super.setChanged();
		super.notifyObservers(source);
	}
}
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import com.sfq.impl.EventCustomType;
import com.sfq.impl.EventCustomer;
public class EventDispatch implements Observer {
	//單例模式
	private final static EventDispatch dispatch = new EventDispatch();
	private EventDispatch() {	//設置爲私有,不允許生成新的實例
	}
	//獲得單例對象
	public static EventDispatch getEventDispatch() {
		return dispatch;
	}
	//事件觸發
	@Override
	public void update(Observable o, Object arg) {
	}
}

被觀察者ProductEvent的動作發生變化後,會通過notifyEventDispatch()方法通知觀察者EventDispatch,然後觀察者通過 update()方法作出反應,這裏暫時空着,還沒反應。

(3)關聯產品和產品事件

產品和產品事件是兩個獨立的對象,兩者都可以獨立地擴展,使用橋樑模式。這裏使用產品管理類ProductManagger作爲橋樑。

ProductManagger類補充如下:

import com.sfq.impl.ProductEventType;
public class ProductManager {
	private boolean isPermittedCreate = false;
	//建立一個產品
	public Product createProduct(String name) {
		isPermittedCreate = true;
		Product product = new Product(this, name);
		//產生一個創建事件
		new ProductEvent(product,ProductEventType.NEW_PRODUCT);
		return product;
	}
	//廢棄一個產品
	public void abandonProduct(Product p) {
		new ProductEvent(p,ProductEventType.DEL_PRODUCT);
		p = null;	//銷燬掉一個產品,例如刪除數據庫記錄
	}
	//修改一個產品
	public void editProduct(Product p,String name) {
		p.setName(name);
		new ProductEvent(p,ProductEventType.EDIT_PRODUCT);
	}
	//獲得是否可以創建一個產品
	public boolean isCreateProduct() {
		return isPermittedCreate;
	}
	//克隆一個產品
	public Product clone(Product p) {
		new ProductEvent(p,ProductEventType.CLONE_PRODUCT);
		return p.clone();
	}
}

(4)事件的處理

現在只有1個產品類,如果產品類很多,不可能每個產品事件都寫一個處理者,對於產品事件來說,它最希望的結果就是我通知了事件處理者(也就是觀察者模式的觀察者),其他具體怎麼處理由觀察者來解決,那觀察者怎麼來處理這麼多的事件呢?事件的處理者必然有N多個。即如下圖所示:

我們可以使用中介者模式。把 EventDispatch類作爲事件分發的中介者,事件的處理者都是具體的同事類,它們有着相似的行爲,都是處理產品事件,但是又有不相同的邏輯,每個同事類對事件都有不同的處理行爲。

package com.sfq.action;

import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import com.sfq.impl.EventCustomType;
import com.sfq.impl.EventCustomer;
public class EventDispatch implements Observer {
	//單例模式
	private final static EventDispatch dispatch = new EventDispatch();
	//事件消費者
	private Vector<EventCustomer> customer = new Vector<EventCustomer>();
	private EventDispatch() {	//設置爲私有,不允許生成新的實例
	}
	//獲得單例對象
	public static EventDispatch getEventDispatch() {
		return dispatch;
	}
	//事件觸發
	@Override
	public void update(Observable o, Object arg) {
		//事件的源頭
		Product product = (Product)arg;
		//事件
		ProductEvent event = (ProductEvent)o;
		//處理者處理,這裏是中介者模式的核心
		for(EventCustomer e:customer) {
			//處理能力是否匹配
			for(EventCustomType t:e.getCustomType()) {
				if (t.getValue() == event.getEventType().getValue()) {
					e.exec(event);
				}
			}
		}
	}
	//註冊事件處理者
	public void registerCustomer(EventCustomer _customer) {
		this.customer.add(_customer);
	}
}
import java.util.Vector;
import com.sfq.action.ProductEvent;
public abstract class EventCustomer {
	//容納每個消費者能夠處理的級別
	private Vector<EventCustomType> customType = new Vector<EventCustomType>();
	//每個消費者都要聲明自己處理哪一類別的事件
	public EventCustomer(EventCustomType _type) {
		addCustomType(_type);
	}
	//每個消費者可以消費多個事件
	public void addCustomType(EventCustomType _type) {
		customType.add(_type);
	}
	//得到自己的處理能力
	public Vector<EventCustomType> getCustomType(){
		return customType;
	}
	//每個事件都要對事件進行聲明式消費
	public abstract void exec(ProductEvent event);
}
public enum EventCustomType {
	//新建事件
	NEW(1),
	//刪除事件
	DEL(2),
	//修改事件
	EDIT(3),
	//克隆事件
	CLONE(4);
	private int value = 0;
	private EventCustomType(int value) {
		this.value = value;
	}
	public int getValue() {
		return value;
	}	
}
import com.sfq.impl.EventCustomType;
import com.sfq.impl.EventCustomer;
import com.sfq.impl.ProductEventType;
public class Beggar extends EventCustomer {
	public Beggar() {
		super(EventCustomType.DEL);
	}
	@Override
	public void exec(ProductEvent event) {
		//事件的源頭
		Product p = event.getSource();
		//事件類型
		ProductEventType type = event.getEventType();
		System.out.println("乞丐處理事件:" + p.getName() + "銷燬,事件類型=" + type);
	}
}
import com.sfq.impl.EventCustomType;
import com.sfq.impl.EventCustomer;
import com.sfq.impl.ProductEventType;
public class Commoner extends EventCustomer {
	public Commoner() {
		super(EventCustomType.NEW);
	}
	@Override
	public void exec(ProductEvent event) {
		//事件的源頭
		Product p = event.getSource();
		//事件類型
		ProductEventType type = event.getEventType();
		System.out.println("平民處理事件:" + p.getName() + "誕生記,事件類型=" + type);
	}
}
import com.sfq.impl.EventCustomType;
import com.sfq.impl.EventCustomer;
import com.sfq.impl.ProductEventType;
public class Nobleman extends EventCustomer {
	public Nobleman() {
		super(EventCustomType.EDIT);
		super.addCustomType(EventCustomType.CLONE);
	}
	@Override
	public void exec(ProductEvent event) {
		//事件的源頭
		Product p = event.getSource();
		//事件類型
		ProductEventType type = event.getEventType();
		if (type.getValue() == EventCustomType.CLONE.getValue()) {
			System.out.println("貴族處理事件:" + p.getName() + "克隆,事件類型=" + type);
		} else {
			System.out.println("貴族處理事件:" + p.getName() + "修改,事件類型=" + type);
		}		
	}
}
import com.sfq.action.Beggar;
import com.sfq.action.Commoner;
import com.sfq.action.EventDispatch;
import com.sfq.action.Nobleman;
import com.sfq.action.Product;
import com.sfq.action.ProductManager;
public class Client {
	public static void main(String[] args) {
		//獲得事件分發中心
		EventDispatch dispatch = EventDispatch.getEventDispatch();
		//接受乞丐對事件的處理
		dispatch.registerCustomer(new Beggar());
		//接受平民對事件的處理
		dispatch.registerCustomer(new Commoner());
		//接受貴族對事件的處理
		dispatch.registerCustomer(new Nobleman());
		//建立一個原子彈生成工廠
		ProductManager factory = new ProductManager();
		//製作一個產品
		System.out.println("-----模擬創建產品事件-----");
		System.out.println("創建一個叫做小男孩的原子彈");
		Product p = factory.createProduct("小男孩原子彈");
		//修改一個產品 
		System.out.println("\n-----模擬修改產品事件-----"); 
		System.out.println("把小男孩原子彈修改爲胖子號原子彈"); 
		factory.editProduct(p, "胖子號原子彈"); 
		//再克隆一個原子彈 
		System.out.println("\n-----模擬克隆產品事件-----"); 
		System.out.println("克隆胖子號原子彈"); 
		factory.clone(p); 
		//遺棄一個產品 
		System.out.println("\n-----模擬銷燬產品事件-----"); 
		System.out.println("遺棄胖子號原子彈"); 
		factory.abandonProduct(p);
	}
}

結果
-----模擬創建產品事件-----
創建一個叫做小男孩的原子彈
平民處理事件:小男孩原子彈誕生記,事件類型=NEW_PRODUCT

-----模擬修改產品事件-----
把小男孩原子彈修改爲胖子號原子彈
貴族處理事件:胖子號原子彈修改,事件類型=EDIT_PRODUCT

-----模擬克隆產品事件-----
克隆胖子號原子彈
貴族處理事件:胖子號原子彈克隆,事件類型=CLONE_PRODUCT

-----模擬銷燬產品事件-----
遺棄胖子號原子彈
乞丐處理事件:胖子號原子彈銷燬,事件類型=DEL_PRODUCT

如果想繼續擴展:

  • 使用責任鏈模式,可以解決一個處理者處理多個事件的問題;
  • 使用模板方法模式,可以實現處理者的啓用、停用等;
  • 使用裝飾模式,可以實現事件的包裝、處理者功能的強化等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章