千呼万唤的抽象工厂模式,它来了!
上一篇分享的工厂模式,它在这里
1. 什么是抽象工厂模式?
定义 :抽象工厂模式(Abstract Factory),提供了一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
假设你已经了解了工厂模式,那么我们对比一下工厂模式和抽象工厂的类图:
工厂模式:
抽象工厂模式:
2. 如何理解抽象工厂模式?
在介绍工厂模式的时候时候,我自己胡诌了一个面点和面点厂的例子,这次,我们还是用这个例子进行说明,只是稍微换一个方式:
说明:
- 面点厂相当于父类,具体的面点继承于面点厂的“加面”和“加糖”的方法
- 糖厂和面粉厂属于是具体的实现类的抽象,可以是接口(推荐),也可以是父类
例子仅用于理解,并不一定符合实际,诸君有更好的例子,欢迎借我学习一下!
个人觉得,多多的画画图,思考一下,会加深理解!
3. 代码实现抽象工厂
光说不练,代码出现:
按照上面的类图,我们来个例子:
首先,我们有一个User接口:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:02 下午
*/
public interface User {
/**
* 得到用户来自哪里
*/
void userFrom();
}
和一个Department接口:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:06 下午
*/
public interface Department {
/**
* 得到用户的部门
*/
abstract void myDepartment();
}
我们通过一个工厂接口,返回具体的人和部门:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:02 下午
*/
public interface IFactory {
abstract User getUser();
abstract Department getDepartment();
}
我们有一个中国的工厂,继承自IFactory:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:06 下午
*/
public class ChineseFactory implements IFactory {
@Override
public User getUser() {
return new Chinese();
}
@Override
public Department getDepartment() {
return new China();
}
}
另外还有一个外国工厂(没错,在我看来,除了中国工厂,都是其他工厂):
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:15 下午
*/
public class ForeginFactory implements IFactory {
@Override
public User getUser() {
return new Foreigner();
}
@Override
public Department getDepartment() {
return new Foreign();
}
}
作为一名中国人:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:11 下午
*/
public class Chinese implements User {
@Override
public void userFrom() {
System.out.println("i'm a chinese!");
}
}
我在中国上班:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:13 下午
*/
public class China implements Department {
@Override
public void myDepartment() {
System.out.println("我在中国公司上班!");
}
}
当然,外国人:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:12 下午
*/
public class Foreigner implements User {
@Override
public void userFrom() {
System.out.println("i'm from England");
}
}
在外国上班:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:13 下午
*/
public class Foreign implements Department {
@Override
public void myDepartment() {
System.out.println("i'm working at America!");
}
}
写一个Main,测试一下:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:16 下午
*/
public class Main {
public static void main(String[] args) {
IFactory iFactory = new ChineseFactory();
User user = iFactory.getUser();
user.userFrom();
Department department = iFactory.getDepartment();
department.myDepartment();
System.out.println("======================");
IFactory iFactory1 = new ForeginFactory();
User user1 = iFactory1.getUser();
user1.userFrom();
Department department1 = iFactory1.getDepartment();
department1.myDepartment();
}
}
输出:
i'm a chinese!
我在中国公司上班!
======================
i'm from England
i'm working at America!
4. 抽象工厂模式的优缺点
就这样,一个简单的抽象工厂模式就结束了!
例子完了,我们说一下它的优缺点:
优点:
- 易于交换产品系列,由于具体工厂类在只在初始化时出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置
即上例中,如果你想让变为外国工厂,只需要改IFactory iFactory = new ChineseFactory();中的具体实现类即可
- 让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
在上例中,我们只看到User和Department,并不知道是中国还是外国。
缺点:
当然了,缺点也是有的,加入此时,我们新增了一个“产品类”,我们就需要增加一个工厂IProduct,以及其子类ChineseProduct和ForeignProduct,而且还要修改IFactory、ChineseFactory和ForeignFactory,是不是很麻烦?
4.1 简单工厂模式解决新增抽象工厂的问题
所以,我们可以使用一下简单工厂模式,来封装一下所有的工厂:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/8/6 3:53 下午
*/
public class FactoryHandler {
private String country;
public FactoryHandler(String country) {
this.country = country;
}
public User createUser(){
switch (country){
case "Foreign":
return new Foreigner();
case "China":
return new Chinese();
default:
return null;
}
}
public Department createDep(){
switch (country){
case "Foreign":
return new Foreign();
case "China":
return new China();
default:
return null;
}
}
}
此时,客户端改成这样:
package com.aran.DesignPatterns.AbstractFactory;
/**
* @Author Aran
* @Date 2020/3/19 8:16 下午
*/
public class Main {
public static void main(String[] args) {
// IFactory iFactory = new ChineseFactory();
//
// User user = iFactory.getUser();
// user.userFrom();
//
// Department department = iFactory.getDepartment();
// department.myDepartment();
//
// System.out.println("======================");
//
// IFactory iFactory1 = new ForeginFactory();
//
// User user1 = iFactory1.getUser();
// user1.userFrom();
//
// Department department1 = iFactory1.getDepartment();
// department1.myDepartment();
User user = new FactoryHandler("China").createUser();
Department department = new FactoryHandler("Foreign").createDep();
user.userFrom();
department.myDepartment();
}
}
注意我的传参!
输出结果:
i'm a chinese!
i'm working at America!
4.2 反射解决简单工厂导致的问题
于是,这样就解决了需要增加AbstractFactory实现的问题。
你以为完了?其实我也以为完了!
然而,这样的情况就是,但如果我们除了China和Foreign时,需要增加一个Alien时,麻烦来了,我们要在FactoryHandler 中每一个 create* 方法中为switch增加一个分支!!!
好吧,既然这么多可能新增的情况,我们不知道有多少个抽象工厂,是不是可以考虑一下————————————————反射。
此时此刻,我才发现,我们的整个例子不太适合用反射模式,利用部分代码讲一下思路:
- 上一步中我们传递了一个“country”参数,我们以简单工厂中的createDep() 为例:
原来它是这样子的:
public Department createDep(){
switch (country){
case "Foreign":
return new Foreign();
case "China":
return new China();
default:
return null;
}
}
- 我们使用反射,就可以去掉switch:
public Department createDep() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return (Department)Class.forName(FactoryHandler.class.getPackageName() + ".Foreign").getConstructor().newInstance();
// switch (country){
// case "Foreign":
// return new Foreign();
// case "China":
// return new China();
// default:
// return null;
// }
}
- FactoryHandler.class.getPackageName() 表示当前包名,你想写死也可以!
再次运行,得到输出:
i'm a chinese!
i'm working at America!
完美~
5. 总结
主要内容简单总结下:
- 抽象工厂模式与工厂模式的对比
- 抽象工厂模式的代码实现
- 抽象工厂模式的优缺点
- 简单工厂模式解决抽象工厂模式中的缺点
- 反射解决简单工厂结合抽象工厂方式中存在的缺点
我在最好的时候碰到你,是我的运气。
——《一代宗师》