Java常用設(shè)計模式

前言:

在Java中,傳說有23中模式,總共分為三大類,分別是:

  • 創(chuàng)建型模式(5種):工廠方法模式、抽象工廠模式、建造者模式、單例模式、原型模式;
  • 結(jié)構(gòu)型模式(7種):適配器模式裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式;
  • 行為型模式(11種):策略模式、模板方法模式觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。

下面就一起來學(xué)習(xí)一下上面字體加粗了的那些設(shè)計模式。


歡迎大家關(guān)注我的公眾號 javawebkf,目前正在慢慢地將簡書文章搬到公眾號,以后簡書和公眾號文章將同步更新,且簡書上的付費文章在公眾號上將免費。


一、工廠方法模式:

工廠是干嘛的,就是用來生產(chǎn)的嘛,這里說的工廠也是用來生產(chǎn)的,它是用來生產(chǎn)對象的。也就是說,有些對象我們可以在工廠里面生產(chǎn),需要用時直接從工廠里面拿出來即可,而不用每次需要用的時候都去new對象。工廠方法模式又分為以下三種:

  • 普通工廠模式:就是建立一個工廠類,對實現(xiàn)了同一接口的一些類進(jìn)行實例的創(chuàng)建。
  • 多個工廠方法模式:是對普通工廠方法模式的改進(jìn),在普通工廠方法模式中,如果傳遞的字符串出錯,則不能正確創(chuàng)建對象,而多個工廠方法模式是提供多個工廠方法,分別創(chuàng)建對象。
  • 靜態(tài)工廠方法模式:將上面的多個工廠方法模式里的方法置為靜態(tài)的,不需要創(chuàng)建實例,直接調(diào)用即可。

上面三個模式中,后一個都是對前一個的改良。下面分別看看這三個模式的具體案例。

情景:有一個發(fā)送消息的接口,有兩個實現(xiàn)類,一個是發(fā)送短信,一個是發(fā)送郵件。代碼如下:

// 接口
public interface Sender {
  public void Send();
} 
//實現(xiàn)一
public class MailSender implements Sender { 
  @Override
  public void Send() {
     System.out.println("this is mail sender!");
  } 
} 
//實現(xiàn)二
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;
       } 
   } 
} 

使用的時候就創(chuàng)建這個工廠的對象,調(diào)用produce方法,需要發(fā)郵件就傳入"mail",需要發(fā)短信就傳入"sms",如果傳入的是別的內(nèi)容,就不會創(chuàng)建任何對象。

  • 多個工廠方法模式:
public class SendFactory { 
   public Sender produceMail(){
      return new MailSender();
   }
   public Sender produceSms(){
      return new SmsSender();
   } 
}

普通工廠模式生產(chǎn)對象都是在一個方法內(nèi)完成,多個工廠方法模式是提供多個方法,分別生產(chǎn)對應(yīng)的對象。使用時先創(chuàng)建工廠的實例,要發(fā)短信就調(diào)用生產(chǎn)短信實例的方法,要發(fā)郵件就調(diào)用生產(chǎn)郵件實例的方法。

  • 靜態(tài)工廠方法模式:
public class SendFactory {
  public static Sender produceMail(){   
       return new MailSender(); 
  } 
  public static Sender produceSms(){  
       return new SmsSender();
  } 
} 

就是多個工廠方法模式里面的方法設(shè)為靜態(tài)的,這樣可以直接通過工廠類的類名調(diào)用,更加方便。

二、抽象工廠模式:

上面的工廠方法模式有一個缺點,就是類的創(chuàng)建依賴工廠類,比如現(xiàn)在還可以用微信發(fā)消息,那么就得在工廠類中新增一個創(chuàng)建WeChat實體的方法。這樣就違背了開閉原則。如何解決?就用到抽象工廠模式,創(chuàng)建多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,就不需要修改之前的工廠類代碼。 上面的案例代碼就可以修改成下面這樣:

// 工廠類接口
public interface Provider {
    public Sender produce();
}
//生產(chǎn)SmsSender對象的工廠
public class SendSmsFactory implements Provider {
  @Override
   public Sender produce() {
       return new SmsSender();
   }
}
//生產(chǎn)MailSender對象的工廠
public class SendMailFactory implements Provider {
   @Override
   public Sender produce() {
      return new MailSender();
   }
}
// 測試
public class Test {
    public static void main(String[] args) {
       Provider provider = new SendMailFactory();
       Sender sender = provider.produce(); 
       sender.send();
    }
} 

