設計模式詳解

前言:使用設計模式的目的是爲了代碼重用,避免程序大量修改,同時使代碼更容易理解。在沒學會設計模式以前,我喜歡申明不少的類,實現不少的藉口,不少類之間看似很類似,但總不知道該如何使代碼看起來更加簡潔,本身寫的代碼幾乎只有本身看得懂,沒有可讀性,直到學了設計模式,頓時茅塞頓開,下面就個人一些理解和你們一塊兒分享。本文主要講的是三種常見的設計模式——單例模式、工廠模式和適配器模式。java

一、單例模式設計模式

單例模式的做用在於保證在整個應用程序的生命週期中,任何一個時刻,單例類的實例最多隻有一個。那何時須要用到單例模式呢?舉個簡單的例子,好比實驗室裏的打印機,實驗室10我的都有可能去使用打印機,每一個人的計算機都能鏈接打印機,爲了不兩我的的材料同時輸出到打印機上,此時打印機就可使用單例模式,即一臺打印機只有一個打印程序的實例。通常來講,單例模式都是自行實例化,並向整個系統提供這個實例單例模式。爲了保證系統中只有一個實例,這就須要把單例類的構造函數聲明爲私有,同時提供一個全局訪問點。例如:安全

public class Test{
   //構造函數必須私有
   private Test();
   private static Test uniqueInstance=new Test();
 
   //此爲全局訪問點
   public static Test getInstance(){
       return uniqueInstance;   
   }

}

上例也說明了爲何單例模式是自行實例化,由於在使用前對象就已經建立好了(uniqueInstance),要使用只需調用public的getInstance方法便可。所以,單例模式在多線程環境下是試用的。最典型的例子就是Hibernate下的SessionFactory了,由於SessionFactory須要線程安全,能被多個線程同時訪問,全部爲了方便使用,用單例模式來實現,具體的能夠自行去看Hibernate有關的知識點。多線程

二、工廠模式ide

通常來講,工廠模式專門負責實例化有大量公共接口的類,它能夠動態的決定將哪個類實例化,而不須要事先知道每次要實例化哪個類。函數

1)簡單工廠模式。簡單工廠模式的工廠類是根據提供給他的參數,返回的是可能產品中的一個類的實例。所以,簡單工廠模式又稱靜態工廠方法模式。它存在的目的很簡單:定義一個用於建立對象的接口。它的構成包括:測試

    (1) 工廠類角色:這是本模式的核心,含有必定的商業邏輯和判斷邏輯。在java中它每每由一個具體類實現。

       (2) 抽象產品角色:它通常是具體產品繼承的父類或者實現的接口。在java中由接口或者抽象類來實現。

       (3)具體產品角色:工廠類所建立的對象就是此角色的實例。在java中由一個具體類實現。spa

好比:線程

(百度上找的圖,本身都呵呵了,仔細看不難發現帶三角形箭頭的箭頭表示繼承或實現關係,不帶三角形的箭頭表示建立關係,及工廠與產品的關係)設計

從上圖咱們看到,SimpleFactory既是工廠類角色,它與Machine的關係在於其內部有返回Machine類(即爲抽象產品角色)的方法,此方法根據不一樣的參數返回不一樣的Machine實現類,此圖中返回的就是具體產品角色(Washer、Televisor、Refrigerator)。

public interface Machine {
    //此處代碼省略
}

public class Refrigerator implements Machine {
    //此處代碼省略
}

public class Televisor implements Machine {
    //此處代碼省略
}

public class Washer implements Machine {
    //此處代碼省略
}

public class SimpleFactory {
    public Machine create(int n) {
        if(n==1)return new Refrigerator();
       else if(n==2)return new Televisor();
       else return new Washer();
    }
}

2)工廠方法模式

工廠方法模式是簡單工廠模式的進一步抽象化和推廣,工廠方法模式裏再也不只由一個工廠類決定那一個產品類應當被實例化,這個決定被交給抽象工廠的子類去作。它是類的建立模式,其用意是定義一個用於建立產品對象的工廠的接口,而將實際建立工做推遲到工廠接口的子類中。多態的使用,使得工廠方法模式保持了簡單工廠模式的優勢,而克服了其缺點。其組成以下:

  (1)抽象工廠角色: 這是工廠方法模式的核心,它與應用程序無關。是具體工廠角色必須實現的接口或者必須繼承的父類。在java中它由抽象類或者接口來實現。

      (2)具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程序調用以建立對應的具體產品的對象。

      (3)抽象產品角色:它是具體產品繼承的父類或者是實現的接口。在java中通常有抽象類或者接口來實現。

      (4)具體產品角色:具體工廠角色所建立的對象就是此角色的實例。在java中由具體的類來實現。

