在面試中經過工廠模式來證實本身的能力

     在面試中,候選人常常會被問到,你在項目裏用到過哪些設計模式?對此,你能夠按本文給出的步驟,系統地經過工廠模式展現本身在設計思想方面的能力。java

    1 經過工廠模式屏蔽建立細節

    工廠模式(Factory Method)是用來向使用者屏蔽建立對象的細節。以前咱們在講SAX解析XML文件時,已經用到過工廠模式,當時咱們是經過以下代碼用SAXParserFacotry這個工廠對象來建立用於解析的parse對象,代碼以下所示。面試

1	SAXParserFactory factory = SAXParserFactory.newInstance();
2	SAXParser parser = factory.newSAXParser();

    做爲使用者,咱們只要能獲得parser對象進行後繼的解析動做,至於parser對象是如何建立的,咱們不須要,也不該管。若是不用工廠模式,那麼咱們還得親自關注如何建立parser對象,好比得考慮建立時傳入的參數,以及是否改用「池」的方式來建立從而提高效率。數據庫

    這樣親力親爲的後果是,會讓使用和建立parser對象的代碼耦合度很高,這樣一旦建立parser的方法發生改變,好比往後須要傳入不一樣的參數,那麼使用parser的代碼也須要對應修改。設計模式

    你們別覺得增長修改量沒什麼大不了,若是咱們在某個模塊裏修改了代碼,哪怕這個修改點再小,也得通過完整的測試才能把這段代碼放入生產環境,這是須要工做量的。若是咱們把「使用」和「建立」對象放在一個模塊裏,那麼「使用」部分的代碼也得測試(雖然沒改),但咱們經過了工廠模式分離了二者,那麼只須要測「建立」模塊,就能夠減小工做量了。架構

    下面咱們先來看下工廠模式的實現代碼,好比咱們要編寫(建立)Java和數據庫方面的兩本書,先在第1行構建一個Book的基類,在第4和第7行建立兩個子類,並且咱們能夠把一些通用性的方法(好比「查資料」)放入Book類裏。    app

1	class Book {  
2	    public book(){  }  
3	}  
4	public class JavaBook extends Book {  
5	   public JavaBook(){System.out.println("Write Java Book");}  
6	}  
7	public class DBBook extends Book{  
8	   public DBBook(){System.out.println("Write DB Book"); }  
9	}

    隨後咱們經過如第10行的接口來定義建立動做,根據需求,咱們能夠在第11和17行實現這個接口,在其中分別實現「編寫Java書」和「編寫數據庫書」的代碼。    ide

10	interface BookFactory { Book createBook(); }  
11	public class JavaFactory implements BookFactory{  
12	      public JavaBook createBook(){  
13	        //省略其它編寫Java書的代碼  
14	        return new JavaBook();  
15	    }    
16	}  
17	public class DBFactory implements BookFactory{  
18	    public DBBook createBook() {  
19	        //省略其它編寫數據庫書的代碼
20	        return new DBBook();
21	    }  
22	}

    在上述代碼裏,咱們提供了「建立」的方法,下面咱們給出了「調用」的代碼,從第2和第4行的代碼中咱們能看到,這裏外部對象能夠經過兩種不一樣的createBook方法分別獲得Java和數據庫書。    測試

1	BookFactory javaFactory = new JavaFactory ();  
2	JavaBook javaBook = javaFactory.createBook();  
3	BookFactory dbFactory = new DBFactory ();  
4	DBBook dbBook = dbFactory.createBook();

2 簡單工廠模式違背了開閉原則

    你們在經過上文,舉例講清楚工廠模式後,能夠當即說出這個結論。具體舉例以下。ui

    在上述的案例中,若是遇到新需求,須要再建立C語言的書,首先能夠在Book父類下再建立一個CBook子類,隨後能夠在BookFactory接口下再建立一個新的工廠來建立,代碼以下。    spa

1	public class CBook extends Book { //構建一個新的類
2	   public CBook(){System.out.println("Write C Book");}  
3	}  
4	public class CFactory implements BookFactory{  
5	    public CBook createBook() {  
6	        //省略其它寫C語言書的代碼
7	        return new CBook();
8	    }  
9	}

    對於這個修改需求,咱們並無修改原有的建立Java和數據庫書籍相關的代碼,而是經過添加新的模塊來實現,這種作法很好地符合了「開閉原則」。

    開閉原則(Open Closed Principle,也叫OCP)和設計模式無關,它是一種設計架構的原則,其核心思想是,系統(或模塊或方法)應當對擴展開放,對修改關閉,好比對於上述案例,遇到擴展了,咱們沒有修改現有代碼,從而能夠避免測試不相干的模塊。

    咱們就用簡單工廠爲例,來看下沒采用開閉原則的後果,好比咱們仍是要建立Java和數據庫方面的書,那麼是在一個方法里根據參數的不一樣來返回不一樣種的類型。    

