[整理] 设计模式——工厂: 简单工厂、静态工厂、工厂方法、抽象工厂
要点
- 所有的工厂都是用来封装对象的创建
- 简单工厂, 虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户端程序从具体类解耦
- 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
- 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中。
- 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
- 工厂方法允许类将实例化延迟到子类进行。
- 抽象工厂创建相关的对象家族,而不需要依赖它们的具体类。
- 依赖倒置原则,指导我们避免依赖具体类型, 而要尽量依赖抽象。
- 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。
简单工厂
示例: 建立一个简单披萨工厂
class SimplePizzaFactory
{
public createPizza(string $type): Pizza
{
$pizza = null;
switch ($type) {
case "cheese":
$pizza = new CheesePizza();
break;
case "clam":
$pizza = new ClamPizza();
break;
case "veggie":
$pizza = new VeggiePizza();
break;
default:
throw new Exception("不支持的披萨类型");
}
return $pizza;
}
}
好处(以下的披萨指工厂生产的产品):
- 客户端(指调用方)不必知道要创建的具体披萨类名
- 披萨的创建从客户端代码中分离开,以后有实现变更时只需修改该类
静态工厂
利用静态方法定义一个“简单工厂”
好处:
- 不需要实例化工厂即可创建所需对象
坏处:
- 无法通过继承改变创建方法的行为
工厂方法 Factory Method
工厂方法模式: 通过让子类决定该创建的对象是什么, 来达到将对象创建的过程封装的目的.
正式定义: 工厂方法模式定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个. 工厂方法让类把实例化推迟到子类
参数化工厂方法: 根据传入的参数创建不同的对象.
工厂通常只生产一种对象, 不需要参数化.
↑ 工厂方法可能需要参数(也可能不需要)来指定所要的产品, 这两种形式都是有效的
abstract function factoryMethod(string $type): Product;
abstract
工厂方法是抽象的, 依赖子类来处理对象的创建Product
工厂方法必须返回一个产品。 超类中定义的方法, 通常使用到工厂方法的返回值。factoryMethod
工厂方法将客户(也就是超类中的代码)和实际创建具体产品的代码分隔开来
工厂方法的目的定义一个约定的方法,该方法用于创建一类对象,创建的具体对象由子类决定。
Eg.
- 约定的创建方法
LoggerFactory::createLogger
- 创建的一类对象
Logger
- 具体的子类1
StdoutLogger
由StdoutLoggerFactory
创建 - 具体的子类2
FileLogger
由FileLoggerFactory
创建
特点:
- 具体对象的创建由各子类构建
- 新增一种创建的对象时,必须配套一个专用的工厂(既是优点也是缺点)
与 简单工厂 的区别
简单工厂是将创建对象的过程延迟到工厂内部,但它有个问题:每次要新增创建的对象时必须去修改工厂类本身,这意味着这个工厂类很容易膨胀,同时这违反了开放封闭原则。
工厂方法则是将实例化的过程下沉到具体子工厂,需要新增创建的对象时,就新增一个对应的工厂。
UML 图
抽象工厂模式
定义: 抽象工厂模式提供一个接口(Interface), 用于创建具体或依赖对象的家族, 而不需要明确指定具体类.
抽象工厂允许客户使用抽象的接口来创建一组相关的产品, 而不需要知道(或关心)实际产出的具体产品是什么. 这样一来, 客户就从具体的产品中被解耦.
从抽象工厂中派生出具体工厂, 这些工厂生产相同的产品, 但是产品的实现不同.
抽象工厂与工厂方法?
抽象工厂的方法经常以工厂方法的方式实现.
- 抽象工厂的任务是定义一个创建一组产品的接口
- 这个接口内的每个方法都负责创建一个具体产品
- 利用实现抽象工厂的子类来提供这些具体产品的做法
所以, 在抽象工厂中利用工厂方法实现生产方法是相当自然的做法。
此处的"接口"是广义上的意思, 包含 interface/abstract class
工厂方法↓
抽象工厂↓