先看個圖:

(又是百度的圖片,哈哈,來者不拒,誰讓我懶得畫)


上圖的帶有三角形的箭頭的箭頭表示實現的關係(concreteCreatorA和B都是實現的接口Creator),虛線箭頭表示工廠與產品的關係,即實現產品對象的實例化。從上圖咱們能夠看出來,接口或抽象類Creator即爲抽象工廠角色,concreteCreatorA和B是具體工廠角色,Product是抽象產品角色,concreteProductA和B是具體產品角色。Creator實現了對產品的全部操做方法,而不實現產品對象的實例化,全部產品的實例化(concreteProductA和B)由Creator的子類(concreteCreatorA和B)來完成。聽起來有點抽象,下面來看看代碼。

//抽象產品角色,至關於上述的Product
public interface Moveable {
    void run();
}
//具體產品角色,至關於concreteProductA和B
public class Plane implements Moveable {
    @Override
    public void run() {
        System.out.println("plane....");
    }
}

public class Broom implements Moveable {
    @Override
    public void run() {
        System.out.println("broom.....");
    }
}

//抽象工廠角色,至關於Creator
public abstract class VehicleFactory {
    abstract Moveable create();
}
//具體工廠角色,至關於concreteCreatorA和B
public class PlaneFactory extends VehicleFactory{
    public Moveable create() {
        return new Plane();
    }
}
public class BroomFactory extends VehicleFactory{
    public Moveable create() {
        return new Broom();
    }
}
//測試類
public class Test {
    public static void main(String[] args) {
        VehicleFactory factory = new BroomFactory();
        Moveable m = factory.create();
        m.run();
    }
}

從代碼應該能比較明顯的看出上述四個角色的邏輯關係,這樣也就能理解工廠方法模式了。

簡單工廠和工廠方法模式的比較:

工廠方法模式和簡單工廠模式在定義上的不一樣是很明顯的。工廠方法模式的核心是一個抽象工廠類,而不像簡單工廠模式, 把核心放在一個實類上。工廠方法模式能夠容許不少實的工廠類從抽象工廠類繼承下來, 從而能夠在實際上成爲多個簡單工廠模式的綜合,從而推廣了簡單工廠模式。 
反過來說,簡單工廠模式是由工廠方法模式退化而來。設想若是咱們很是肯定一個系統只須要一個實的工廠類, 那麼就不妨把抽象工廠類合併到實的工廠類中去。而這樣一來,咱們就退化到簡單工廠模式了。

 

3)抽象工廠模式

抽象工廠模式是全部形態的工廠模式中最爲抽象和最具通常性的一種形態。它指的是當有多個抽象角色時,抽象工廠模式能夠向客戶端提供一個接口,使客戶端在沒必要指定產品的具體的狀況下,建立多個產品族中的產品對象。在抽象工廠模式中,抽象產品 (AbstractProduct) 多是一個或多個,從而構成一個或多個產品族(Product Family)。 在只有一個產品族的狀況下,抽象工廠模式實際上退化到工廠方法模式。

按慣例,先看抽象工廠模式的設計類圖。(實線段表示繼承或實現的關係,虛線表示工廠與產品的關係)

AbstractProductA和AbstractProductB各表明一個產品家族,實現這些接口的類表明具體的產品,AbstractFactory爲建立產品的接口,可以建立全部產品家族中的全部類型的產品,它的子類(ConcreteFactory1和2)能夠根據具體狀況建立對應的產品。換句話說,Client須要的只是類型與這些抽象產品角色相同的一些實例,而不是這些抽象產品的實例。能夠把上圖想象成蘋果公司,接口AbstractFactory即爲蘋果總公司(總公司是抽象的,總公司大樓並不生產產品),而ConcreteFactory1和2能夠表示其在亞洲和美洲的生產基地(是具體的,是蘋果總公司在亞洲和美洲的具體實現)。而AbstractProductA表示Iphone系列,是抽象的產品家族,假設此係列目前流水線上有兩款手機產品一是iPhone6s(ProductA1,主要在亞洲基地ConcreteFactory1生產),另外一個是iPhone7(ProductA2,還未在亞洲上市,只能在美洲ConcreteFactory2生產)。AbstractProductA表示Ipad系列,B1在亞洲(ConcreteFactory1)生產,B2在美洲ConcreteFactory2生產。而每一個人(Client)要買蘋果系列都須要向蘋果總公司提交網上申請。此時我想買個iPhone7,那我根本不會關係iPhone7是在哪裏生產的,我只須要一個iPhone7而已(即類型與ProductA2相同的一個實例),蘋果總公司內部是怎麼運做的我不須要知道,但我知道蘋果總公司能給我任何它旗下產品族的一個產品。這就是抽象工廠的概念,解釋的可能不夠嚴謹,下面再看一個例子。