1	public class BookFactory {
2	   public Book create(String type) {
3	      switch (type) {
4	          case "Java": return new JavaBook(); 
5	          case "DB":return new DBBook();
6	          //要擴展的話,只能加在這裏 
7	          case "C":return new CBook();
8	          default: return null;
9	     }
10	  } 
11	}

    若是要加新類型的書,只能是新加一個case,一旦有修改,那麼咱們得改動第2行的create方法,這樣一來,create方法(乃至BookFactory類)對修改就不關閉了。若是你們對此不理解,能夠回顧下工廠模式的案例,當時遇到這個需求,咱們是經過添加CFactory類來實現的,原來的BookFactory和DBFactory並無改動(它們對修改關閉了)。

    對比一下二者的差異,因爲簡單工廠模式沒遵循開閉原則,那麼一旦添加C語言的書籍,那麼就影響到其它不相干的Java和DB書籍了(這兩部分的case代碼也得隨之測試),這也是爲何簡單工廠模式適用場景比較少的緣由。

    3 抽象工廠和通常工廠模式的區別

    抽象工廠是對通常工廠模式的擴展,好比咱們在寫java和數據庫方面的書籍時,須要添加錄製講解視頻的方法,也就是說,在Java書和數據庫書這兩個產品裏,咱們不只要包含文稿,還得包含視頻。

    具體到生產Java書和數據庫書的這兩個工廠裏,咱們要生產多類產品,不只得包括文稿,還得包括代碼,此時就可使用抽象模式,示例代碼以下。   

1	class Video { //視頻的基類 
2	    public Video(){  }  
3	}  
4	public class JavaVideo extends Video { 省略定義動做  }  
5	public class DBBook extends Video { 省略定義動做 }

    在第1行裏,咱們建立了視頻的基類,在第4和第5行裏,建立了針對Java和數據庫書視頻的兩個類。    

6	abstract class CreateBook{ //抽象工廠 
7	    public abstract Book createBook();//編寫文稿  
8	    public abstract Book createVideo();//錄製視頻  
9	}  
10	//具體建立java書的工廠  
11	class CreateJavaBook extends CreateBook{  
12	    public JavaBook createBook() {省略編寫文稿的具體動做} 
13	    public JavaVideo createVideo() {省略錄製視頻的具體動做}
14	}  
15	//具體建立數據庫書的工廠 
16	class CreateDBBook extends CreateBook{  
17	    public DBBook createBook() {省略編寫文稿的具體動做} 
18	    public DBVideo createVideo() {省略錄製視頻的具體動做}
19	}

    在第6行裏,咱們定義了一個抽象工廠,在其中定義了建立視頻和書籍的兩個方法,在第11和16行,咱們經過繼承這個抽象工廠,實現了生產兩個具體Java和數據庫書籍的工廠。

    和通常工廠相比,抽象工廠的頂層類通常是抽象類(也就是抽象工廠名稱的來源),但和通常工廠模式相比,沒有優劣之分,只看哪一種模式更能適應需求。好比要在同一類產品(好比書)裏生產多個子產品(好比文稿和視頻),那麼就能夠經過抽象工廠模式,而若是須要生產的產品裏只有主部件(好比文稿),而不須要附屬產品(好比視頻),那麼就能夠用通常工廠模式。

    4 再進一步分析建造者模式和工廠模式的區別

    建造者模式和工廠模式都是關注於「建立對象」,在面試時,咱們通常會問它們的差異。經過工廠模式,咱們通常都是建立一個(或一類)產品,而不關心產品的組成部分,建造者模式也是用來建立一個產品,但它不只建立產品,更專一這個產品的組件和組成過程。

    經過下面的代碼,咱們來看下建造者模式的用法,你們能夠對比下建造者和工廠模式的差異。    

