小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

從封裝變化的角度看設(shè)計模式——對象創(chuàng)建

 路人甲Java 2022-06-15 發(fā)布于北京

封裝變化之對象創(chuàng)建

在對象創(chuàng)建的過程中,經(jīng)常會出現(xiàn)的一個問題就是通過顯示地指定一個類來創(chuàng)建對象,從而導(dǎo)致緊耦合。這是因為創(chuàng)建對象時指定類名將使你受特定實現(xiàn)的約束而不是特定接口的約束。這會使未來的變化更加復(fù)雜。要避免這種情況,就應(yīng)該間接地創(chuàng)建對象。

這種緊耦合的問題很大程度是由new關(guān)鍵字帶來的,由于new的緊耦合出現(xiàn),使得緊耦合的類很難獨(dú)立地被復(fù)用,因為它們之間是相互依賴的。并且緊耦合產(chǎn)生單塊的系統(tǒng),要改變或者刪掉一個類,就必須要理解和改變其他許多類。這也是導(dǎo)致系統(tǒng)難以維護(hù)和移植的一個重要原因。

所以可以通過“對象創(chuàng)建”模式繞開new,從而避免在對象創(chuàng)建(new)過程中所導(dǎo)致的緊耦合(依賴具體的類),以此支持對象創(chuàng)建的穩(wěn)定。

那么如何避免new呢?舉個例子!

	public void fun1(){
		//...
		Product p = new Product(); //改為:Product p = productFactory.createProduct();
		//...
	}

這樣的方式就是通過一個工廠調(diào)用一個方法來創(chuàng)建相應(yīng)的產(chǎn)品,但是可能大家又會產(chǎn)生一個問題,這樣操作雖然解決了Product的new操作,但是對于ProductFactory而言不是也需要通過new來產(chǎn)生嗎?

對于這個問題,我想是很多人在接觸到設(shè)計模式的時候都會去思考的問題,既然ProductFactory還是要用到new,那工廠類還有存在的必要嗎?這時,我們可以會想到兩種解決方式,一是將createProdyct()方法寫成靜態(tài)方法,這樣調(diào)用的時候自然不需要new了。二是通過注入的方式,比如在應(yīng)用類當(dāng)中通過setter或是構(gòu)造方法傳入一個工廠類的對象。

對于靜態(tài)方法而言,簡單地說,即使是使用靜態(tài)方法,那Product p = ProductFactory.createProduct()這樣依然是一種緊耦合的方式,因為工廠類無法替換,和直接new出產(chǎn)品區(qū)別不大。

對于注入方式,大家更多的是疑惑,既然可以傳入一個工廠類對象,那為什么不直接傳入相應(yīng)的產(chǎn)品,不是更簡單直接嗎?當(dāng)然不是的,首先需要明白的是,工廠類的作用是作為一個籠子,這個籠子需要幫助我們束縛住 '未來的變化’ ,要知道一個產(chǎn)品的變化可能總是大于工廠的變化。在這種情況下,舉出一個最簡單的例子,你在編碼的過程中,可能會用到不只一個產(chǎn)品,那你就可能需要很多setter或者修改構(gòu)造方法;但是如果這些產(chǎn)品都可以通過這個工廠來獲取,是不是就相當(dāng)于用籠子關(guān)住了變化,使其在一個范圍中跳動。

在學(xué)習(xí)設(shè)計模式時,永遠(yuǎn)要記住的一句話就是“設(shè)計模式是用來教會我們?nèi)绾螒?yīng)對未來可能的變化”。如果你能夠確定自己的系統(tǒng)未來沒有變化,那自然用不到設(shè)計模式;或者你的系統(tǒng)未來全是變化,那也用不到設(shè)計模式,設(shè)計模式做的就是隔離穩(wěn)定與變化,如果沒有穩(wěn)定,那就用不到設(shè)計模式。

'new’是一種硬編碼,究竟 ’硬' 在那里,同樣一個簡單的理由,如果未來構(gòu)造方法發(fā)生變化或者說構(gòu)造參數(shù)增加(減少),而在源碼中有很多地方都是通過new來獲取實例對象,找到并修改源碼將會是一項很大的工作。

在解決這樣的 “對象創(chuàng)建” 問題中就有工廠方法、抽象工廠、原型模式和建造者模式等相關(guān)設(shè)計模式。

