Android Tech And Thoughts.

Factory design pattern

Word count: 2kReading time: 7 min
2019/12/14 Share

在面向对象的世界里,工厂模式被广泛地应用于项目中,也许你没听过,但你肯定用过。简单地来说,工厂模式地出现源于增加程序的可扩展性,降低耦合度。之所以叫工厂模式,是用工厂生产产品来形象比如代码中生产对象的过程。

  1. 简单工厂模式
  2. 工厂方法模式
  3. 抽象工厂模式

简单工厂模式 | simple Factory Pattern

Before patterns:
首先设计两种汽车,分别实现 “Auto” 接口。

1
public interface Auto{
2
	//所有汽车都可以驾驶
3
	public void drive();
4
}
5
6
//小轿车
7
public class Car implements Auto{
8
	@Override
9
	public void drive(){
10
		System.out.println("小轿车启动了...");
11
	}
12
}
13
14
public class Bus implements Auto{
15
	@Overroide
16
	public void drive(){
17
		System.out.println("大巴车启动了");
18
	}
19
}

现在我们来实现一个简单工厂类:

1
public class AutoFactory{
2
	//生产汽车
3
	public Auto proudce(String name){
4
		if("car".equals(name)){
5
			return new Car();
6
		}else if("bus".equals(name)){
7
			return new Bus();
8
		}
9
	}
10
}

简单工厂将创建对象的逻辑封装起来(Unsteady),将实现隐藏起来有很多好处,在工厂类中你可以添加所需的生产成品的逻辑代码。试想一下,如果一个类的new改动了(new可能涉及到很多参数),那需要在每个实现的地方都去改动,这是一件多么可怕的事情。
但是问题来了,简单工厂不符合 开放封闭 原则(对扩展开放,对修改关闭),当需要增加汽车的种类的时候,必须要修改 produce 的代码。

所谓对扩展开放,对修改关闭的含义,简而言之就是,当增加新的功能的时候,不需要通过修改原有的代码来实现(即原有的代码都是有价值的),而只是增加新的代码,代码具有向后兼容性。

工厂方法模式 | Factory Method Pattern

产品经理刚接到一个电话,工厂要扩大规模了,现在要生产卡车(Trunk)了,你稍加思索,太简单了,只需要新增一个 Trunk 类,并在简单工厂类AutoFactory 的 produce 函数里增加一个判断分支就可以了。

1
public class Trunk implements Auto{
2
	@Override
3
	public void drive(){
4
		System.out.println("Trunk start drive...");
5
	}
6
}
1
public class AutoFactory{
2
	public Auto proudce(String name){
3
			if("car".equals(name)){
4
				return new Car();
5
			}else if("bus".equals(name)){
6
				return new Bus();
7
			} // 增加新的分支来解决需求
8
			else if("Trunk".equals(name)){  
9
				return new Trunk();
10
			}
11
	 }
12
}

为什么我们要对修改关闭?
这里不针对工厂模式。当新的需求增加,如果我们需要的是去修改原有的代码,这是很不利的,一则改动可能很大,二则,其实你并不知道哪些地方需要改动(这里不仅针对工厂模式处理的问题),所以你可能会浏览整个的代码,而忐忑不安地去改动,需求的不可预料性,让你的代码很不稳定,很有可能在未来地某个时间节点遇到问题(源于你没有发现的需要改动的地方)。所以,在我们设计代码的时候,就应该将代码设计成扩展类型的,而非修改型的,这样即便出了问题,也仅仅是一些 easy case,你也很容将应该扩展的部分补齐,而不是整个系统的崩溃。

聊完了这个设计原则,我们再来看看简单工厂 (simple factory),它把对象的创建放在一个工厂类中,通过参数来创建不同的对象,缺点就是每添加一种类型的对象,就要对原有代码进行修改,虽然仅仅是添加一个 switch-case 分支,但依然不符合 “不改代码” 的原则。

那我们有什么办法来解决这个问题呢?
我们如何想不修改原来的代码似乎很难,因为工厂在那呢,我们要生成肯定要用到工厂,问题似乎陷入僵局,但是转念一想,如果不能修改原有的工厂,那我们可以将抽象层次提高,将工厂抽象成一个接口,当抽象层次提高的时候,以前的工厂其实就算是底层了,而底层的东西是可以多种实现的,即便工厂也不例外,我们可以新建新的工厂的实现来生产我们要的东西,来避免对原有工厂进行修改。

我们首先设计一个通用的工厂接口

1
public interface IAutoFactory{
2
	//生产汽车 ,name也是非必要的,不过可以对一开始的默认的几个产品有效果
3
	public Auto produce(String name);
4
}
5
6
// 原有的工厂类也不需要拆分了,那部分仍可沿用默认的 string 传参方法实现就可以了,当然也可以拆分哈

卡车工厂

1
public class TrunkAutoFactory implements IAutoFactory{
2
	@Override
3
	public Auto produce(String name){
4
		return new Trunk();
5
	}
6
}

抽象本就是一个很抽象的词,他和封装是对应的,有封装就有抽象,它门的作用就是隐藏细节,提供通用接口。所以,其实设计模式也是对应于这一OO的核心思想。还记得看到过一句话。

如果一个问题解决不了,那就给它加一层抽象

IAutoFactory就是对 factory 的一种抽象,使得 Factory 的实现也可变,更加灵活。

使用卡车工厂:

1
IAutoFactory factory = new TrunkAutoFactory();
2
Auto car = factory.produce(null);
3
car.drive(;)

微信图片_20191227223011.png

抽象工厂模式 | Abstract Factory

仅仅是工厂方法的复杂化,保存了多个 new,大工程才用得上。
我们继续对汽车工厂,接下来工厂要扩大规模,开始涉足配件(上层决定涉足汽车大灯业务,针对已有车型生产前大灯)。

那我们该怎么做呢?
和车一样,定义灯的基类(或者接口),定义灯的工厂基类(接口)。然后定义不同的大灯工厂么?

其实我们没必要,我们可以在原来的工厂(IAutoFactory)里面进行扩展,增加一个生产灯的方法即可。

1
public interface IAutoFactory{
2
	//生产汽车
3
	public Auto produce();
4
	
5
	//生产大灯
6
	public Light produceLight;
7
}

从工厂模式过渡到抽象工厂模式,并不需要太大的改动,只需要在原有的工厂中增加一些方法即可。(可是这不是不符合对修改封闭么)
抽象工厂模式中,我们可以定义实现不止一个接口,一个工厂也可以生产不只一个产品类,抽象工厂较好的实现了 “开放-封闭” 原则,是三个模式中较为抽象,并具一般性的模式
abstract.jpg

实际上,抽象工厂模式也存在缺点,可能细心的你发现了,在抽象工厂模式下,增加新的产品族很容易,但是增加新的产品结构则很难,需要改动 IFactory ,以及每个实现的 FactoryImpl.

抽象工厂模式的这种性质被称为 “开闭原则”的倾斜性 . 正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。

Thanks:

  1. Java设计模式之工厂模式
  2. 为什么要用工厂模式-知乎
CATALOG
  1. 1. 简单工厂模式 | simple Factory Pattern
  2. 2. 工厂方法模式 | Factory Method Pattern
  3. 3. 抽象工厂模式 | Abstract Factory
  4. 4. Thanks: