1. 程式人生 > >常用設計模式(二)

常用設計模式(二)

Strategy策略模式 將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換,由使用者決定使用哪種策略;通過策略類的等級結構來管理演算法族,避免使用將演算法的選擇與演算法本身的實現混合在一起if…else等多重判斷;缺點是使用者需要知道所有的演算法實現,且由於每個演算法都封裝成類,因此產生的物件會很多;策略模式重點是如何組織、呼叫這些演算法,從而讓程式結構更靈活,具有更好的維護性和擴充套件性; 組成: 環境類(Context):持有一個Strategy抽象策略類或策略介面的引用; 抽象策略類(Strategy):一般是一個介面或抽象類,定義所有支援的演算法的公共介面; 具體策略類(ConcreteStrategy):封裝了相關的演算法或行為;

//抽象策略類
public interface UserStrategy {
	public double calPrice(double price);
}
//具體策略類
public class NormalStrategy implements UserStrategy{
	@Override
	public double calPrice(double price) {
		return price;
	}
}
public class VIPStrategy implements UserStrategy{
	@Override
	public double calPrice(double price) {
		return price*0.9;
	}
}
//環境類
public class Price {
	//持有一個具體的策略物件
	private UserStrategy strategy;
	public Price(UserStrategy strategy) {
		this.strategy = strategy;
	}
	//策略介面的引用
	public double settlement(double price) {
		return this.strategy.calPrice(price);
	}
}
//測試
public class Test {
	public static void main(String[] args) {
		//選擇並建立需要使用的策略物件
		UserStrategy strategy=new VIPStrategy();
		//建立環境
		Price price = new Price(strategy);
		System.out.println(price.settlement(100));
	}
}

Prototype原型模式 將一個物件作為原型,對其進行復制、克隆,產生一個和原物件類似的新物件;原型模式要求物件實現一個可以“克隆”自身的介面,這樣就可以通過複製一個例項物件本身來建立一個新的例項。可以是淺拷貝也可以是深拷貝; 我們先討論一下Java中的克隆: Object類提供protected Object clone()方法對物件進行復制,子類當然也可以把這個方法置換掉,提供滿足自己需要的複製方法;Cloneable介面的作用是在執行時期通知Java虛擬機器可以安全地在這個類上使用clone()方法; 克隆條件: (1)對任何的物件x,都有:x.clone()!=x;換言之,克隆物件與原物件不是同一個物件; (2)對任何的物件x,都有:x.clone().getClass() == x.getClass(),換言之,克隆物件與原物件的型別一樣; (3)如果物件x的equals()方法定義其恰當的話,那麼x.clone().equals(x)應當成立的; 淺克隆(淺拷貝):只負責克隆按值傳遞的資料(比如基本資料型別、String型別),而不復制它所引用的物件; 深克隆(深拷貝):克隆按值傳遞的資料和所引用的物件;即把要複製的物件所引用的物件都複製了一遍;這裡會涉及到一個克隆深度的問題,這個取決於引用的物件是深克隆還是淺克隆,所以這裡可能會有一個迴圈克隆的問題;

接下來再具體說下原型模式: (1)客戶(Client)角色:客戶類提出建立物件的請求。 (2)抽象原型(Prototype)角色:這是一個抽象角色,通常由一個Java介面或Java抽象類實現。此角色給出所有的具體原型類所需的介面。 (3)具體原型(Concrete Prototype)角色:被複制的物件。此角色需要實現抽象的原型角色所要求的介面。 原型模式有兩種表現形式:(1)簡單形式(2)登記形式,這兩種表現形式僅僅是原型模式的不同實現;登記形式 簡單形式:

//抽象原型類
public class People implements Cloneable{
    private String name;
    private Date date;
    public People(String name, Date date) {
	this.name = name;
	this.date = date;
    }
   //重寫clone方法,關鍵在於這裡,深拷貝淺拷貝自己選擇
    @Override
    protected Object clone(){
    	People p=null;
        try {
		p=(People)super.clone();
	} catch (CloneNotSupportedException e) {
		e.printStackTrace();
	}
        return p;
    }
    //getter、setter省略
}
//具體原型類
public class Student extends People{
	public Student(String name, Date date) {
		super(name, date);
	}
	public void print(){
		System.out.println(this.getName()+"---"+this.getDate());
	}
}
//客戶端
public class Test{
	public static void main(String[] args) throws CloneNotSupportedException {
		Student s=new Student("張三",new Date());
		s.print();
		Student s2=(Student)s.clone();
		s2.print();
	}
}

列印結果: 張三—Fri Sep 21 11:20:27 CST 2018 張三—Fri Sep 21 11:20:27 CST 2018

登記形式:多了一個原型管理器(PrototypeManager)角色,該角色的作用是:建立具體原型類的物件,並記錄每一個被建立的物件;

//原型管理器
public class PrototypeManager {
	//用來記錄原型的編號和原型例項的對應關係
    private static Map<String,People> map = new HashMap<String,People>();
    //私有化構造方法,避免外部建立例項
    private PrototypeManager(){}
    /**
     * 向原型管理器裡面新增或是修改某個原型註冊
     * @param prototypeId 原型編號
     * @param prototype 原型例項
     */
    public synchronized static void setPrototype(String prototypeId , People p){
        map.put(prototypeId, p);
    }
    /**
     * 從原型管理器裡面刪除某個原型註冊
     * @param prototypeId 原型編號
     */
    public synchronized static void removePrototype(String prototypeId){
        map.remove(prototypeId);
    }
    /**
     * 獲取某個原型編號對應的原型例項
     * @param prototypeId    原型編號
     * @return    原型編號對應的原型例項
     * @throws Exception    如果原型編號對應的例項不存在,則丟擲異常
     */
    public synchronized static People getPrototype(String prototypeId){
    	People prototype = map.get(prototypeId);
        if(prototype == null){
            System.out.println("您希望獲取的原型還沒有註冊或已被銷燬");
        }
        return prototype;
    }
}
//客戶端
public class Test{
	public static void main(String[] args) throws CloneNotSupportedException {
		Student s1=new Student("張三",new Date());
		PrototypeManager.setPrototype("s1", s1);
		Student s2=(Student) PrototypeManager.getPrototype("s1").clone();
		s1.print();
		s2.print();
	}
}

對比:如果需要建立的原型物件數目較少而且比較固定的話,建議採用簡單形式,物件由客戶端儲存;反之,建議採用登記形式,物件由管理器儲存,並且增加判斷管理;

Template模板模式 準備一個抽象類,將部分邏輯以具體方法以及具體建構函式的形式實現,然後宣告一些抽象方法來迫使子類實現剩餘的邏輯;不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現;子類可以置換掉父類的可變部分,但是子類卻不可以改變模板方法所代表的頂級邏輯; 組成: 抽象模板(Abstract Template):定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟;定義並實現了一個模板方法,這個模板方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能呼叫一些具體方法; 具體模板(Concrete Template):實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟;每一個抽象模板角色都可以有任意多個具體模板角色與之對應,而每一個具體模板角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同;

//抽象模板
public abstract class AbstractTemplate {
    //模板方法,可定義多個
    public void templateMethod(){
        //呼叫基本方法
        abstractMethod();
        hookMethod();
        concreteMethod();
    }
    //基本方法的宣告(由子類實現)
    protected abstract void abstractMethod();
    //基本方法(空方法)
    protected void hookMethod(){}
    //基本方法(已經實現)
    private final void concreteMethod(){
        //業務相關的程式碼
    }
}
//具體模板
public class ConcreteTemplate extends AbstractTemplate{
    //基本方法的實現
    @Override
    public void abstractMethod() {
        //業務相關的程式碼
    }
    //重寫父類的方法
    @Override
    public void hookMethod() {
        //業務相關的程式碼
    }
}