前言
本来昨天已经把这一篇发出来了,但是突然发现好像自己的理解有些问题,这就尴尬了,于是又去看了一下网上的众多帖子,再次梳理总结一下我对抽象工厂模式的理解。
本篇主要讲的是我个人对抽象工厂模式的理解以及java实现的demo
原理
首先抽象工厂模式(Abstract Factory Pattern)主要是用于解决创建复杂对象的问题,和工厂模式的 区别在于,工厂模式所创建的对象一般都实现了某一个接口,也就是说他们都具有某一类特定的功能;而抽象工厂模式还解决了接口选择的问题,他所创建的对象可以分为几大类(等级),每个类别的对象分别实现一个接口,并且每个类别下的具体类是一一对应的。而系统要使用时只会使用其中的一个类别的对象,下面举个例子说明:
假设我们有一个生产服装的超级工厂,它的下面有三个子工厂,分别用于生产商务装,休闲装和运动装。我们知道不管是哪个类别工厂生产的套装,都是由一样的东西构成的:上衣、裤子、鞋子。当我们需要一套衣服时,我们首先去超级工厂看看自己想要哪一类的套装,假设我们想要运动装,那么超级工厂就会让我们去运动装生产工厂,去“生产”一套运动装–运动服,运动裤,运动鞋。需要其他类型套装的也是如此,注意,每种工厂只会生产对应的一套套装,比如我们去商务装工厂是生产不了运动装的。
总结起来就是,抽象工厂模式就是围绕一个超级工厂来创建一个具体的工厂,系统需要使用的就是某一个具体工厂的一套产品。我们把每一个工厂所能生产的所有产品叫做一个等级,比如上面服装有运动型,休闲型,商务型。把每个具体工厂下相同种类的产品叫做一个产品族,比如上面的上衣是一个产品族,裤子是一个产品族,鞋子是一个产品族。抽象工厂模式的最大特点就是增加一个等级比较简单,新建一座具体工厂就行,比如我要新增一个礼服等级,只需要新建一个专门生产礼服的工厂,然后再去超级工厂等级一下;但是增加一个产品族就很困难了,需要改变所有的工厂才能实现,比如我要增加一个帽子产品族,则需要在每个具体工厂里都增加做帽子的功能,当已经有的产品族较多时,这是非常困难的。
例如QQ的主题/皮肤有白天模式,黑夜模式,蓝色主题,红色主题等等,每一个主题就是一个等级,每种主题下都会有亮度、大小、颜色、图标等一系列具体的设置,系统每次只会从主题库(超级工厂)里选择一套主题(一个具体工厂)来配置界面,从选择的主题中提取图标、字体、亮度、颜色这一套配置,就相当于用一个具体的工厂创建了一套想要的产品等级。
可以看出,抽象工厂模式可以把创建出来的对象限制在某一类别之下,这种模式特别适用于管理具有不同等级、不同产品族的产品,控制他们的创造方式。
java实现
好了,啰嗦了这么多,下面用一个具体的例子说明,并且用代码实现一下。
故事是这样的,世界上有一种见不得光的职业叫杀手,他们会下面几件事:
- 搞到一把趁手的枪外加它的配套的子弹– getWeapon();
- 把子弹填到枪里–assembel();
- 寻找刺杀目标–findTarget();
- 射击–shoot();
于是,我们用Killer这个抽象类来表示:
public abstract class Killer {
//搞到武器
public abstract void getWeapon();
//闭着眼睛都能组装武器
public void assemble(){
System.out.println("killer:weapon assembled !");
}
//每次都顺利找到目标
public void findTarget(){
System.out.println("killer:target found !");
}
//假设杀手命中率100%
public void shoot(){
System.out.println("killer:target killed !");
}
}
我们假设每个杀手的武器都是枪,都需要子弹:
//枪的父类
public class Gun{...}
//子弹的父类
public class Bullet{...}
有的杀手比较喜欢AK47的枪,自然要配上AK47的子弹:
AKWeapon.java
//接口AKWeapon 所有AK生产的东西都实现了这个接口
public interface AKWeapon {
void logInfo();
}
AKGun.java
//AK所生产的枪AK47
public class AkGun extends Gun implements AKWeapon {
@Override
public void logInfo() {
// TODO Auto-generated method stub
System.out.println("ak_gun got from factory.");
}
}
AKBullet.java
//AK生产的专门配AK枪的子弹
public class AKBullet extends Bullet implements AKWeapon {
@Override
public void logInfo() {
// TODO Auto-generated method stub
System.out.println("ak_bullets got from factory");
}
}
萝卜白菜各有所爱,有的杀手就喜欢沙漠之鹰的枪,当然只能配沙漠之鹰的子弹:
SmzyWeapon.java
//所有沙漠之鹰工厂生产的枪和子弹都实现这个接口,这是一种沙漠之鹰的标记
public interface SmzyWeapon {
void logInfo();
}
SmzyGun.java
//沙漠之鹰生产的枪
public class SmzyGun extends Gun implements SmzyWeapon {
@Override
public void logInfo() {
// TODO Auto-generated method stub
System.out.println("smzy_gun got from factory.");
}
}
SmzyBullet.java
//沙漠之鹰的配套子弹
public class SmzyBullet extends Bullet implements SmzyWeapon {
@Override
public void logInfo() {
// TODO Auto-generated method stub
System.out.println("smzy_bullet got from factory.");
}
}
那么问题来了,假设我是一个刚入行,还没有武器的杀手小白,今天接到了第一单生意,要我去刺杀某个国家的总统。于是,我得先去弄清楚在哪里可以搞到武器(一直听说沙漠之鹰很厉害,所以我决定去搞一套AK47的武器)。
经过杀手界的前辈指点,所有的杀手(不管用什么武器)都会去离自己最近一个超级武器工厂来获取武器,我住的地方附近就有一个超级工厂,不过规模比较小,只有两个子工厂,一个专门生产AK的武器,包括AK47和它的子弹,另一个专门生产沙漠之鹰和对应的子弹。虽说两个子工厂生产的武器等级不一样,但是抽象一下,都是武器工厂嘛:
WeaponFactory.java
//抽象的武器工厂,可能会生产AK或者沙漠之鹰以及配套的子弹
public abstract class WeaponFactory {
public abstract AKWeapon createAKWeapon(String aKType);
public abstract SmzyWeapon createSmzyWeapon(String smzyType);
}
处于好奇,我首先去专门生产沙漠之鹰的具体工厂看了一下:
SmzyFactory.java
//专门生产沙漠之鹰的工厂
public class SmzyFactory extends WeaponFactory {
//果然只能生产沙漠之鹰
@Override
public AKWeapon createAKWeapon(String aKType) {
// TODO Auto-generated method stub
return null;
}
@Override
public SmzyWeapon createSmzyWeapon(String smzyType) {
// TODO Auto-generated method stub
String type = smzyType.toLowerCase();
if(type.equals("gun")){
System.out.println("smzy_factory: create sumzy_gun;");
return new SmzyGun();
}else if(type.equals("bullet")){
System.out.println("smzy_factory: create sumzy_bullet;");
return new SmzyBullet();
}
return null;
}
}
接着,我又去了专门生产AK的工厂:
AKFactory.java
//专门生产AK的工厂
public class AKFactory extends WeaponFactory {
@Override
public AKWeapon createAKWeapon(String aKType) {
// TODO Auto-generated method stub
String type = aKType.toLowerCase();
if(type.equals("gun")){
System.out.println("ak_factory: create ak_gun;");
return new AkGun();
}else if(type.equals("bullet")){
System.out.println("ak_factory: create ak_bullet;");
return new AKBullet();
}
return null;
}
//果然只生产AK
@Override
public SmzyWeapon createSmzyWeapon(String smzyType) {
// TODO Auto-generated method stub
return null;
}
}
逛完之后,我再次回到了超级工厂:
public class SuperFactory {
public static WeaponFactory createWeaponFactory(String fType){
String type = fType.toLowerCase();
if(type.equals("ak")){
System.out.println("super_factory: create ak_factory;");
return new AKFactory();
}else if(type.equals("smzy")){
System.out.println("super_factory: create smzy_factory;");
return new SmzyFactory();
}
return null;
}
}
那里的人问我:“尊敬的客人,请问你要使用的是什么武器?”
我说:“AK吧,我比较用的来”.
–”好的,请直接去那边那个叫做ak的武器工厂”
于是,搞到了武器的我正式成为了一个用AK的杀手:
public class AKKIller extends Killer {
@Override
public void getWeapon() {
// TODO Auto-generated method stub
//杀手告诉超级工厂我只用AK的武器,工厂让我去对应的AK工厂
WeaponFactory factory = SuperFactory.createWeaponFactory("ak");
//拿到枪和子弹(都是AK武器)
AKWeapon gun = factory.createAKWeapon("gun");
AKWeapon bullet = factory.createAKWeapon("bullet");
System.out.println("akKiller:weapon got !");
}
}
有了枪和子弹,还等什么?干活去了
public class Demo {
public static void main(String args[]){
AKKIller akKiller = new AKKIller();
akKiller.getWeapon();
akKiller.assemble();
akKiller.findTarget();
akKiller.shoot();
}
}
任务记录:
super_factory: create ak_factory;//去ak工厂吧
ak_factory: create ak_gun;//拿到AK47
ak_factory: create ak_bullet;//拿到AK的子弹
akKiller:weapon got !//一手交钱,一手交货
killer:weapon assembled !//装弹
killer:target found !//找目标、瞄准
killer:target killed !//射击!任务完成
… …
时代在改变,连超级工厂也要扩建了,首先它计划能够让杀手们在它那能买到经典的左轮手枪和左轮子弹。那么,需要怎么办呢,首先当然是新建一个专门生产左轮产品的左轮工厂,接着给生产出来的枪和子弹实现左轮特有的标记,左轮接口(ZuoLunWeapon.java),修改一下抽象工厂的可能生产出的武器等级(加一个左轮类别)就可以了。
随着时间的推移,超级工厂下面也有了十几个子工厂,可以生产出包括沙漠之鹰和AK47在内的十几种枪和子弹。然而,新的问题又来了,杀手们希望所有的枪和子弹能再配一个弹夹,以携带更多的子弹,也就是说,超级工厂得增加弹夹这个产品族(包括沙漠之鹰弹夹和AK47弹夹以及后来扩建之后的工厂应该有的左轮弹夹等等十几种弹夹),这下就麻烦了,所有的子工厂都得停止生产,先引进各自所需的弹夹生产线,才能继续生产。
看来,增加等级类别比较简单,要增加产品族就难了。
以上代码只是我对抽象工厂模式理解的一个比较粗浅的实现,没有考虑null对象的问题。也不知道理解的对不对,欢迎指正。