這樣即使要新增生產(chǎn)WechatSender實例的方法,也不需要修改現(xiàn)有代碼,只需實現(xiàn)工廠接口,重寫生產(chǎn)方法即可。從工廠方法模式到抽象工廠模式,后者更加體現(xiàn)了Java的封裝、抽象等思想。

三、建造者模式:

工廠模式提供的是創(chuàng)建單個類實例的模式,而建造者模式可以理解為是批量生產(chǎn)。還是使用工廠方法模式中的情景,看看用建造者模式怎么實現(xiàn):

// 建造類
public class Builder { 
   private List<Sender> list = new ArrayList<Sender>();
   public void produceMailSender(int count) { // 生產(chǎn)count個MailSender
      for (int i = 0; i < count; i++) {
           list.add(new MailSender());
      }
   }
   public void produceSmsSender(int count) { // 生產(chǎn)count個SmsSender
      for (int i = 0; i < count; i++) {
           list.add(new SmsSender());
      }
   }
} 
/* =========================== 使用 ============================*/
public class TestBuilder {
   public static void main(String[] args) {
      Builder builder = new Builder();
      builder.produceMailSender(10); // 生產(chǎn)10個MailSender
   }
}


四、單例模式:

什么叫單例,就是保證一個類在內(nèi)存中只有一個對象。Runtime()方法就是單例設(shè)計模式進(jìn)行設(shè)計的。如何保證內(nèi)存中只有一個對象呢?
設(shè)計思路:

  • 不讓其他程序創(chuàng)建該類對象。
  • 在本類中創(chuàng)建一個本類對象。
  • 對外提供方法,讓其他程序獲取這個對象。

實現(xiàn)步驟:

  • 私有化構(gòu)造函數(shù);
  • 創(chuàng)建私有并靜態(tài)的本類對象;
  • 定義公有并靜態(tài)的方法,返回該對象。

代碼實現(xiàn):

  • 懶漢式:延遲加載
class Single{
    private Single(){} // 將構(gòu)造方法私有化
    private static Single s = null; // 私有靜態(tài)的本類對象
    public static synchronized Single getInstance(){ // 靜態(tài)公共的返回對象的方法
        if(s==null)
            s = new Single();
        return s;
    }
}
  • 餓漢式:
class Single{
    private Single(){} //私有化構(gòu)造函數(shù)。
    private static Single s = new Single(); //創(chuàng)建私有并靜態(tài)的本類對象。
    public static Single getInstance(){ //定義公有并靜態(tài)的方法,返回該對象。
        return s;
    }
}


五、適配器模式:

適配器模式將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口表示,目的是消除由于接口不匹配所造成的類的兼容性問題。主要分為以下三類:

  • 類的適配器模式:比如一個類有一個方法method1,但是客戶端使用的時候還需要一個method2方法,那就可以將method1和method2方法寫進(jìn)接口中,然后新建一個適配器類繼承原來的類并實現(xiàn)這個接口。
  • 對象的適配器模式:與類適配器相比,不需再繼承source類,而是將source的對象傳過去。
  • 接口的適配器模式

看一下具體用代碼怎么體現(xiàn):

  • 類的適配器模式:
public class Source {
   public void method1() {
     System.out.println("這是method1方法"); 
   } 
}
// 新建接口
public interface Targetable { 
   /* 與原類中的方法相同 */ 
   public void method1();  
   /* 新類的方法 */
   public void method2(); 
} 
// 適配類
public class Adapter extends Source implements Targetable {
   @Override
   public void method2() { 
      System.out.println("這是method2方法"); 
   }
} 
/* ===================== 使用 ======================*/
public class AdapterTest {
   public static void main(String[] args) {
      Targetable target = new Adapter(); 
      target.method1(); 
      target.method2(); 
   }
}  


  • 對象的適配器模式:
