抽象工厂模式简单介绍
抽象工厂模式也是创建型设计模式之一。在上一篇博客中我们已经对工厂方法模式进行了介绍,那么这个抽象工厂又是怎么一回事呢?大家联想一下现实生活中的工厂肯定都是具体的,也就是说每个工厂都会生产某一种具体的产品,那么抽象工厂意味着生产出来的产品是不确定的,那这岂不是很奇怪?抽象工厂模式起源于以前对不同操作系统的图形化解决方案,如不同操作系统中的按钮和文本框控件其实现不同,展示效果也不一样,对于每一个操作系统,其本身就构成一个产品类,而按钮与文本框也构成一个产品类,而按钮与文本框控件也构成一个产品类,两种产品类两种变化,各自有自己的特性,如 Android 中的 Button 和 TextView,IOS 中的 Button 和 TextView,WindowPhone 中的 Button 和 TextView 等。
抽象工厂模式的定义
为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定它们的具体类。
抽象工厂模式的使用场景
一个对象有相同的约束时可以使用抽象工厂模式。是不是听起来很抽象?举个例子,Android、IOS、WindowPhone 下都有短信软件 和 拨号软件,两者都属于 Software 软件的范畴,但是,它们所在的操作系统平台不一样,即使是同一家公司出品的软件,其代码的实现逻辑也是不同的,这个时候就可以考虑使用工厂方法模式来生产 Android、IOS、WindowPhone 下的短信软件和拨号软件。还是有点迷糊?没关系,等我们稍后给出一个抽象工厂的例子之后,你就明白了。
抽象工厂模式的 UML 类图
类图画的不是很好,由于是采用在线工具 UMLer 进行绘制的,它里面的关系线貌似好像不能折弯,所以关系线出现了重叠,不过大概也可以看明白类与类的关系的。
其实从抽象工厂模式的类图可以看出它和我们上篇博客中提到了工厂方法模式的类图基本上是大同小异的。工厂方法类图是咱们给出的 UML 类图其实是单个工厂对应单个产品,而抽象工厂模式其实更偏向于多工厂对应多产品这么一种情况,大家可以看下工厂方法模式的类图和抽象工厂模式的类图的区别。
角色介绍:
- Factory:抽象工厂角色,它声明了一组用于创建一种产品的方法,每一个方法对应一种产品,如上述类图中的 Factory 中就定义了两个方法,分别用于创建产品A 和 产品B。
- ConcreteFactory:具体工厂角色,它实现了在抽象工厂中定义的抽象产品的方法,生成一组具体的产品,这些产品构成了一个产品种类,每一个产品都位于某个产品等级结构中,如上述类图中的ConcreteFactoryA 和 ConcreteFactoryB。
- Product:抽象产品角色,它为每种产品生命接口,比如上述类图中的 ProductA 和 ProductB。
- ConcreteProduct:具体产品角色,它定义具体工厂生产的具体产品角色,实现抽象产品接口中声明的业务方法,如上述类图中的 ConcreteProductA1、ConcreteProductA2、ConcreteProductB1 和 ConcreteProductB2。
根据类图可以得到一套通用的模板代码:
//抽象产品类A
public abstract class ProductA {
/**
* 产品类的抽象方法,由具体的产品类去实现
*/
public abstract void method();
}
//抽象产品类B
public abstract class ProductB {
/**
* 产品类的抽象方法,由具体的产品类去实现
*/
public abstract void method();
}
//具体产品类A1
public class ConcreteProductA1 extends ProductA {
@Override
public void method() {
Log.d("Product","我是具体的产品A1");
}
}
//具体产品类A2
public class ConcreteProductA2 extends ProductA {
@Override
public void method() {
Log.d("Product","我是具体的产品A2");
}
}
//具体产品类B1
public class ConcreteProductB1 extends ProductA {
@Override
public void method() {
Log.d("Product","我是具体的产品B1");
}
}
//具体产品类B2
public class ConcreteProductB2 extends ProductA {
@Override
public void method() {
Log.d("Product","我是具体的产品B2");
}
}
//抽象工厂类
public abstract class Factory {
/**
* 抽象工厂方法,具体生产什么产品由子类去实现
*
* @return
*/
public abstract <T extends ProductA> T createProductA(Class<T> clazz);
public abstract <T extends ProductB> T createProductB(Class<T> clazz);
}
//具体工厂类A
public class ConcreteFactoryA extends Factory {
@Override
public <T extends ProductA> T createProductA(Class<T> clazz) {
ProductA p = null;
try {
p = (ProductA) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) p;
}
@Override
public <T extends ProductB> T createProductB(Class<T> clazz) {
ProductB p = null;
try {
p = (ProductB) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) p;
}
}
//具体工厂类
public class ConcreteFactoryB extends Factory {
@Override
public <T extends ProductA> T createProductA(Class<T> clazz) {
ProductA p = null;
try {
p = (ProductA) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) p;
}
@Override
public <T extends ProductB> T createProductB(Class<T> clazz) {
ProductB p = null;
try {
p = (ProductB) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) p;
}
}
大家可以自己找下感觉,下面我们就开始实战
抽象工厂方法模式实战
在上篇博客中的工厂方法模式中,我们已经拿汽车厂为例阐述了工厂方法模式,但是,假设现在出现了一个问题,虽然 Q3、Q5、Q7 都是一个车系,但是三者之间的零部件差别却是很大,就拿 Q3 和 Q5 来说,Q3 使用的发动机是国产的,而 Q7 使用的是原装进口的;Q3 的轮胎是普通的轮胎,而 Q7 则使用的是全尺寸越野轮胎;还有 Q3 使用的是比较普通的制动系统,而 Q7 则使用的是制动性能极好的制动系统。Q3、Q7 对应的是一系列车,而 发动机、轮胎、制动系统则对应的是一系列零件,两者是两种不同的产品类型,这时候就可以将抽象工厂模式应用到其中。
我们先来捋一捋思路吧:首先,从上面给出的题目中其实我们就已经知道了很多信息:1、Q3、Q5、Q7是同一系列车;2、Q3、Q5、Q7的零部件不同。那么我们就要思考了,因为是汽车厂,是以生产汽车为主,咱们在上篇博客中之所以采用一条生产线是因为所有车都是一样的,所以没必要开辟多条生产线,而这里还行么?虽然车是同一系列车,但是车的零部件不一样啊?如果还是用一条生产线还能同时生成 3 中车么?不能了吧?所以这里我们就要创建 3 条生产线去生成不用系列的车(相对语需要 3 个工厂),而每个工厂再去组装本系列的车(发动机、轮胎、制动系统),OK了,下面就是代码实现了:
/**
* 抽象车厂类
* 每个具体的工厂都需要生产本系列车需要的部件
*/
public abstract class AudiFactory {
/**
* 生产轮胎
* @param clazz
* @param <T>
* @return 轮胎
*/
public abstract <T extends ITire> T createTire(Class<T> clazz);
/**
* 生产发动机
* @param clazz
* @param <T>
* @return 发动机
*/
public abstract <T extends IEngine> T createEngine(Class<T> clazz);
/**
* 生产制动系统
* @param clazz
* @param <T>
* @return 制动系统
*/
public abstract <T extends IBrake> T createBrake(Class<T> clazz);
}
/**
* 轮胎接口
*/
public interface ITire {
void tire();
}
/**
* 普通轮胎
*/
public class NormalTire implements ITire {
@Override
public void tire() {
Log.d("Audi","普通轮胎");
}
}
/**
* 越野轮胎
*/
public class SUVTire implements ITire {
@Override
public void tire() {
Log.d("Audi","越野轮胎");
}
}
/**
* 发动机接口
*/
public interface IEngine {
void engine();
}
/**
* 国产发动机
*/
public class DomesticEngine implements IEngine {
@Override
public void engine() {
Log.d("Audi", "国产发动机");
}
}
/**
* 进口发动机
*/
public class ImportEngine implements IEngine {
@Override
public void engine() {
Log.d("Audi", "进口发动机");
}
}
/**
* 制动系统
*/
public interface IBrake {
void brake();
}
/**
* 普通制动系统
*/
public class NormalBrake implements IBrake {
@Override
public void brake() {
Log.d("Audi", "普通制动系统");
}
}
/**
* 高级制动系统
*/
public class SeniorBrake implements IBrake {
@Override
public void brake() {
Log.d("Audi", "高级制动系统");
}
}
/**
* Q3系列车以及零部件生产工厂
*/
public class Q3Factory extends AudiFactory {
/**
* 生产轮胎
* @param clazz
* @param <T>
* @return 轮胎
*/
@Override
public <T extends ITire> T createTire(Class<T> clazz) {
ITire tire = null;
try {
tire = (ITire) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) tire;
}
/**
* 生产发动机
* @param clazz
* @param <T>
* @return 发动机
*/
@Override
public <T extends IEngine> T createEngine(Class<T> clazz) {
IEngine engine = null;
try {
engine = (IEngine) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) engine;
}
/**
* 生产制动系统
* @param clazz
* @param <T>
* @return 制动系统
*/
@Override
public <T extends IBrake> T createBrake(Class<T> clazz) {
IBrake brake = null;
try {
brake = (IBrake) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) brake;
}
}
/**
* Q7系列车以及零部件生产工厂
*/
public class Q7Factory extends AudiFactory {
/**
* 生产轮胎
* @param clazz
* @param <T>
* @return 轮胎
*/
@Override
public <T extends ITire> T createTire(Class<T> clazz) {
ITire tire = null;
try {
tire = (ITire) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) tire;
}
/**
* 生产发动机
* @param clazz
* @param <T>
* @return 发动机
*/
@Override
public <T extends IEngine> T createEngine(Class<T> clazz) {
IEngine engine = null;
try {
engine = (IEngine) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) engine;
}
/**
* 生产制动系统
* @param clazz
* @param <T>
* @return 制动系统
*/
@Override
public <T extends IBrake> T createBrake(Class<T> clazz) {
IBrake brake = null;
try {
brake = (IBrake) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) brake;
}
}
/**
* 客户类
*/
public class Test {
public static void main() {
//构造一个 Q3 工厂
Q3Factory q3Factory = new Q3Factory();
//生产 Q3 汽车使用的普通轮胎
q3Factory.createTire(NormalTire.class).tire();
//生产 Q3 汽车使用的国产发动机
q3Factory.createEngine(DomesticEngine.class).engine();
//生产 Q3 汽车使用的普通制动系统
q3Factory.createBrake(NormalBrake.class).brake();
Q7Factory q7Factory = new Q7Factory();
//生产 Q7 汽车使用的越野轮胎
q7Factory.createTire(SUVTire.class).tire();
//生产 Q7 汽车使用的进口发动机
q7Factory.createEngine(ImportEngine.class).engine();
//生产 Q7 汽车使用的高级制动系统
q7Factory.createBrake(SeniorBrake.class).brake();
}
}
上面我们只是模拟了 Q3 和 Q7 的工厂,如果此时我们需要增加 Q5 的工厂呢?那么对应的轮胎、制动系统和发动机类又要增加(仅当 Q5 的轮胎、发动机和制动系统又出现上面没有的型号时才需要相对应的增加,如果已经存在,则不需要增加),这里就可以看出抽象工厂方法模式的一个弊端,就是类的徒增,如果工厂类过多,势必会导致类文件增多,因此,在实际开发中一定要权衡慎用。
总结
- 抽象工厂方法模式的优点:
一个显著的优点就是分离接口与实现,客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现逻辑,客户端只是面向产品的接口编程而已,使其从具体的产品实现中解耦,同时基于接口与实现的分离,是抽象工厂方法模式在切换产品类时更加灵活、容易。
- 抽象工厂方法模式的缺点:
上面我们也有所提及,一是类文件的爆炸性增加,二是不太容易扩展新的产品类,因为每当我们增加一个产品类就需要修改抽象工厂,那么所有的具体工厂类均会被修改。