工廠方法(Factory Method)

  1. 意圖

    定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類。FactoryMethod使得一個類的實例化延遲到其子類。

  2. 實例

    Factory Method相對于簡單工廠而言,完全遵循了“不改代碼”的原則,但是其使用情形相比抽象工廠使用條件沒有那么高,因此可以說是使用最多的創(chuàng)建型模式之一了。

    考慮這樣一個應(yīng)用,它可以向用戶顯示多種文檔,比如word、pdf、txt等等。在這個框架中,首先,想到的可能就是應(yīng)用簡單工廠模式。

    public interface Document{
        public void open();
        public void close();
        public void save();
       // ......
    }
    public class PdfDocument implements Document{
        @Override
        public void open(){
            //open pdfDocument code
            System.out.println("open pdf!");
        }
    
        @Override
        public void close() {
            System.out.println("close pdf!");
        }
    
        @Override
        public void save() {
            System.out.println("save pdf!");
        }
       // ......
    }
    public class TxtDocument implements Document{
        //Txt實現(xiàn)代碼,同PdfDocument
        ......
    }
    public class DocumentFactory{
        public Document createDocument(String type){
            if(type=="pdf"){
               
                return new PdfDocument();
            }else if(type=="txt"){
                return new TxtDocument();
            }else {
                return null;
            }
        }
    }
    
    //簡單工廠模式在客戶類當(dāng)中的調(diào)用
    public class Client {
        public static void main(String[] args) {
            DocumentFactory factory
                = new DocumentFactory();
            Document pdfDocument 
     			= factory.createDocument("pdf");
            pdfDocument.open();
            pdfDocument.save();
            pdfDocument.close();
    
            Document txtDocument 
      			= factory.createDocument("txt");
            txtDocument.open();
            txtDocument.save();
            txtDocument.close();
        }
    }
    

    這樣簡單工廠模式,在不考慮未來新文檔類型的情況下,確實是一種不錯的實現(xiàn)方法。但是在后續(xù)的擴(kuò)展過程當(dāng)中,如果需要增加新的文檔類,就需要去修改DocumentFactory中的createDocument()方法,增加新的類別,并且客戶還必須知道這些類別才能使用。

    為了應(yīng)對這種情況,就出現(xiàn)了工廠方法。工廠方法就直接將工廠抽象出來,每個產(chǎn)品對應(yīng)一個工廠,消除工廠模式中的條件分支結(jié)構(gòu)(其實還有一種消除條件語句的模式,就是之前“組件協(xié)作”當(dāng)中的策略模式)。

    //Document部分不變
    public interface Document{
        public void open();
        public void close();
        public void save();
        ......
    }
    public class PdfDocument implements Document{
        public void open(){
            //open pdfDocument code
        }
        // close 和 save
        ......
    }
    public class TxtDocument implements Document{
        //Txt實現(xiàn)代碼
        ......
    }
    //并且后續(xù)可以擴(kuò)展新的文檔類
    ......
    
    //修改factory部分如下
    public interface DocumentFactory{
        public Document createDocument();
    }
    public class PdfDocumentFactory 
        		implements DocumentFactory {
        @Override
        public Document createDocument() {
            return new PdfDocument();
        }
    }
    public class TxtDocumentFactory 
        		implements DocumentFactory {
        @Override
        public Document createDocument() {
            return new TxtDocument();
        }
    }
    //如果后續(xù)有新的產(chǎn)品,直接再實現(xiàn)DocumentFactory,得到新的工廠
    ......
    
    //調(diào)用過程可做如下修改:
    public class Client {
        public static void main(String[] args) {
            //利用多態(tài)性質(zhì),直接生成相應(yīng)的factory子類
            //消除了控制耦合
            DocumentFactory factory = new PdfDocumentFactory();
            Document pdfDocument
                    = factory.createDocument();
            pdfDocument.open();
            pdfDocument.save();
            pdfDocument.close();
    
            factory = new TxtDocumentFactory();
            Document txtDocument
                    = factory.createDocument();
            txtDocument.open();
            txtDocument.save();
            txtDocument.close();
        }
    }
    

    有人可能會有疑問,這樣不是還沒完全消除new嗎?首先這里的客戶類已經(jīng)到最高的調(diào)用層次了,這個過程當(dāng)中是必然會有new的出現(xiàn),不然怎樣進(jìn)行程序調(diào)用呢?

    我們所說的消除new的過程是指main與factory之間,產(chǎn)生的一個中間層次(如下面的App)中去消除new。

    //這樣的代碼中,就消除了new的存在
    //具體的注入過程可以由其他的形式完成,比如Spring中的DI
    public class App{
       private DocumentFactory factory;
        public void setFactory(DocumentFactory factory) {
            this.factory = factory;
        }
        public void operateDoc(){
           Document document = factory.createDocument();
           document.open();
           document.save();
           document.close();
       }
    }
    //main中的代碼是最高層次,也是變化最頻繁的層次,這里是不可能消除new的
    public class Client {
        public static void main(String[] args) {
            DocumentFactory factory = new PdfDocumentFactory();
            App app = new App();
            app.setFactory(factory);
            app.operateDoc();
            //同樣對于其他的工廠類也是可以采用同樣的方式調(diào)用。
            ......
        }
    }
    

    這樣修改代碼的好處在那里呢?第一,顯而易見的就是完全實現(xiàn)了“開閉原則”的思想,擴(kuò)展時不再需要去修改源碼。第二,有些對象的創(chuàng)建過程可能比較復(fù)雜,因此如果直接在應(yīng)用程序當(dāng)中使用new或者其他形式創(chuàng)建很麻煩,通過工廠創(chuàng)建之后,就不再需要去關(guān)注那些復(fù)雜的創(chuàng)建過程。第三,通過new創(chuàng)建,始終是一種硬編碼的形式,如果在應(yīng)用程序當(dāng)中過多的使用這種方式,那么一旦某對象的創(chuàng)建方式發(fā)生改變,修改源碼必然是很繁瑣的。

  3. 結(jié)構(gòu)——類創(chuàng)建型模式

    FactoryMethod .png

  4. 參與者

    • Product(Document)

    定義工廠方法中工廠創(chuàng)建的對象的接口。

    • ConcreteProduct(PdfDocument、TxtDocument)

      實現(xiàn)Product的接口。

    • Creator(DocumentFactory)

      聲明工廠方法——createProduct(),可以調(diào)用該方法返回一個Product類型的對象。

    • ConcreteCreator(PdfDocumentFactory、TxtDocumentFactory)

      重定義工廠方法以返回具體的ConcreteProduct。

    • Client(客戶類)

      使用工廠和產(chǎn)品,工廠方法模式中,客戶類也是一個重要的參與者,因為工廠方法主要的作用就是分離開客戶類與產(chǎn)品類之間的耦合關(guān)系,所以脫離客戶類去談工廠方法模式時,總會覺得差了些什么東西,無法完全體會到工廠方法模式的優(yōu)勢。

  5. 適用性

    在下列情況下可以使用Factory Method模式:

    • 當(dāng)一個類不知道它所必須創(chuàng)建的對象的類的時候。
    • 當(dāng)一個類希望由它的子類來指定它所創(chuàng)建的對象的時候。
    • 當(dāng)類將創(chuàng)建對象的職責(zé)委托給多個幫助子類中的某一個,并且你希望將哪一個幫助子類是代理者這一信息局部化的時候。

    簡單地說,就是使用過程中只需要聲明一個抽象工廠類的引用,具體調(diào)用那個工廠去生成那個對象,是由調(diào)用者去確定的。

  6. 相關(guān)模式

    Abstract Factory經(jīng)常用工廠方法來實現(xiàn),抽象工廠創(chuàng)建產(chǎn)品的過程就可以使用工廠方法來完成。

    工廠方法通常在Template Method中被調(diào)用,這一點在“組件協(xié)作”當(dāng)中也提到過。

  7. 思考

    • Creator的兩種實現(xiàn)情況。第一種情況,Creator只作為抽象層,也就是只聲明接口,不做任何的實現(xiàn);這種情況就必須要子類來實現(xiàn)。第二種情況,Creator作為一個具體的實現(xiàn)類,而不是抽象類,這種情況下,Creator可以提供一個缺省的實現(xiàn)接口,這樣即使沒有子類重寫它,客戶可以通過這樣一個缺省的實現(xiàn)完成任務(wù)。
    • 靈活運(yùn)用工廠方法模式。作為一種創(chuàng)建類模式,在任何需要生成復(fù)雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復(fù)雜對象適合使用工廠方法模式,而簡單對象,特別是只需要通過 new 就可以完成創(chuàng)建的對象(這也是為什么工廠方法講解用到的例子總是無法說服人的原因之一),無需使用工廠方法模式;因為使用工廠方法模式,就需要引入一個工廠類,會增加系統(tǒng)的復(fù)雜度。

