1. 程式人生 > >23種設計模式之五種建立型模式從概念介紹再到案例分析,不懂就從例子去感受

23種設計模式之五種建立型模式從概念介紹再到案例分析,不懂就從例子去感受

### 一、建立型模式(Factory Method) ##### 1.工廠模式 ###### 1.1普通工廠模式 就是建立一個工廠類,對實現了同一介面的一些類進行例項的建立。首先看下關係圖: ![](https://img2020.cnblogs.com/blog/2172340/202010/2172340-20201030130910989-933370781.png) 舉例如下:(我們舉一個傳送郵件和簡訊的例子) 首先建立二者的共同介面 ``` public interface Sender {       public void Send();   }  ``` 其次,建立實現類: ``` public class MailSender implements Sender {       @Override       public void Send() {           System.out.println("this is mailsender!");       }   }  ``` ``` public class SmsSender implements Sender {          @Override       public void Send() {           System.out.println("this is sms sender!");       }   } ``` 最後,建工廠類: ``` public class SendFactory {          public Sender produce(String type) {           if ("mail".equals(type)) {              return new MailSender();           } else if ("sms".equals(type)) {               return new SmsSender();           } else {               System.out.println("請輸入正確的型別!");              return null;           }       }   } ``` 我們來測試下: ``` public class FactoryTest {          public static void main(String[] args) {           SendFactory factory = new SendFactory();           Sender sender = factory.produce("sms");           sender.Send();       }   } ``` ###### 1.2 多工廠方法模式 是對普通工廠方法模式的改進,在普通工廠方法模式中,如果傳遞的字串出錯,則不能正確建立物件,而多個工廠方法模式是提供多個工廠方法,分別建立物件。關係圖: ![](https://img2020.cnblogs.com/blog/2172340/202010/2172340-20201030131923683-460946567.png) 將上面的程式碼做下修改,改動下SendFactory類就行,如下: ``` public class SendFactory {              public Sender produceMail(){           return new MailSender();       }              public Sender produceSms(){           return new SmsSender();       }   }   ``` 測試類如下: ``` public class FactoryTest {          public static void main(String[] args) {           SendFactory factory = new SendFactory();           Sender sender = factory.produceMail();           sender.Send();       }   }   ``` `輸出:this is mailsender!` ###### 1.3 靜態工廠方法模式 將上面的多個工廠方法模式裡的方法置為靜態的,不需要建立例項,直接呼叫即可。 ``` public class SendFactory {              public static Sender produceMail(){           return new MailSender();       }              public static Sender produceSms(){           return new SmsSender();       }   } ``` ``` public class FactoryTest {          public static void main(String[] args) {               Sender sender = SendFactory.produceMail();           sender.Send();       }   } ``` `輸出:this is mailsender!` 總體來說,工廠模式適合:凡是出現了大量的產品需要建立,並且具有共同的介面時,可以通過工廠方法模式進行建立。在以上的三種模式中,第一種如果傳入的字串有誤,不能正確建立物件,第三種相對於第二種,不需要例項化工廠類,所以,大多數情況下,我們會選用第三種——靜態工廠方法模式。 ##### 2. 抽象工廠模式 ​ 工廠方法模式有一個問題就是,類的建立依賴工廠類,也就是說,如果想要拓展程式,必須對工廠類進行修改,這違背了閉包原則,所以,從設計角度考慮,有一定的問題,如何解決?就用到抽象工廠模式,建立多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的程式碼。因為抽象工廠不太好理解,我們先看看圖,然後就和程式碼,就比較容易理解。 ![](https://img2020.cnblogs.com/blog/2172340/202010/2172340-20201030132205911-112561717.png) 請看例子: ``` public interface Sender {       public void Send();   } ``` 兩個實現類: ``` public class MailSender implements Sender {       @Override       public void Send() {           System.out.println("this is mailsender!");       }   }  ``` ``` public class SmsSender implements Sender {          @Override       public void Send() {           System.out.println("this is sms sender!");       }   }  ``` 兩個工廠類: ``` public class SendMailFactory implements Provider {              @Override       public Sender produce(){           return new MailSender();       }   } public class SendSmsFactory implements Provider{          @Override       public Sender produce() {           return new SmsSender();       }   }  ``` 在提供一個介面: ``` public interface Provider {       public Sender produce();   }  public class Test {          public static void main(String[] args) {           Provider provider = new SendMailFactory();           Sender sender = provider.produce();           sender.Send();       }   }  ``` 其實這個模式的好處就是,如果你現在想增加一個功能:發及時資訊,則只需做一個實現類,實現Sender介面,同時做一個工廠類,實現Provider介面,就OK了,無需去改動現成的程式碼。這樣做,拓展性較好! ##### 3. 單例模式 通過私有構造方法,不允許外面去建立物件,實現只有一個例項。 ###### 3.1 餓漢式單例 缺點:一開始就把所有的物件都載入,容易浪費空間。 ``` public class Hungry { //核心是構造,不允許外面去建立物件 private Hungry() { } //建立靜態變數 private final static Hungry HUNGRY = new Hungry(); //返回物件 public static Hungry getInstance() { return HUNGRY; } } ``` ###### 3.2 懶漢式單例 (1)只有在物件為空時才去new物件------單執行緒下沒有問題 ``` public class LazyMan { private LazyMan() { } private static LazyMan lazyMan; //只有在物件為空時才去new物件------單執行緒下沒有問題 public static LazyMan getInstance() { if(lazyMan == null) { lazyMan = new LazyMan(); } return lazyMan; } } ``` (2)通過多執行緒去測試,發現單例被破壞。 ``` private LazyMan() { System.out.println(Thread.currentThread().getName()+"ok"); } //多執行緒的情況下就會出現多個物件 public static void main(String[] args) { for(int i=0;i<10;i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } ``` 去實現雙重檢測鎖模式的懶漢式單例--DCL懶漢式,並且在lazyMan = new LazyMan();位置,由於還不是原子操作,容易造成指令重排,去加上volatile屬性,他是可見的不是原子的,來保證安全。 ``` private volatile static LazyMan lazyMan; public static LazyMan getInstance() { if(lazyMan==null) { synchronized (LazyMan.class) { //鎖住類就會只有一個物件 if(lazyMan == null) { lazyMan = new LazyMan(); //不是一個原子操作 /** * 1. 分配記憶體空間 * 2. 執行構造方法,初始化物件 * 3. 把物件指向這個空間 * 假如又A,B兩個執行緒,A執行了132是沒有問題的,但是當B在執行時就 直接return了,但是此時還沒有完成構造,是不安全的。 * volatile只能保證可見性,不證原子性。 * */ } } } return lazyMan; } ``` (3)通過反射機制去破環構造方法的私有性,從而破壞單例模式---一個反射物件。 ``` public static void main(String[] args) throws Exception{ LazyMan instance = LazyMan.getInstance(); Constructor