public class Source {
   public void method1() {
     System.out.println("這是method1方法"); 
   } 
}
public interface Targetable {
   /* 與原類中的方法相同 */
   public void method1();
   /* 新類的方法 */
   public void method2();
}
// 適配類
public class Wrapper implements Targetable { 
   private Source source;
   public Wrapper(Source source) { 
      super(); 
      this.source = source;
   } 
   @Override
   public void method2() {
      System.out.println("this is the targetable method!");
   }
   @Override
   public void method1() {
       source.method1();
   } 
}
/* ======================= 使用 =========================*/
public class AdapterTest {
   public static void main(String[] args) {
      Source source = new Source();
      Targetable target = new Wrapper(source);
      target.method1();
      target.method2();
   }
}    

與類的適配器模式不同的是,對象的適配器模式不再繼承source類,而是直接將source對象傳到Wrapper類就可以了。

  • 接口的適配器模式

接口的適配器是這樣的:有時我們寫的一個接口中有多個抽象方法,當(dāng)我們寫該接口的實現(xiàn)類時,必須實現(xiàn)該接口的所有方法,這明顯有時比較浪費,因為并不是所有的方法都是我們需要的,有時只需要某一些,此處為了解決這個問題,我們引入了接口的適配器模式,借助于一個抽象類,該抽象類實現(xiàn)了該接口,實現(xiàn)了所有的方法,而我們不和原始的接口打交道,只和該抽象類取得聯(lián)系,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行。

六、裝飾器模式:

裝飾模式就是給一個對象動態(tài)的增加一些新的功能。要求裝飾對象和被裝飾對象實現(xiàn)同一個接口,裝飾對象持有被裝飾對象的實例。 這樣說得也很抽象,看看具體的案例:

// 接口
public interface Sourceable {
    public void method();
}
// 被裝飾的類,實現(xiàn)Sourceable接口
public class Source implements Sourceable {
    @Override
    public void method() {
        System.out.println("the original method!");
    }
}
// 裝飾的類,也要實現(xiàn)Sourceable 接口
public class Decorator implements Sourceable {
    private Sourceable source; // 持有被裝飾類的對象
    public Decorator(Sourceable source) {
         super();
         this.source = source;
    }
    @Override
    public void method() {
         System.out.println("before decorator!"); // 裝飾
         source.method(); 
         System.out.println("after decorator!"); // 裝飾
    }
} 
// 測試
public class DecoratorTest {
    public static void main(String[] args) {
         Sourceable source = new Source();
         Sourceable obj = new Decorator(source);
         obj.method();
    } 
} 

IO流體系中很多類就用到了這種設(shè)計模式。

七、策略模式:

策略模式是對算法的包裝,是把使用算法的責(zé)任和算法本身分割開來,委派給不同的對象管理。策略模式通常把一個系列的算法包裝到一系列的策略類里面,作為一個抽象策略類的子類??纯淳唧w的代碼:

// 策略接口
public interface Strategy {
    public void strategyInterface();
}
// 具體策略類A
public class ConcreteStrategyA implements Strategy {
    @Override
    public void strategyInterface() {
        // 相關(guān)的業(yè)務(wù)
    }
}
// 具體策略類B
public class ConcreteStrategyB implements Strategy {
    @Override
    public void strategyInterface() {
        //相關(guān)的業(yè)務(wù)
    }
}
// 使用策略的類
public class Context {
    public Context(Strategy strategy){ // 構(gòu)造函數(shù),傳入一個具體策略對象
        this.strategy = strategy;
    }
    public void contextInterface(){ // 策略方法
        strategy.strategyInterface();
    }
}

在創(chuàng)建Context類對象的時候,需要使用哪個策略就傳入該策略,然后就可以使用。


八、模板方法模式:

定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。簡單的說就是很多相同的步驟,只是在某一些地方有差別,那么就可以使用這種模式。看例子:

  • 獲取一段程序運行時間的模板:
public abstract class GetTime{
      public long getTime(){
         long  start = System.currentTimeMillis;
         //表示要計算運行時間的代碼
         code();
         long  end = System.currentTimeMillis;
         return end-start;
      }
      public abstract void code(); 
}
  • 使用該模板:
public class forDemo extends GetTime{
     //重寫抽象方法
     public void code(){
          for(int x=0;x<1000;x++){
                 System.out.println(x);
          }
     }
}
  • 測試:
public class test{
     GetTime gt=new forDemo();
     gt.getTime();
}

這樣就可以計算那個for循環(huán)運行的時間了。


九、觀察者模式:

