本文转发自技术世界,原文链接 http://www.jasongj.com/design_pattern/factory_method/
简单工厂模式(Simple Factory Pattern)又叫静态工厂方法模式(Static FactoryMethod Pattern)。
建立一个简单披萨工厂
public class SimplePizzaFactory{ public static Pizza createPizza(String type) { Pizza pizza=null; if(type.equals("cheese")) { pizza=new CheesePizza(); }else if(type.equals("pepperoni")) { pizza=new PepperoniPizza(); }else if(type.equals("clam")) { pizza=new ClamPizza(); }else if(type.equals("veggie")) { pizza=new ViggiePizza(); } return pizza; } }重做PizzaStore类
public class PizzaStore{ public Pizza orderPizza(String type) { Pizza pizza=SimplePizzaFactory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }简单工厂模式进阶
使用反射实现扩展性
从SimplePizzaFactory可以看出,当有新的种类的pizza加入时,需要更新SimplePizzaFactory的代码也实现对新的pizza的支持。这就违反了开闭原则(Open-Close Principle)。可以利用反射(Reflection)解决该问题。
public class SimplePizzaFactory{ public static Pizza createPizza() { String name=null; Pizza pizza=null; try { XMLConfiguration config = new XMLConfiguration("pizza.xml"); name= config.getString("factory.class"); } catch (ConfigurationException ex) { ex.printStackTrace(); } try { pizza = (Pizza)Class.forName(name).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { e.printStackTrace(); } return pizza; } }通过读取配置文件,获取需要的Pizza,然后创建相应的实例并由父类Pizza的引用指向它,利用多态执行不同的prepare、bake、cut、box方法。从上面代码中可以看到,之后如果需要引入新的Pizza,只需要在配置文件中指定该Pizza的完整类名(包括package名),SimplePizzaFactory即可通过反射将其实例化。实现了对扩展的开放,同时保证了对修改的关闭。熟悉Spring的读者应该会想到Spring IOC的实现。
在推广SimpleFactory时,你发现加盟店的确是采用你的工厂创建披萨,但是其他部分,却开始采用他们自创的流程:烘烤的做法有些差异、不要切片、使用其他厂商的盒子。
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫多态工厂模式或者虚拟构造器模式。在工厂方法模式中,工厂父类定义创建产品对象的公共接口,具体的工厂子类负责创建具体的产品对象。每一个工厂子类负责创建一种具体产品。
工厂方法模式类图 工厂方法模式使用方式
如简单工厂模式直接使用静态工厂方法创建产品对象不同,在工厂方法,客户端通过实例化具体的工厂类,并调用其创建实例接口创建具体产品类的实例。根据依赖倒置原则,具体工厂类的实例由工厂接口引用(客户端依赖于抽象工厂而非具体工厂),具体产品的实例由产品接口引用(客户端和工厂依赖于抽象产品而非具体产品)。具体调用代码如下:
public class Client { public static void main(String[] args) { IDaoFactory factory = new MySQLDaoFactory(); IUserDao userDao = factory.createUserDao(); userDao.getUser("admin"); } }工厂方法模式优点
因为每个具体工厂类只负责创建产品,没有简单工厂中的逻辑判断,因此符合单一职责原则。与简单工厂模式不同,工厂方法并不使用静态工厂方法,可以形成基于继承的等级结构。新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可,相比于简单工厂模式需要修改判断逻辑而言,工厂方法模式更符合开-闭原则。工厂方法模式缺点
添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要换用另外一种产品,仍然需要修改实例化的具体工厂。一个具体工厂只能创建一种具体产品简单工厂模式与OOP原则
已遵循的原则
依赖倒置原则(要依赖抽象,不依赖具体类)迪米特法则里氏替换原则接口隔离原则单一职责原则(每个工厂只负责创建自己的具体产品,没有简单工厂中的逻辑判断)开闭原则(增加新的产品,不像简单工厂那样需要修改已有的工厂,而只需增加相应的具体工厂类)未遵循的原则
开闭原则(虽然工厂对修改关闭了,但更换产品时,客户代码还是需要修改)抽象工厂模式解决的问题
在工厂方法模式中一种工厂只能创建一种具体产品。而在抽象工厂模式中一种具体工厂可以创建多个种类的具体产品。
抽象工厂模式介绍
抽象工厂模式(Factory Method Pattern)中,抽象工厂提供一系列创建多个抽象产品的接口,而具体的工厂负责实现具体的产品实例。抽象工厂模式与工厂方法模式最大的区别在于抽象工厂中每个工厂可以创建多个种类的产品。
抽象工厂模式类图 抽象工厂模式使用方式
与工厂方法模式类似,在创建具体产品时,客户端通过实例化具体的工厂类,并调用其创建目标产品的方法创建具体产品类的实例。根据依赖倒置原则,具体工厂类的实例由工厂接口引用,具体产品的实例由产品接口引用。具体调用代码如下
public class Client { public static void main(String[] args) { IDaoFactory factory = new MySQLDaoFactory(); IUserDao userDao = factory.createUserDao(); User user = new User(); user.setUsername("demo"); user.setPassword("demo".toCharArray()); userDao.addUser(user); IRoleDao roleDao = factory.createRoleDao(); roleDao.getRole("admin"); IProductDao productDao = factory.createProductDao(); Product product = new Product(); productDao.removeProduct(product); } }抽象工厂模式案例解析
上例是J2EE开发中常用的DAO(Data Access Object),操作对象(如User和Role,对应于数据库中表的记录)需要对应的DAO类。
在实际项目开发中,经常会碰到要求使用其它类型的数据库,而不希望过多修改已有代码。因此,需要为每种DAO创建一个DAO接口(如IUserDao,IRoleDao和IProductDao),同时为不同数据库实现相应的具体类。
调用方依赖于DAO接口而非具体实现(依赖倒置原则),因此切换数据库时,调用方代码无需修改。
这些具体的DAO实现类往往不由调用方实例化,从而实现具体DAO的使用方与DAO的构建解耦。实际上,这些DAO类一般由对应的具体工厂类构建。调用方不依赖于具体工厂而是依赖于抽象工厂(依赖倒置原则,又是依赖倒置原则)。
每种具体工厂都能创建多种产品,由同一种工厂创建的产品属于同一产品族。例如PostgreSQLUserDao,PostgreSQLRoleDao和PostgreSQLProductDao都属于PostgreSQL这一产品族。
切换数据库即是切换产品族,只需要切换具体的工厂类。如上文示例代码中,客户端使用的MySQL,如果要换用Oracle,只需将MySQLDaoFactory换成OracleDaoFactory即可。
抽象工厂模式优点
因为每个具体工厂类只负责创建产品,没有简单工厂中的逻辑判断,因此符合单一职责原则。与简单工厂模式不同,抽象工厂并不使用静态工厂方法,可以形成基于继承的等级结构。新增一个产品族(如上文类图中的MySQLUserDao,MySQLRoleDao,MySQLProductDao)时,只需要增加相应的具体产品和对应的具体工厂类即可。相比于简单工厂模式需要修改判断逻辑而言,抽象工厂模式更符合开-闭原则。抽象工厂模式缺点
新增产品种类(如上文类图中的UserDao,RoleDao,ProductDao)时,需要修改工厂接口(或者抽象工厂)及所有具体工厂,此时不符合开-闭原则。抽象工厂模式对于新的产品族符合开-闭原则而对于新的产品种类不符合开-闭原则,这一特性也被称为开-闭原则的倾斜性。