创建型设计模式
知识补充
java反射机制
此处仅作最简单的应用型描述,因为下面会用到,所以在此补充,读者如果想深入了解java反射机制,请自行查资料。
举个简单例子:
Class c = Class.forName("String");
Object obj = c.newInstance();
return obj;
上面的代码能够返回一个String 对象(与new String()是一样的),比较神奇的是,我的String是出现在字符串中的。
所以你可以简单的理解成,java反射机制的一个最简单的应用就是从字符串到一个类的转变,即给你一个"String",你能通过反射机制获取String对象。
索引
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
- 单例模式
- 原型模式
- 建造者模式
1. 简单工厂模式
简述
- 简单工厂模式不是GOF提出的23种模式中的一个,简单工厂仅仅是一个小弟,他大哥叫抽象工厂模式。
- 思路很简单,一张图就能搞明白:
理解
- 上图一共四个类,抽象产品类,具体产品A类,具体产品B类,工厂类,前三个类的作用不用多说了,提取出一个抽象类有利于开闭原则以及代码复用。工厂类的作用:看上图左边的代码,根据传的参数来判断返回哪一个具体产品类。
- 上图仅仅是一个基本原理图,聪明的读者一定发现,这种代码还是不符合开闭原则。
个人体会:
- 其实也是针对接口编程的一种体现,Factory类中的factoryMethod(String arg):Product表示返回类型是Product即抽象类,根据参数判断返回哪个具体类。
- 上述方法的缺点:仅仅将复杂的逻辑移动到了工厂类而已,并不能削减复杂度,而且如果新增产品的话,还是需要修改代码,导致扩展困难。
适用范围: - 适用于创建的对象比较少,而且比较固定,不会产生扩展的情况。
2. 工厂方法模式
简述
上面说到,简单工厂方法对扩展很不友好,需要修改代码,不符合开闭原则,所以引入了工厂方法模式。
理解
上图仅仅是最基本的原理图,读者可能还会有一些疑惑:当需要扩充新产品ConcreteProductB的时候怎么办?答案是:新建一个与之对应的ConcreteFactoryB,那么问题又来了:
每个产品都对应一个工厂,而且工厂里的factoryMethod()方法就执行了一行new语句,那要工厂有啥用呢?
- 事情没有想象中的那么简单,其实引入工厂的初衷是因为类或者对象需要大量初始化代码,我们将默认初始化代码全部放入factoryMethod中,这样对于客户端代码来说就友好了很多,即用工厂方法创建的对象是具有一定默认初始化的。
这种方法能做到开闭原则的对修改关闭,对扩展开放吗?
能,解析如下:当需要新增ConcreteProductB的时候,新建ConcreteProductB和ConcreteFactoryB,这就是对扩展开放,那么客户端在创建产品的时候还是要修改一些语句呀,比如需要将 ConcreteFactory.factoryMethod() 修改成 ConcreteFactoryB.factoryMethod() 那岂不是还不遵循开闭原则,莫急,下面解决这个问题。
如何解决上面的问题
用反射,关于反射看文首的知识补充。解决方案在上两篇文章中提到过,依赖注入,即将类名写在xml文件中,然后读取xml文件,利用反射获取相应对象,下面给一个示例代码:
public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() {
try {
//创建DOM文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));
//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
class Client{
public static void main(String args[]){
LoggerFactory factory;
Logger logger;
factory = (LoggerFactory)XMLUtil.getBean();
logger = factory.createLogger();
logger.writeLog();
}
}
再次引入一个问题:
客户得修改配置文件岂不是很麻烦,其实简介方案很简单,将修改配置文件部分做成图形界面就ok了。
总结
优点:
- 用户无需关心创建产品的细节
- 在系统中加入新产品的时候,无需修改代码,只需去修改配置文件。然后加入具体产品类和其对应的工厂方法。
缺点也很明显:
- 另外增加了类的个数,每次引入新产品都需要加具体产品类和对应的工厂方法。
- 引入抽象层,增加了理解难度,而且还用到了反射,增加了系统实现难度。
适用场景:
- 客户端不知道它所需要的对象的类,只需要知道对应的工厂即可。
- 在程序运行时确定使用哪个子类,然后进行替换,覆盖,使系统更容易扩展。
3. 抽象工厂模式
简述
抽象工厂模式又称为Kit模式,可以理解成是一种打包式的工厂模式,即工厂模式如果子工厂类无限扩充怎么办?那把他们打包一下,再增加一个抽象层,也就是抽象工厂。
理解
如果能够理解工厂方法模式的话,再理解抽象工厂方法模式就不难了,他们本质上是相同的,抽象工厂针对的是一个产品家族,而工厂方法针对的是一个产品。
总结
优点
隔离了具体类的生成,生成了产品族,而且增加新的产品族很方便
缺点
增加新的产品结构很难
适用场景
- 系统中有产品族需要组合。
- 产品族中的产品是有约束的,即这些产品族是一起生效的,比如一个皮肤中的背景+按钮样式他们是一起生效的,属于同一款主题中的不同控件。
- 产品结构稳定,不会频繁更新产品结构。