1. 程式人生 > >設計模式Design Patterns (一)

設計模式Design Patterns (一)

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編制真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。專案中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。

一、設計模式的分類

總體來說設計模式分為三大類:

建立型模式,共五種:**工廠**方法模式、**抽象工廠**模式、**單例**模式、**建造者**模式、**原型**模式。

結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。

二、設計模式的六大原則

1、**開閉原則**(Open Close Principle)

開閉原則就是說對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。

2、**里氏代換原則**(Liskov Substitution Principle)

里氏代換原則(Liskov Substitution Principle LSP)面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承複用的基石,只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

3、**依賴倒轉原則**(Dependence Inversion Principle)

這個是開閉原則的基礎,具體內容:真對介面程式設計,依賴於抽象而不依賴於具體。

4、**介面隔離原則**(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟體的設計思想,從大型軟體架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。

5、**迪米特法則**(最少知道原則)(Demeter Principle)

為什麼叫最少知道原則,就是說:一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。

6、**合成複用原則**(Composite Reuse Principle)

原則是儘量使用合成/聚合的方式,而不是使用繼承。

建立型模式

1.工廠方法模式(Factory Method)

1.1 普通工廠模式

關係圖

舉例

一個sender介面和兩個實現該介面的類

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 SmsSender"); } }

建立工廠類 定義一個根據不同型別返回不同實現類的方法

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 sendFactory = new SendFactory();
        Sender sender = sendFactory.produce("mail");
        sender.send();
    }
}

在普通工廠方法模式中,如果傳遞的字串出錯,則不能正確建立物件

1.2 多個工廠方法模式
            -----提供多個工廠方法,分別建立物件

這裡寫圖片描述

舉例:

介面和實現類同上

建立的工廠類中定義了兩個不同的方法 分別返回不同的實現類
public class SendFactory {
    public Sender produceSms() {
        return new SmsSender();
    }

    public Sender produceMail() {
        return new MailSender();
    }
}

測試類

public class FactoryTest {
    public static void main(String[] args) {
        SendFactory sendFactory = new SendFactory();
        Sender sender = sendFactory.produceSms();
        sender.send();
    }
}
1.3 **靜態工廠模式**
            ----- 無需建立工廠例項

在上一個模式的基礎上 將工廠類內部的方法定義為靜態static 
public class SendFactory {
    public static Sender produceSms() {
        return new SmsSender();
    }

    public static Sender produceMail() {
        return new MailSender();
    }
}

測試類

public class FactoryTest {
    public static void main(String[] args) {
       Sender sender = SendFactory.produceMail();
       sender.send();;
    }
}

2.抽象工廠模式 Abstract Factory

                        ------ 建立多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的程式碼

這裡寫圖片描述

舉例
定義一個Sender介面和兩個實現類MailSender & SmsSender

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 SmsSender");
    }
}

再定義一個Provider介面和實現它的兩個工廠類MailFactory & SmsFactory

public interface Provider {
    public Sender produce();
}
public class MailFactory implements Provider {
    @Override
    public Sender produce() {
        return new MailSender();
    }
}
public class SmsFactory implements Provider {
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}

測試類

public class FactoryTest {
    public static void main(String[] args) {
       Provider provider = new MailFactory();
       Sender sender = provider.produce();
       sender.send();
    }
}

如果你現在想增加一個功能:發及時資訊,則只需做一個實現類,實現Sender介面,同時做一個工廠類,實現Provider介面,就OK了,無需去改動現成的程式碼。這樣做,拓展性較好!

3.單例模式 Singleton

        ------單例物件能保證在一個JVM中,該物件只有一個例項存在

好處:
1、某些類建立比較頻繁,對於一些大型的物件,這是一筆很大的系統開銷。
2、省去了new操作符,降低了系統記憶體的使用頻率,減輕GC壓力。
3、有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以建立多個的話,系統完全亂了。(比如一個軍隊出現了多個司令員同時指揮,肯定會亂成一團),所以只有使用單例模式,才能保證核心交易伺服器獨立控制整個流程。

舉例

public class Singleton {
    /*持有私有靜態例項 防止被引用
    * 此處賦值null==> 實現延遲載入*/
//    private static Singleton instance = null;

    /*私有構造方法 防止被例項化*/
    private Singleton() {
    }

    private static class SingletonFactory{
        private static Singleton instance = new Singleton();
    }

    /*靜態工程方法  建立例項*/
    public static Singleton getInstance() {
       /* if (instance == null) {
            synchronized (instance){
                instance = new Singleton();
            }
        }*/
        return SingletonFactory.instance;
    }

    /*如果該物件被用於序列化 可以保證序列化前後一致*/
    public Object readResolve() {
        return SingletonFactory.instance;
    }
}

4. 建造者模式 Builder
工廠類是提供建立單個類的模式 而建造者模式則是將各種產品集中起來進行管理 用來建立符合物件
舉例
介面Sender和實現類SmsSender MailSender與前面相同

建立Builder類 
public class Builder {
    List<Sender> senderList = new ArrayList<>();

    public void produceSms(int count) {
        for (int i = 0; i < count; i++) {
            senderList.add(new SmsSender());
        }
    }

    public void produceMail(int count) {
        for (int i = 0; i < count; i++) {
            senderList.add(new MailSender());
        }
    }

    public List<Sender> getSenderList() {
        return senderList;
    }
}

5.原型模式 Prototype

                ------將一個物件作為原型 對其進行復制 克隆 產生一個和原物件類似的新物件

舉例:

public class Prototype implements Cloneable,Serializable {

    private String string;

    private SerializableObject obj;

    /*淺複製*/
    /*將一個物件複製後,基本資料型別的變數都會重新建立,而引用型別,指向的還是原物件所指向的。*/
    public Object clone() throws CloneNotSupportedException {
        Prototype prototype = (Prototype) super.clone();
        return prototype;
    }

    /*深複製*/
    /*將一個物件複製後,不論是基本資料型別還有引用型別,都是重新建立的*/
    public Object deepClone() throws IOException, ClassNotFoundException {
        /*寫入當前物件的二進位制流*/
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        /*獨處二進位制流產生的新物件*/
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }


    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }

    public SerializableObject getObj() {
        return obj;
    }

    public void setObj(SerializableObject obj) {
        this.obj = obj;
    }
}

class SerializableObject implements Serializable{
}

測試類

public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {

        Prototype prototype = new Prototype();
        prototype.setString("heqianqian");
        prototype.setObj(new SerializableObject());

        /*淺複製*/
        Prototype p2 = (Prototype) prototype.clone();
        System.out.println(p2.getString());
        System.out.println(p2.getString()==prototype.getString());
        System.out.println(p2.getObj()==prototype.getObj());

        /*深複製*/
        Prototype p3 = (Prototype) prototype.deepClone();
        System.out.println(p3.getString());
        System.out.println(p3.getString()==prototype.getString());
        System.out.println(p3.getObj()==prototype.getObj());
    }
}