设计模式(二 & 三)工厂模式:3-抽象工厂模式
- 什么是抽象工厂?
抽象工厂模式,引入了“产品族”的概念。
何为产品族?还是以 设计模式(二)工厂模式:2-工厂方法模式 提到的 Operation 为例。
之前讨论的都是局限于 Operation 这个产品,这是一个由“工厂”做出来的产品,而至于是哪一个工厂做出来的产品,我们并不关心。
抽象工厂模式关注的就是这一点,现在为其加上前缀:“工厂1生产的 Operation”,“工厂2生产的 Operation”。
这就是产品族,对象拥有相同的行为,但却是有不同的实现。
- 模拟场景:
Operation 在这个并不能很好地体现 抽象工厂 的优势,我们换一个例子:
现在,我需要去创建用户(User)和部门(Department)两个对象,但是底层需要用的数据库(DB2 和 MySQL),可能产生变化,所以需要两套解决方案。
- 思想:
首先,针对需要创建的对象,需要一个抽象工厂(AbsFactory),负责创建抽象用户(AbsUser)和抽象部门(AbsDepartment)。
根据底层的数据库,抽象工厂拥有两个实现,DB2 工厂(FactoryDB2)和 MySQL 工厂(FactoryMySQL)。
每个工厂各司其职,创建对应的继承抽象类的对象。
- UML
- UML 分析:
这是一个针对给出情景的完整 UML 类图。
为用户(AbsUser)定义了两种行为,而部门(AbsDepartment)没有定义行为。
从 UML 就可以看出,抽象工厂,是对 工厂方法 的一种延伸,FactoryDB2 和 FactoryMySQL 起着 AbsOperationFactory 的作用。
将工厂方法的 AbsOperationFactory 转为实例工厂,并在之上加上一个抽象层,就是抽象工厂。
- 代码:
public abstract class AbsFactory { public abstract AbsUser createUser(); public abstract AbsDepartment createDepartment(); }
public final class FactoryDB2 extends AbsFactory { @Override public UserDB2 createUser() { return new UserDB2(); } @Override public DepartmentDB2 createDepartment() { return new DepartmentDB2(); } }
public final class FactoryMySQL extends AbsFactory { @Override public UserMySQL createUser() { return new UserMySQL(); } @Override public DepartmentMySQL createDepartment() { return new DepartmentMySQL(); } }
public abstract class AbsUser { protected boolean hasRegistered = false; protected void joinDepartment(AbsDepartment department) { if (hasRegistered) { System.out.println(this + " join " + department); } else { System.out.println(this + " not registered cannot join department"); } } protected abstract void register(); @Override public String toString() { return "User[" + this.getClass().getSimpleName() + ":" + this.hashCode() + "]"; } }
public final class UserDB2 extends AbsUser { @Override public void register() { super.hasRegistered = true; System.out.println(this + "register success"); } }
public final class UserMySQL extends AbsUser { @Override public void register() { super.hasRegistered = false; System.out.println(this + "register failure"); } }
public abstract class AbsDepartment { @Override public String toString() { return "Department[" + this.getClass().getSimpleName() + ":" + this.hashCode() + "]"; } }
public final class DepartmentDB2 extends AbsDepartment { }
public final class DepartmentMySQL extends AbsDepartment { }
- 客户端的调用代码:
public final class AbsFactoryTest { @Test void testFactoryDB2() { AbsFactory factory = new FactoryDB2(); AbsUser user = factory.createUser(); AbsDepartment department = factory.createDepartment(); user.register(); user.joinDepartment(department); } @Test void testFactoryMySQL() { AbsFactory factory = new FactoryMySQL(); AbsUser user = factory.createUser(); AbsDepartment department = factory.createDepartment(); user.register(); user.joinDepartment(department); } }
- 优势
从客户端的调用,我们就可以发现,从创建用户开始,后续的代码都是一致的。
也就是说,只需要改变顶层的实例工厂,就可以改变具体的行为(到底是 MySQL 还是 DB2)。
如果需要加入新的工厂,例如 FactoryOracle,不会对现有的代码产生破坏,很好体现了:对扩展开放,对修改关闭。
- 劣势
在设计 抽象工厂 时,需要完整地考虑到工厂需要创建哪些东西,在这个例子上就是 User 和 Department。
如果在设计完毕之后,需要引入新的产品,例如订单 Order 之类的,对于现有代码的结构破坏会很大,从顶层开始就破坏了代码的封装性。
- 抽象工厂有哪些实际应用?
数据库的 ORM 框架。
Java 平台无关的特性。