设计模式:抽象工厂模式
一、前言
上次我们学习了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> "; 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> "); 17 sb.append(caption+" "); 18 sb.append("<ul> "); 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> "); 26 sb.append("</li> "); 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> "); 18 sb.append("<h1>"+title+"</h1>"); 19 20 sb.append("<ul> "); 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> "); 27 sb.append("<hr><address>"+author+"</address>"); 28 sb.append("</body></html> "); 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()非常多次,通过迭代器来展现了这个方法的多态。灵活使用抽象工厂模式可以说是设计模式真正入门的起点。抽象工厂将抽象零件组装成抽象产品,易于增加具体的工厂难于增加新的零件。