1	//定義一個待生產的產品,好比帶視頻講解的書
2	public class BookwithVideo {  
3	    //其中包括了稿件和視頻兩個組件
4	    Book PaperBook;
5	    Video Video;
6	}
7	//定義一個抽象的建造者
8	public abstract class Builder {    
9	    public abstract Book createPaperBook();//編寫稿件
10	    public abstract Video createVideo();//錄製視頻
11	}
12	//定義一個具體的建造者,用來建立Java書
13	public class JavaBookProduct extends Builder {    
14	    private BookwithVideo bookWithVideo = new BookwithVideo();
15	    //經過這個方法返回組裝後的書(稿件加視頻)
16	    public BookWithVideo getBook(){return bookWithVideo;}  
17	    //編寫稿件
18	    public void setPaperBook() {  
19	        //創造Java文稿,並賦予javaBook對象
20	        bookWithVideo.book = javaBook;
21	    }  
22	    //錄製視頻
23	    public void setVideo() {  
24	        錄製Java書的視頻,並賦予javaVideo對象
25	        bookWithVideo.video = javaVideo;
26	    }     
27	}  
28	//定義一個具體的數據庫書籍的建造者
29	public class DBBookProduct extends Builder {    
30	    private BookwithVideo bookWithVideo = new BookwithVideo();
31	    //經過這個方法返回組裝後的書(稿件加視頻)
32	    public BookWithVideo getBook(){return bookWithVideo;} 
33	    //紙質書
34	    public void setPaperBook() {  
35	        寫數據庫書的文稿,並賦予dbBook對象
36	        bookWithVideo.book = dbBook;
37	    }  
38	    //錄製視頻
39	    public void setVideo() {  
40	        錄製數據庫書的視頻,並賦予dbVideo對象
41	        bookWithVideo.video = dbVideo;
42	    }     
43	}

    在第8行裏,咱們定義了一個抽象的創造者類Builder,在第13和29這兩行裏,咱們經過繼承Builder這個創造者類建立了兩個實體創造者,分別用來創造Java和數據庫的書籍。

    在每個創造者裏,咱們經過了setPaperBook方法創造文稿,經過setVideo建立視頻,並把創造好的文稿和視頻分別賦予bookWithVideo對象裏的兩個文稿和視頻組件。

    看到這裏,彷佛和工廠模式差很少,因爲建造者模式會偏重於組件的建立過程,因此會經過以下的總控類來組裝對象,而工廠模式偏重於「建立產品「的這個結果,而不關注產品中組裝各組件的過程,因此通常不會有總控類。    

44	//總控類
45	public class Director {    
46	   void productBook(Builder builder){
47	         builder.setPaperBook();
48	         builder.setVideo();
49	    } 
50	}

    在總控類裏的第46行裏,咱們定義了用來建立書的productBook方法,請注意這個方法是抽象的builder類,經過下面的代碼,咱們能看到如何經過上述定義的總控類和建造者類來動態地建立不一樣種類的對象。    

1	Director director = new Director();
2	Builder javaBookBuild = new JavaBookProduct();
3	Builder dbBookBuilder = new DBBookProduct();
4	director.productBook(javaBookBuild);
5	director.productBook(dbBookBuilder);

    在第1行裏,咱們定義了一個總控類,在第2和第3行裏,咱們定義了具體的建立Java和數據庫書籍的建造者對象,在第4和第5行裏,分別傳入了javaBookBuilder和dbBookBuilder這兩個建造者對象,這樣在總控類的productBook方法裏,會根據傳入參數類型的不一樣,分別建造java和數據庫書籍。

    咱們常常經過建造者模式來建立項目裏的業務對象,因此候選人在他們的項目裏通常都會用到這種模式,在面試中也常常聽到候選人用這種模式來舉例,這裏列一種比較好的回答。

    第一,這位候選人用電商平臺的訂單來舉例,首先他建立了一個訂單的基類,在其中包括了商品列表、總價錢、總積分和發貨地址這四個組件。

    第二,經過繼承這個訂單基類,建立了兩類訂單,分別是「通常用戶的訂單」和「VIP客戶的訂單」,它們的算總價和算總積分的業務邏輯是不一樣的。

    第三,定義了一個抽象的建造者對象,在其中定義了諸如「統計商品」和「算總價」等的方法。

    第四,經過繼承抽象的建造者,定義了兩個具體的建造者,分別用來建造「通常訂單」和「VIP訂單」,在每一個具體的建造者對象裏,建立商品列表、總價錢、總積分和發貨地址,並把它們組裝成一個訂單。

    第五,也是關鍵點,須要建立一個總控類(這也是建造者模式的核心,也是和工廠模式的差異點),在其中提供一個productOrder(Builder builder)方法,它的參數是抽象的建造者。

    至此構造了建造者模式的所有代碼,在須要建立訂單時,則能夠經過productOrder(VIP訂單的建造者對象)的調用方式,經過傳入的具體的建造者對象(不是抽象的建造者對象)來完成建造。

    上述的敘述是給你們作個參考,其實根據實際的項目需求敘述建造者模式並不困難,通常來講,不少面試官會多問句,建造者模式和工廠模式有什麼差異?這在前文裏也說過了,你們能夠經過項目需求詳細說明。

相關文章
相關標籤/搜索