在對象之間定義了一對多的依賴,這樣一來,當(dāng)一個對象改變狀態(tài),依賴它的對象會收到通知并自動更新,這就是觀察者模式。
情景:有一個微信公眾號服務(wù),不定時發(fā)布一些消息,關(guān)注公眾號就可以收到推送消息,取消關(guān)注就收不到推送消息。
示例代碼:

  • 定義一個被觀察者接口:
public interface BeObserverd {
    public void registerObserver(Observer o);// 添加觀察者
    public void removeObserver(Observer o);// 刪除觀察者
    public void notifyObserver();// 通知觀察者
}
  • 定義一個觀察者接口:
public interface Observer {
    public void update(String message);// 當(dāng)被觀察者發(fā)出通知時,這個方法就會執(zhí)行
}
  • 定義被觀察者,實現(xiàn)了BeObserverd接口,對BeObserverd接口的三個方法進(jìn)行了具體實現(xiàn),同時有一個List集合,用以保存注冊的觀察者,等需要通知觀察者時,遍歷該集合即可。
// 被觀察者,也就是微信公眾號服務(wù)實現(xiàn)了BeObserverd接口,對BeObserverd接口的三個方法進(jìn)行了具體實現(xiàn)
public class WechatServer implements BeObserverd {
    private List<Observer> list;
    private String message;
    public WechatServer() {
        list = new ArrayList<Observer>(); // 觀察者的集合
    }
    // 新增觀察者
    @Override
    public void registerObserver(Observer o) {
        list.add(o);
    }
    // 刪除觀察者
    @Override
    public void removeObserver(Observer o) {
        if(!list.isEmpty())
            list.remove(o);
    }
    // 給觀察者發(fā)通知
    @Override
    public void notifyObserver() {
        for(int i = 0; i < list.size(); i++) {
            Observer observer = list.get(i);
            observer.update(message);
        }
    }
    // 模擬公眾號推送消息的方法
    public void setInfomation(String s) {
        this.message = s;
        System.out.println("微信服務(wù)更新消息: " + s);
        //消息更新,通知所有觀察者
        notifyObserver();
    }
}
  • 定義具體觀察者,微信公眾號的具體觀察者為用戶User。
public class User implements Observer {
    private String name;
    private String message;
    public User(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        this.message = message;
        read();
    }
        public void read() {
        System.out.println(name + " 收到推送消息: " + message);
    }
}

假如現(xiàn)有三個人關(guān)注公眾號,當(dāng)微信公眾號調(diào)用setInfomation方法發(fā)送推文時,通過調(diào)用notifyObserver方法,通知每個觀察者,在notifyObserver方法里調(diào)用update方法,update里面調(diào)用read方法,三個人都能收到推送;加入現(xiàn)在有人取消關(guān)注了,那么就會調(diào)用removeObserver方法,下次推送這個人就收不到了。(本案例參考羅漢果的博文,感謝大神整理!)

總結(jié):

本文聊了九個比較常用的設(shè)計模式,有些模式看文字可能覺得比較抽象難懂,通過案例代碼結(jié)合起來理解會更容易些。剩下的十四個設(shè)計模式,下次再聊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 設(shè)計模式概述 在學(xué)習(xí)面向?qū)ο笃叽笤O(shè)計原則時需要注意以下幾點:a) 高內(nèi)聚、低耦合和單一職能的“沖突”實際上,這兩者...
    彥幀閱讀 3,888評論 0 14
  • 設(shè)計模式匯總 一、基礎(chǔ)知識 1. 設(shè)計模式概述 定義:設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,093評論 1 15
  • 文章部分內(nèi)容轉(zhuǎn)載自:http://blog.csdn.net/zhangerqing 一、設(shè)計模式的分類 總體來說...
    j_cong閱讀 2,142評論 0 20
  • 秋天里葉未落 淅淅瀝瀝賞秋雨 春雨秋雨潤萬物 昨日今日到明日 傻傻懵懂少年郎 天若有情天亦老 不寫憂傷是秋婷 朧朧...
    小品味閱讀 403評論 0 2
  • 人的一生中總會有重要的人,薩哈拉也是一樣,他的背后一直都有一個人在默默支持著他,幫助著他,他就是薩哈拉的新老師——...
    孤漠寒霜閱讀 469評論 0 0

友情鏈接更多精彩內(nèi)容