常用設計模式(二)
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() {
//業務相關的程式碼
}
}