抽象工廠(Abstract Factory)

  1. 意圖

    提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定他們具體的類。

  2. 實例

    假定存在這樣一個服務(wù)層,該層當(dāng)中需要做的就是訪問數(shù)據(jù)庫中的數(shù)據(jù),并且執(zhí)行一系列的相關(guān)操作。根據(jù)面向接口編程的思想,可以先作這樣一個代碼編寫。

    //對數(shù)據(jù)庫進(jìn)行訪問的三個接口
    //先建立連接,再執(zhí)行相關(guān)操作,最后返回相應(yīng)結(jié)果
    public interface DBConnection{}
    public interface DBCommand{}
    public interface DBDataReader{}
    
    //對于MySql,可以建立以下實現(xiàn)
    public class MySqlDBConnection implements DBConnection{}
    public class MySqlDBCommand implements DBCommand{}
    public class MySqlDBDataReader implements DBDataReader{}
    //同樣對于Sql Server,Oricle也是這樣的實現(xiàn)
    ......
    

    這樣的實現(xiàn)下,我們可以說是滿足了面向接口編程的一個思想;并且在實現(xiàn)中,我們可以為每個接口,按照工廠方法模式,為其創(chuàng)建一個工廠。

    //工廠接口
    public interface DBConnectionFactory{
        public DBConnection createDBConnetion();
    }
    public interface DBCommandFactory{
         public DBCommand createDBCommand();
    }
    public interface DBDataReaderFactory{
        public DBDataReader createDBDataReader();
    }
    //然后對于每個具體的數(shù)據(jù)庫,實現(xiàn)不同的具體工廠
    //以MySql為例
    public class MySqlDBConnetionFactory implements DBConnectionFactory {
        @Override
        public DBConnection createDBConnetion() {
            return new MySqlDBConnection();
        }
    }
    public class MySqlDBCommandFactory implements DBCommandFactory {
        @Override
        public DBDBCommand createDBCommand() {
            return new MySqlDBCommand();
        }
    }
    public class MySqlDataReaderFactory implements DataReaderFactory {
        @Override
        public DBDataReader createDataReader() {
            return new MySqlDataReader();
        }
    }
    //剩下的Orcle,Sql Server也是如此
    ......
    

    工廠模式方法的調(diào)用就不再演示,區(qū)別和工廠方法中的Document例子中差別不大。

    對于這樣的實現(xiàn),雖然我們很好的利用了工廠方法模式,但是也引入了工廠方法模式的一個弊端——大量的對象和類(本例當(dāng)中,三個系列,每個系列三個產(chǎn)品,光產(chǎn)品就是9個子類;每個產(chǎn)品再對應(yīng)一個工廠,一共就是18個子類)。在使用的過程中,反而能夠明顯的感覺到系統(tǒng)復(fù)雜度不減反增。并且,DBConnection、DBCommandDBDataReader明顯是有著一定的關(guān)系的,換句話說,MySql建立的DBConnection是和MySqlDBCommand、MySqlDBDataReader一起使用的,如果出現(xiàn)MySqlDBConnection、OricleDBCommand、SqlServerDBDataReader這種組合肯定是無法正常執(zhí)行的。這時抽象工廠的出現(xiàn),就很好的解決了這樣的問題。

    //首先,具體的產(chǎn)品類不會發(fā)生變化,簡化的主要是工廠層次
    //先抽象出抽象工廠,將產(chǎn)品系列的創(chuàng)建方法合并到一個接口中
    public interface DBFactory{
        public DBConnection createDBConnetion();
        public DBCommand createDBCommand();
        public DBDataReader createDBDataReader();
    }
    //根據(jù)不同的具體工廠,創(chuàng)建具體的對象
    public class MySqlDBFactory implements DBFactory {
        @Override
        public DBConnection createDBConnetion() {
            
            return new MySqlDBConnection();
        }
    
        @Override
        public DBCommand createDBCommand() {
            return new MySqlDBCommand();
        }
    
        @Override
        public DBDataReader createDBDataReader() {
            return new MySqlDBDataReader();
        }
    }
    //Oricle,sql server的工廠,同樣如此
    ......
    

    抽象工廠主要是對工廠層次的簡化,這樣修改下來,對比工廠方法模式,減少了2/3的工廠子類創(chuàng)建,只需要3個工廠(有多少個產(chǎn)品系列就有多少個工廠子類)就可以完成產(chǎn)品的創(chuàng)建。

    這樣的一種創(chuàng)建工廠方式,不僅減少了工廠的數(shù)量,而且使得產(chǎn)品的一致性得以保證,它可以保證,一次只能使用同一個系列當(dāng)中的對象。

    public class Client {
        public static void main(String[] args) {
            DBFactory factory = new MySqlDBFactory();
            App app = new App();
            app.setFactory(factory);
            app.operate();
            //同樣對于其他的工廠類也是可以采用同樣的方式調(diào)用。
            // ......
        }
    }
    class App{
        private DBFactory factory;
        public void setFactory(DBFactory factory) {
            this.factory = factory;
        }
        public void operate(){
            DBConnection connection
                    = factory.createDBConnetion();
            DBCommand command 
                	= factory.createDBCommand();
            DBDataReader reader 
                	= factory.createDBDataReader();
            //執(zhí)行相關(guān)操作
            .....
        }
    }
    

    這樣的應(yīng)用程序代碼,在一定程度上就減少了工廠子類的數(shù)量,并且在operate()中保證了產(chǎn)品系列的一致性,使得MysqlDBFactory生成的產(chǎn)品,只會是與MySql相關(guān)的。

  3. 結(jié)構(gòu)——對象創(chuàng)建型模式

    Abstract Factory.png

  4. 參與者

    • AbstractFactory(DBFactory)

      聲明一個創(chuàng)建抽象產(chǎn)品對象的操作接口。

    • ConcreteFactory(MySqlDBFactory)

      實現(xiàn)創(chuàng)建具體產(chǎn)品對象的操作。

    • AbstractProduct(DBConnection、DBCommand、DBDataReader)

      為一類產(chǎn)品對象聲明一個接口。

    • ConcreteProduct(MySqlDBConection、MySqlDBCommand、MySqlDBDataReader)

      定義一個將被相應(yīng)的具體工廠創(chuàng)建的產(chǎn)品對象,并實現(xiàn)抽象產(chǎn)品的相應(yīng)接口。

    • Client

      調(diào)用抽象工廠和抽象產(chǎn)品提供的接口。在創(chuàng)建者模式當(dāng)中,客戶類也是重要的參與成員,因為對創(chuàng)建模式的理解容易混亂的點正是在客戶類中的調(diào)用過程 (new) 產(chǎn)生的,關(guān)于這個問題,已經(jīng)在前面做過很多解釋了,不再多說。

  5. 適用性

    以下情況可以使用AbstractFactory模式:

    • 一個系統(tǒng)要獨(dú)立于它的產(chǎn)品的創(chuàng)建、組合和表示時。
    • 一個系統(tǒng)要由多個產(chǎn)品系列中的一個來配置時。
    • 當(dāng)你要強(qiáng)調(diào)一系列相關(guān)的產(chǎn)品對象的設(shè)計以便進(jìn)行聯(lián)合使用時。
    • 當(dāng)你提供一個產(chǎn)品類庫,而只想顯示它們的接口而非實現(xiàn)時。
  6. 相關(guān)模式

    Singleton:一個具體的工廠通常會是一個單件。因為在一個應(yīng)用中,一般每個產(chǎn)品系列只需要一個具體工廠。

    Factory Method:在Abstract Factory中,僅僅是聲明一個創(chuàng)建對象的接口,真正的創(chuàng)建過程是由具體工廠實現(xiàn)的。這時,可以為每一個方法對應(yīng)的具體對象之間再定義一個工廠方法。但其問題就在于,這樣的做法就像是在工廠方法上再套上一層抽象工廠,從而又增加了系統(tǒng)的復(fù)雜度。

  7. 思考

    • 難以支持新種類的產(chǎn)品 對于工廠模式而言,它的最大優(yōu)點就在于保證了產(chǎn)品的一致性,但也正是如此,這就使得它的產(chǎn)品系列需要保持穩(wěn)定,如果在后續(xù)的過程中出現(xiàn)新的產(chǎn)品,比如在實例當(dāng)中需要增加一個新的功能系列,就需要去擴(kuò)展DBFactory接口,并且涉及到DBFactory及其子類的改變。
    • 定義可擴(kuò)展的工廠 這是對于新種類產(chǎn)品創(chuàng)建靈活性的提高,但是不太安全;就是給創(chuàng)建 對象的操作增加一個參數(shù),該參數(shù)來指定將被創(chuàng)建的對象種類。使用這種方式,抽象工廠就只需要一個“Create”方法和一個標(biāo)識符參數(shù)即可完成創(chuàng)建操作。但問題就在于,客戶需要完全了解所有的參數(shù)才能更好使用工廠創(chuàng)建自己所需要的對象。

