設計模式:抽象工廠模式
一、前言
上次我們學習了Builder模式,用來組裝復雜的實例,Builder就是我們蓋房子的一塊塊磚頭,鋼筋和水泥,以及簡單的用法,使用監工將這些元素有機的組合在了一起就能夠建造整個建築了,是監工將這些原材料按照一定的次序和特定的處理流程糅合在了一起,這個過程就是組裝了。而現在我們學習抽象工廠模式,將關鍵零件組裝成產品。
在這之前,讓我們對前面的幾種模式做簡單的回顧,首先我們學習了迭代器模式,使用了工廠方法來創造迭代器,並且完成了元素的內部實現和遍歷的分離,因此成為‘器’,也算是一種配合,其次我們學習了適配器,有類適配器和對象適配器,這兩者只是實現的方式不同,本質是一樣的,都是通過在原素材上加入一個適配器使得能夠滿足現在的需要,一般用在版本之間的兼容上使得新版本的內容能夠在舊版本上使用(適配),以及一些復用的時候需要適當的修改(適配)的場合;之后我們學習了模板方法和工廠方法,模板方法的一個特殊實現其實就是工廠方法,模板方法就是通過在父類之中定義職責,然后讓子類實現各自的子任務,最后通過父類來進行調用,提高了代碼的可修改性和可擴展性,工廠方法則是在模板方法的基礎上,通過生產產品的方式將框架和實現分離,遵循了高內聚和低耦合的原則;在之后我們學習了單例模式和原型模式,單例模式是保證全局關於某個類只有一個對象,在某些多線程或者編碼誤用的條件下非常重要,原型模式則是實現了對象的深淺拷貝,使得經過了很長時間才得到的對象能夠保存以及復制和使用,省去了很多不必要的new操作。之后我們學習了Builder模式,通過增加一個監工類來將父類中定義的方法組合起來實現某種功能,實現了類的隔離,便於代碼的復用,是模板模式的升級版。
學了這么多模式,不知道大家對設計模式有沒有什么感悟,可以說就是通過接口、抽象類、繼承、多態等機制遵循高內聚低耦合、封閉原則、里氏代換原則等實現代碼的可復用性,可擴展性,盡管比以前變得復雜了,其實程序越大擴展就越簡單。那么什么又是抽象工廠模式呢?!
抽象工廠模式是一個非常復雜的模式,和工廠方法簡直差別太大了,但是也有相同之處,那就是抽象工廠也是用了工廠方法來創造產品,只不過抽象工廠模式中包含了零件、產品、工廠、抽象等概念,這樣就非常的復雜了,一般還要用到模板方法、迭代器甚至原型模式等,就“抽象”兩個字來說,就是將所有的角色都分成兩部分,一部分是這個角色的的抽象類,另一部分是這個角色的具體類(實現類),工廠就是沿用了工廠模式,因此抽象工廠模式主要分成兩大部分,抽象部分和具體部分,如圖所示。
就上面的抽象部分,最重要的就是抽象的工廠類、抽象的零件類(Link和Tray)、抽象的產品類(Page),這兩個零件有共同之處,因此通過Item類進行抽象便於兩者之間的互通。下面的具體實現部分就是對抽象的實現了,之后我們使用Main類來進行整合,可以發現我們只用對抽象類進行編程,完全不用使用任何的具體類就能實現我們想要的功能,甚至導致編譯器在編譯的時候還需要指出需要編譯的具體類,因為我們使用了Class.forName()方法實現了反射。這樣的結構看似非常的龐大,其實仔細的推敲,反而妙趣橫生,當我們還想創建另一個具體的工廠的時候實在是太簡單不過了,原來抽象工廠的代碼都不用修改,只用按照相應的抽象實現就可以了,之后我們就可以直接使用了,只用在Main中將Class.forName()所指定的類改一下就可以了,非常的方便。凡是有利就有弊,如果我們對抽象工廠中的某些定義不滿意了呢,這個時候如果我們對抽象方法進行一定的調整和更改(增加或刪除),那么所有實現了該抽象工廠的具體工廠的類都需要進行修改,如果有100個具體工廠,無疑是非常可怕的,因此我們應該理智的取舍,廢話少說,讓我們看一下代碼。
二、代碼實現
我們將類分成三個部分,這樣思路更加清晰,第一部分是抽象工廠中的類,第二部分是具體實現的具體工廠類,第三部分是測試使用的Main類。
抽象工廠包:
Item 抽象類:
1 package zyr.dp.abstractfactory.factory; 2 3 public abstract class Item { 4 protected String caption; 5 public Item(String caption){ 6 this.caption=caption; 7 } 8 public abstract String makeHTML(); 9 }
Link 抽象類:(這里注意父子皆抽象)
1 package zyr.dp.abstractfactory.factory; 2 3 public abstract class Link extends Item{ 4 5 public String url; 6 public Link(String caption,String url) { 7 super(caption); 8 this.url=url; 9 } 10 11 }
Tray抽象類:
1 package zyr.dp.abstractfactory.factory; 2 3 import java.util.ArrayList; 4 5 public abstract class Tray extends Item{ 6 7 protected ArrayList items = new ArrayList(); 8 public Tray(String caption) { 9 super(caption); 10 } 11 public void add(Item item){ 12 items.add(item); 13 } 14 15 }
Page 抽象類:
1 package zyr.dp.abstractfactory.factory; 2 3 import java.io.FileWriter; 4 import java.io.IOException; 5 import java.io.Writer; 6 import java.util.ArrayList; 7 8 public abstract class Page { 9 10 protected String title; 11 protected String author; 12 protected ArrayList content=new ArrayList(); 13 14 public Page(String title,String author){ 15 this.author=author; 16 this.title=title; 17 } 18 19 public void add(Item item){ 20 content.add(item); 21 } 22 public void output(){ 23 String filename=title+".html"; 24 try { 25 Writer writer=new FileWriter(filename); 26 writer.write(this.makeHTML()); 27 writer.close(); 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } 31 System.out.println("寫入文件:"+filename+"已成功!"); 32 } 33 public abstract String makeHTML() ; 34 35 }
Factory工廠抽象類:
1 package zyr.dp.abstractfactory.factory; 2 3 public abstract class Factory { 4 5 public static Factory getFactory(String classname){ 6 Factory factory=null; 7 try { 8 factory=(Factory)Class.forName(classname).newInstance(); 9 } catch (InstantiationException e) { 10 e.printStackTrace(); 11 } catch (IllegalAccessException e) { 12 e.printStackTrace(); 13 } catch (ClassNotFoundException e) { 14 e.printStackTrace(); 15 } 16 return factory; 17 } 18 public abstract Link createLink(String caption,String url); 19 public abstract Tray createTray(String caption); 20 public abstract Page createPage(String title,String author); 21 22 23 }
具體實現工廠類:
ListLink類:
1 package zyr.dp.abstractfactory.listfactory; 2 3 import zyr.dp.abstractfactory.factory.Link; 4 5 6 public class ListLink extends Link{ 7 8 public ListLink(String caption, String url) { 9 super(caption, url); 10 } 11 12 public String makeHTML() { 13 return "<li>"+"<a href=\""+url+"\" >"+caption+"</a></li>\n"; 14 } 15 16 }
ListTray類:(迭代器模式)ArrayList本身實現了迭代器。
1 package zyr.dp.abstractfactory.listfactory; 2 3 import java.util.Iterator; 4 5 import zyr.dp.abstractfactory.factory.Item; 6 import zyr.dp.abstractfactory.factory.Tray; 7 8 public class ListTray extends Tray { 9 10 public ListTray(String caption) { 11 super(caption); 12 } 13 14 public String makeHTML() { 15 StringBuffer sb=new StringBuffer(); 16 sb.append("<li>\n"); 17 sb.append(caption+"\n"); 18 sb.append("<ul>\n"); 19 20 Iterator it=items.iterator(); 21 while(it.hasNext()){ 22 Item item=(Item)it.next(); 23 sb.append(item.makeHTML()); 24 } 25 sb.append("</ul>\n"); 26 sb.append("</li>\n"); 27 return sb.toString(); 28 } 29 30 }
ListPage類:(迭代器模式)ArrayList本身實現了迭代器。
1 package zyr.dp.abstractfactory.listfactory; 2 3 import java.util.Iterator; 4 5 import zyr.dp.abstractfactory.factory.Item; 6 import zyr.dp.abstractfactory.factory.Page; 7 8 public class ListPage extends Page { 9 10 public ListPage(String title, String author) { 11 super(title, author); 12 } 13 14 public String makeHTML() { 15 StringBuffer sb=new StringBuffer(); 16 sb.append("<html><head><title>"+title+"</title></head>"); 17 sb.append("<body>\n"); 18 sb.append("<h1>"+title+"</h1>"); 19 20 sb.append("<ul>\n"); 21 Iterator it=content.iterator(); 22 while(it.hasNext()){ 23 Item item=(Item)it.next(); 24 sb.append(item.makeHTML()); 25 } 26 sb.append("</ul>\n"); 27 sb.append("<hr><address>"+author+"</address>"); 28 sb.append("</body></html>\n"); 29 return sb.toString(); 30 } 31 32 }
ListFactory類:(工廠方法模式)
1 package zyr.dp.abstractfactory.listfactory; 2 3 import zyr.dp.abstractfactory.factory.Factory; 4 import zyr.dp.abstractfactory.factory.Link; 5 import zyr.dp.abstractfactory.factory.Page; 6 import zyr.dp.abstractfactory.factory.Tray; 7 8 9 public class ListFactory extends Factory { 10 11 public Link createLink(String caption, String url) { 12 return new ListLink(caption, url); 13 } 14 15 public Tray createTray(String caption) { 16 return new ListTray(caption); 17 } 18 19 public Page createPage(String title, String author) { 20 return new ListPage( title, author) ; 21 } 22 23 }
Main類:
1 package zyr.dp.abstractfactory.test; 2 3 import zyr.dp.abstractfactory.factory.Factory; 4 import zyr.dp.abstractfactory.factory.Link; 5 import zyr.dp.abstractfactory.factory.Page; 6 import zyr.dp.abstractfactory.factory.Tray; 7 8 9 public class Main { 10 11 public static void main(String[] args) { 12 String []choice={"zyr.dp.abstractfactory.listfactory.ListFactory"}; 13 Factory factory=Factory.getFactory(choice[0]); 14 15 Tray tray_life=factory.createTray("我的生活"); 16 Link link_graduate=factory.createLink("我的本科", "http://www.swjtu.edu.cn"); 17 Link link_postgraduate=factory.createLink("我的研究生", "http://www.uestc.edu.cn"); 18 tray_life.add(link_graduate); 19 tray_life.add(link_postgraduate); 20 21 Tray tray_blog=factory.createTray("我的博客"); 22 Link link_iterator=factory.createLink("迭代器", "https://www.cnblogs.com/zyrblog/p/9217673.html"); 23 Link link_adapter=factory.createLink("適配器", "https://www.cnblogs.com/zyrblog/p/9218316.html"); 24 tray_blog.add(link_iterator); 25 tray_blog.add(link_adapter); 26 27 28 Tray tray_blog_all=factory.createTray("博客園"); 29 Link link_other1=factory.createLink("解釋器模式", "https://www.cnblogs.com/Answer-Geng/p/9231042.html"); 30 Link link_other2=factory.createLink("OAuth 2.0", "https://www.cnblogs.com/cjsblog/p/9230990.html"); 31 32 tray_blog_all.add(tray_blog); 33 tray_blog_all.add(link_other1); 34 tray_blog_all.add(link_other2); 35 36 Page page=factory.createPage("zyr", "朱彥榮"); 37 38 page.add(tray_life); 39 page.add(tray_blog_all); 40 41 page.output(); 42 } 43 44 }
運行結果:
打開zyr.html文件:
查看源代碼:
可以看到完全組成了一個網頁。到了這里大家可能很質疑,難道費了這么大的功夫就是為了實現這么簡單的功能?!其實這里我們可以看到抽象工廠的強大之處,零件的組裝與嵌套,相互關聯,通過迭代器、模板方法、工廠方法等模式最終實現了這種功能,可擴展性非常之強大,如果還要生成其他種類的工廠,將非常的方便,直接寫實現類就可以了,其他代碼基本上不需要改動,這樣的功能可以說非常強大了,至今為止我們很多的代碼都是強耦合的,很難實現復用,而這個抽象的工廠就可以實現高層次的復用,只需要知道實現類的類名就可以執行了,我們完全可以實現其他工廠,從而實現其他的功能。抽象工廠模式最重要的就是可復用性和完美的隔離性,其中使用了makeHTML()非常多次,通過迭代器來展現了這個方法的多態。靈活使用抽象工廠模式可以說是設計模式真正入門的起點。抽象工廠將抽象零件組裝成抽象產品,易於增加具體的工廠難於增加新的零件。