//抽象工廠類
public abstract class AbstractFactory {
    public abstract Vehicle createVehicle();
    public abstract Weapon createWeapon();
    public abstract Food createFood();
}
//具體工廠類,其中Food,Vehicle,Weapon是抽象類,
public class DefaultFactory extends AbstractFactory{
    @Override
    public Food createFood() {
        return new Apple();
    }
    @Override
    public Vehicle createVehicle() {
        return new Car();
    }
    @Override
    public Weapon createWeapon() {
        return new AK47();
    }
}
//測試類
public class Test {
    public static void main(String[] args) {
        AbstractFactory f = new DefaultFactory();
        Vehicle v = f.createVehicle();
        v.run();
        Weapon w = f.createWeapon();
        w.shoot();
        Food a = f.createFood();
        a.printName();
    }
}

這段代碼比較簡單,沒有很複雜的邏輯關係,應該比較好懂。

3.適配器模式

適配器模式也成爲變壓器模式,它是把一個類的接口轉換成客戶端指望的另外一個接口,從而使本來因接口不匹配而沒法一塊兒工做的兩個類可以一塊兒工做,適配類能夠根據所傳遞的參數返還一個合適的實例給客戶端。

模式中的角色:

目標接口(Target):客戶所期待的接口。目標能夠是具體的或抽象的類,也能夠是接口。

須要適配的類(Adaptee):須要適配的類或適配者類。

適配器(Adapter):經過包裝一個須要適配的對象,把原接口轉換成目標接口。

好比,如今系統裏已經實現了點、線和正方形,而如今客戶須要一個圓形,通常的方法是創建一個Circle類來繼承Shape類,而後去實現對應的display、fill等方法。但若是此時發現其餘項目組其餘人已經實現了一個畫圓的類,可是他的方法名卻和本身不同(爲displayhh,fillhh),則不能直接使用這個類(由於正方形等類都是繼承的Shape,display、fill是對shape方法的override,方法名不同不行,必須保證多態性,即繼承的多態性)。此時要是從新寫這個類就顯得很愚蠢(本例中的Circle類簡單,但實際中每每會遇到很複雜的類,真要重寫,真是呵呵了)。在上述例子中目標接口即爲須要的Circle類,須要適配的類就是已經有的畫圓的類。具體看個圖和看個例子。

適配器模式的代碼實現
/// <summary>
    /// 定義客戶端期待的接口
    /// </summary>
    public class Target
    {
        /// <summary>
        /// 使用virtual修飾以便子類能夠重寫
        /// </summary>
        public virtual void Request()
        {
            Console.WriteLine("This is a common request");
        }
    }

    /// <summary>
    /// 定義須要適配的類
    /// </summary>
    public class Adaptee
    {
        public void SpecificRequest()
        {
            Console.WriteLine("This is a special request.");
        }
    }

    /// <summary>
    /// 定義適配器
    /// </summary>
    public class Adapter extends Target
    {
        // 創建一個私有的Adeptee對象
        private Adaptee adaptee = new Adaptee();

        /// <summary>
        /// 經過重寫,表面上調用Request()方法,變成了實際調用SpecificRequest()
        /// </summary>
       @Override
        public void Request()
        {
            adaptee.SpecificRequest();
        }
    }




客戶端代碼

   public class Program
    {
        public static void main(string[] args)
        {
            // 對客戶端來講,調用的就是Target的Request()
            Target target = new Adapter();
            target.Request();

            Console.Read();
        }
    }

Circle的例子和這個也是差很少的,具體就不給了,少年們本身去實現吧。

相關文章
相關標籤/搜索