原型模式(Prototype)

  1. 意圖

    用原型實例指定對象的創(chuàng)建種類,并且通過拷貝這些原型創(chuàng)建新的對象。

  2. 實例

    Prototype模式,有點像是對工廠方法模式中產(chǎn)品與工廠的合并。怎么說呢?看下面的代碼:

    //工廠方法模式中的產(chǎn)品類與工廠方法類
    public interface Document{
        public void open();
        public void close();
        public void save();
        ......
    }
    public interface DocumentFactory{
        public Document createDocument();
    }
    

    這是在Factory Method中使用的創(chuàng)建方式,而原型做的事就是,不再用工廠來進(jìn)行創(chuàng)建,而是轉(zhuǎn)而克隆的方式。變成下面這樣:

    //合并Document和DocumentFactory
    public abstract class Document 
        			implements Cloneable{
        public void open();
        public void close();
        public void save();
        ......
        //相當(dāng)于Factory中的createDocument();
       public Object clone() {
          Object clone = null;
          try {
             clone = super.clone();
          } catch (CloneNotSupportedException e) {
             e.printStackTrace();
          }
          return clone;
       }
    }
    public class PdfDocument implements Document{
        @Override
        public void open(){
            //open pdfDocument code
            System.out.println("open pdf!");
        }
        @Override
        public void close() {
            System.out.println("close pdf!");
        }
        @Override
        public void save() {
            System.out.println("save pdf!");
        }
        //......
    }
    //文檔類的實現(xiàn)與工廠方法中的一樣
    ......
    

    那么在具體的客戶類當(dāng)中就是通過這樣一種方式來進(jìn)行調(diào)用:

    public class App{
        //在工廠方法模式當(dāng)中,這里是documentFactory
    	private Document prototype;
        public void setDoucument(Document document){
            prototype = document;
        }
        public void useDocument(){
            //documentFactory.createDocument();
            Document doc = prototype.clone();
            //然后使用prototype克隆出來的doc進(jìn)行操作
            doc.open();
            ......
        }
    }
    //在客戶類中調(diào)用表現(xiàn)
    public class Client {
        public static void main(String[] args) {
            Document doc = new PdfDocument();
            App app = new App();
            app.setFactory(doc);
            app.useDocument();
            //同樣對于其他的工廠類也是可以采用同樣的方式調(diào)用。
            //......
        }
    }
    

    問題來了,為什么不直接用原型(prototype),而是要多一步克???解決這個問題,首先要明白的是,我們使用原型的目的不是將原型作為客戶類的一個屬性去使用,而是一個創(chuàng)建者。既然是一個創(chuàng)建者,那么在使用的過程中,就不只一個地方會用到同樣類型的對象;如果在不同的地方都直接使用原型,可能會在某個地方修改了原型的值,從而使得其他直接使用原型的方法出現(xiàn)不可預(yù)知的錯誤。

  3. 結(jié)構(gòu)——對象創(chuàng)建型模式

    Prototype.png

  4. 參與者

    • Prototype(Document)

      聲明一個克隆自身的接口。

    • ConcretePrototype(PdfDocument...)

      繼承Prototype并實現(xiàn)克隆自身的接口。

    • Client

      讓一個原型克隆自身從而創(chuàng)建一個新的對象。

  5. 適用性

    • 當(dāng)一個系統(tǒng)應(yīng)該獨(dú)立于它的產(chǎn)品創(chuàng)建、構(gòu)成和表示時,可以使用Prototype模式。

    • 當(dāng)要實例化的類是在運(yùn)行時候指定時,比如動態(tài)裝載。

    • 為了避免創(chuàng)建一個產(chǎn)品類平行的工廠類層次時。

    • 當(dāng)一個類的實例只能有幾種不同狀態(tài)組合中的一種時,建立相應(yīng)數(shù)目的原型并克隆他們可以比每次用合適的狀態(tài)手工實例化該類更方便一些。

  6. 相關(guān)模式

    Abstract Factory和Prototype在某種方面是相互競爭的,但是在某種情況下也是可以一起使用,比如,在抽象工廠中存儲一個被克隆的產(chǎn)品集合,在使用時,直接根據(jù)集合中的對象返回相應(yīng)的產(chǎn)品。

    大量使用Composite(組合模式)和Decorator(裝飾器模式)的設(shè)計上也可以采用Protorype來減少Composite或Decorator對象的創(chuàng)建。

  7. 思考

    • 減少了子類的構(gòu)造。實例當(dāng)中就可以看出,原型模式簡化了工廠方法模式中的工廠類,因此,減少了許多子類的創(chuàng)建。
    • 資源優(yōu)化。類初始化可能需要消化非常多的資源,這個資源包括數(shù)據(jù)、硬件資源等等,使用原型模式就可以減少這樣一個初始化過程。
    • 深、淺拷貝的問題。對于java而言,淺拷貝實現(xiàn) Cloneable,深拷貝是通過實現(xiàn) Serializable 讀取二進(jìn)制流。

