设计模式-简单工厂
- 简单工厂:一个工厂类,通过静态方法,可以创建产品线的每一个产品
- 什么是工厂:万物皆对象,万物皆产品;工厂生成(new)产品;工程生成基类产品;
- 用户不关心产品生产过程,产品详细的生产过程放在工厂方法里
- 核心的工厂类负责所有的产品的创建
- 为什么使用工厂方法:在用户类代码中,使用new关键字实例化产品类时,就会导致用户类与产品类强耦合。
- 使用建议
- 工厂方法静态化: 简单工厂类中创建实例的方法,应为静态方法。
- 实例创建配置文件化:实例创建应尽量通过配置文件及反射机制,动态创建,达到能根据某个值,自动判断并创建对应类的实例的目的,这样就可以将庞大的swith语句块消除,同时,实例化部分的修改,只需要修改配置文件即可。
- 简单工厂模块化:一个简单工厂可以定义多个创建实例的静态方法,建议按照不同的功能模块,创建不同的工厂类。因为简单工厂类是一个模块封装提的一部分。
- 使用简单工厂模式之前
- 原始代码
class Chart { private String type; // 图表类型 public Chart(Object[][] data, String type) { this.type = type; if (type.equalsIgnoreCase("histogram")) { // 初始化柱状图 } else if (type.equalsIgnoreCase("pie")) { // 初始化饼状图 } else if (type.equalsIgnoreCase("line")) { // 初始化折线图 } } public void display() { if (this.type.equalsIgnoreCase("histogram")) { // 显示柱状图 } else if (this.type.equalsIgnoreCase("pie")) { // 显示饼状图 } else if (this.type.equalsIgnoreCase("line")) { // 显示折线图 } } }
- 缺点
- (1) 在Chart类中包含很多“if…else…”代码块,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断。
(2) Chart类的职责过重,它负责初始化和显示所有的图表对象,将各种图表对象的初始化代码和显示代码集中在一个类中实现,违反了“单一职责原则”,不利于类的重用和维护;而且将大量的对象初始化代码都写在构造函数中将导致构造函数非常庞大,对象在创建时需要进行条件判断,降低了对象创建的效率。
(3) 当需要增加新类型的图表时,必须修改Chart类的源代码,违反了“开闭原则”。
(4) 客户端只能通过new关键字来直接创建Chart对象,Chart类与客户端类耦合度较高,对象的创建和使用无法分离。
(5) 客户端在创建Chart对象之前可能还需要进行大量初始化设置,例如设置柱状图的颜色、高度等,如果在Chart类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设置,这些代码在每次创建Chart对象时都会出现,导致代码的重复。
- (1) 在Chart类中包含很多“if…else…”代码块,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断。
- 简单工厂的改进
- 使用配置文件,去除if-else
-
<?xml version="1.0"?> <config> <chartType>histogram</chartType> </config>
-
public class XMLUtil { //该方法用于从XML配置文件中提取图表类型,并返回类型名 public static String getChartType() { try { //创建文档对象 DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("config.xml")); //获取包含图表类型的文本节点 NodeList nl = doc.getElementsByTagName("chartType"); Node classNode = nl.item(0).getFirstChild(); String chartType = classNode.getNodeValue().trim(); return chartType; } catch(Exception e) { e.printStackTrace(); return null; } } }
- 简单工厂的简化
- 将抽象产品类和工厂类合并,将静态工厂方法移至抽象产品类中
- 简单工厂的优点
- 能够非常简单快捷的实现模块的组件化,组件通过对外公开接口,实现面向接口编程
- 实现了外部调用和具体实现的解耦,增强了系统的健壮性和易维护性。
- 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
- 简单工厂的缺点
- 违背开放封闭原则(对新增是开的对修改是封闭的)
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
- 适用场景
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
- 参考 https://blog.csdn.net/csdn_ds/article/details/78211992
- 类图
public interface Product { public void viewColor(); public void viewName(); }
public class ConcreteProduct_A implements Product { @Override public void viewColor() { System.out.println("view ConcreateProduct_A color"); } @Override public void viewName() { System.out.println("view ConcreateProduct_A name"); } }
public class ConcreteProduct_B implements Product { @Override public void viewColor() { System.out.println("view ConcreateProduct_B color"); } @Override public void viewName() { System.out.println("view ConcreateProduct_B name"); } }
public class ProductFactory { public static Product CreateInstance(String type){ Product result=null; switch (type) { case "A": result =new ConcreteProduct_A(); break; case "B": result = new ConcreteProduct_B(); break; } return result; } }