建造者模式( Builder)

  1. 意圖

    將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以有不同的表示。

  2. 實例

    游戲場景當(dāng)中,尤其是3d場景中,必不可少就是建筑物,比如說房子。對房子的構(gòu)建肯定不是一下全部構(gòu)建完成的,而是會分成幾個部分,比如墻、窗戶、地板、房頂、門,一部分、一部分地去構(gòu)建。

    public abstract class House{
        //房子屬性,紋理、材質(zhì)...
        private Texture texture;
        private Material material;
        ......
        //墻、窗戶、地板、房頂、門
        public Wall buildWall();
        public Window buildWindow();
        public Floor buildFloor();
        public Door buildDoor();
        public Roof buildRoof();
        //房子構(gòu)建過程
        public void buildHouse(){
            buildFloor();
            Wall[] walls = new Wall[4];
            for(int i=0;i<walls.length;i++)
                walls[i] = buildWall();
            Window window = buildWindow();
            wall[1].setWindow(window);
            Door door = builDoor();
            wall[2].setDoor(door);
            buildRoof();
        }
    }
    

    這種構(gòu)建方式,采用的明顯就是模板方法(Template Method),這種實現(xiàn)方式還可以根據(jù)不同的房子類型,實現(xiàn)具體的細(xì)節(jié)。

    //石頭屋
    public class StoneHouse extends House{
        //具體實現(xiàn)細(xì)節(jié)
        .......
    }
    //茅草屋
    public class ThatchedHouse extends House{
        //具體實現(xiàn)細(xì)節(jié)
        .......
    }
    
    //按照模板主方法在客戶類中的調(diào)用形式表現(xiàn):
    public class Client{
        public static void main(String[] args){
            //只需要生成一個相應(yīng)對象即可在游戲場景中完成相應(yīng)類型房子的創(chuàng)建
            House house = new StoneHouse();
            house.buildHouse();	//生成石頭屋
            
            house = new ThatchedHouse();
            house.buildHouse();	//生成茅草屋
            
        }
    }
    

    這種實現(xiàn)有什么問題呢?類太臃腫了,對吧~這樣的實現(xiàn)過程,可以體現(xiàn)復(fù)用的思想,但是問題之一就在于所有的內(nèi)容全部都放在了一個類中,體現(xiàn)不出單一職責(zé)和類的信息與行為集中;這時就可以將創(chuàng)建過程分離出來,形成一個Builder,由這樣一個Builder來專門負(fù)責(zé)創(chuàng)建。

    //Director
    public class House{
        //房子屬性,紋理、材質(zhì)...
        private Texture texture;
        private Material material;
        ......
        //增加builder的引入
        private HouseBuilder houseBuilder;
        public void setHouseBuilder(HouseBuilder hBuilder){
            houseBuilder = hbuilder;
        }
        //房子構(gòu)建過程
        public void buildHouse(){
            houseBuilder.buildFloor();
            Wall[] walls = new Wall[4];
            for(int i=0;i<walls.length;i++)
                walls[i] = houseBuilder.buildWall();
            Window window 
                = houseBuilder.buildWindow();
            wall[1].setWindow(window);
            Door door = houseBuilder.builDoor();
            wall[2].setDoor(door);
            houseBuilder.buildRoof();
        }
    }
    
    //分離出來的builder
    public interface HouseBuilder{
        //墻、窗戶、地板、房頂、門
        public Wall buildWall();
        public Window buildWindow();
        public Floor buildFloor();
        public Door buildDoor();
        public Roof buildRoof();
    }
    public class StoneHouseBuilder
        			implements HouseBuilder{
        //具體實現(xiàn)細(xì)節(jié)
        .......
    }
    public class ThatchedHouseBuilder 
        			implements HouseBuilder{
        //具體實現(xiàn)細(xì)節(jié)
        .......
    }
    
    //修改過后,在客戶類中的調(diào)用形式表現(xiàn):
    public class Client{
        public static void main(String[] args){
            //只需要生成一個相應(yīng)對象即可在游戲場景中完成相應(yīng)類型房子的創(chuàng)建
            House house = new House();
            HouseBuilder builder 
                	= new StoneHouseBuilder();
            house.setHouseBuilder(builder);
            house.buildHouse();
            
            builder = new ThatchedHouseBuilder();
            //這個set過程可以運(yùn)行時完成
            house.setHouseBuilder(builder);
            house.buildHouse();
        }
    }
    

    通過這樣一種方式,實現(xiàn)復(fù)雜對象構(gòu)建與表示的分離,并且,對于不同的房子對象,如果房子其他參數(shù)沒有任何差別,就只需要傳入相應(yīng)的builder即可,而不需要再生成各種各樣的子類(如StoneHouse、ThatchedHouse)。

    一旦生成對象,只需要修改其builder就可以馬上改變其對象表示,而不需要新生成對象。并且這種修改過程,是可以動態(tài)完成的,就如果Spring當(dāng)中的依賴注入過程一樣,可以在運(yùn)行時刻完成,而不一定是一開始就確定的

  3. 結(jié)構(gòu)——對象創(chuàng)建型模式

    Builder.png

  4. 參與者

    • Builder(HouseBuilder)

      為創(chuàng)建一個Product對象的各個部件指定抽象接口。

    • ConcreteBuilder(StoneHouseBuilder、ThatchedHouseBuilder)

      • 實現(xiàn)Builder的接口以構(gòu)造各裝配該產(chǎn)品的各個部件。
      • 定義并明確它所創(chuàng)建的表示。
    • Director(House)

      構(gòu)造一個使用builder的對象。

    • Product(Wall、Window...)

      包含了定義組成部件的類以及被構(gòu)造的復(fù)雜對象等。

  5. 適用性

    Builder的適用情況:

    • 當(dāng)創(chuàng)建復(fù)雜對象的算法應(yīng)該獨(dú)立于該對象的組成部分,以及它們的裝配方式時。
    • 當(dāng)構(gòu)造過程必須允許被構(gòu)造的對象的不同的表示時。

    Builder模式更多的是體現(xiàn)的一種思想,而不是具體的過程,這種思想就是,當(dāng)一個類的信息與行為過于臃腫時,也許可以采用Builder這種方式對類的信息與行為進(jìn)行重新劃分,從而使得類看起來更加的“輕” 。

  6. 相關(guān)模式

    Abstract Factory和Builder都是對復(fù)雜對象的創(chuàng)建封裝,但二者的區(qū)別在于,Builder著重于一步一步構(gòu)建一個復(fù)雜對象,而Abstract Factory著重于多個系列產(chǎn)品對象的創(chuàng)建,并且系列對象之間有著某種聯(lián)系。

    Composite模式中對象通常情況下就是用Builder生成的。

  7. 思考

    • 可以改變產(chǎn)品的內(nèi)部表示 Builder對象提供給Director一個構(gòu)造產(chǎn)品對象的內(nèi)部接口,從而使得產(chǎn)品的表示和內(nèi)部結(jié)構(gòu)得以隱藏。因此,在構(gòu)建過程中,只需要替換不同的Builder就可以得到不同的表示。
    • Builder中的方法可以缺省為空 這樣在具體的實現(xiàn)過程中,客戶可以只實現(xiàn)他們感興趣的操作,這種方式其實在很多模式中都可以應(yīng)用,其最主要的思想就是能用默認(rèn)配置當(dāng)然是最好的。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多