工厂方法模式(创建型模式)
模式概述
这种设计模式在父类中提供一种创建对象的方法,让子类决定要实例化的对象类型。
就像工厂可以根据客户需求生产出产品那样,工厂方法
因此而得名。
问题
假设目前公司正在开发一款知识付费应用,第一版只能用微信支付。但随着用户越来越多,很多用户要求能用支付宝支付。而且今后可能还需要支持余额支付、优惠券支付、积分支付和信用卡支付。
如果每增加一种支付方式就要大改一次代码,那简直是太麻烦了。
方案
这时候就可以用工厂方法模式来解决。
工厂方法模式用一个称为工厂的类代替对对象的直接创建和调用,工厂方法创建的对象也被称为产品
。
而工厂
对产品
的唯一要求是它们都必须实现一个公共的接口,这也符合现实世界的情况,毕竟,罐头厂生产的都是罐头,机器只能识别出罐头模子。
在刚才那个支付的例子中,不管是微信支付、支付宝支付还是今后更多的支付,都要先实现支付接口,只有这样,子类才能返回不同类型的具体产品。

这样一来,任何客户端都无需了解不同子类返回对象的差别,就像用户并不关心不同支付方式的具体技术实现到底有什么区别一样,用户关心的只是支付
而已。
结构

实现
Java
/**
* 产品接口
*
*/
public interface Payment {
void pay();
}
/**
* 一个具体产品
*
*/
public class WeixinPayment implements Payment {
public void pay() {
System.out.println("调用微信支付");
}
}
/**
* 另一个具体产品
*
*/
public class AlipayPayment implements Payment {
public void pay() {
System.out.println("调用支付宝支付");
}
}
/**
* 基础创建者
*
*/
public abstract class Creator {
public void pay() {
Payment payment = createPayment();
payment.pay();
}
/**
* 抽象方法,将被子类覆盖
*/
public abstract Payment createPayment();
}
/**
* 具体创建者
*
*/
public class WeixinCreator extends Creator {
@Override
public Payment createPayment() {
return new WeixinPayment();
}
}
/**
* 另一个具体创建者
*
*/
public class AlipayCreator extends Creator {
@Override
public Payment createPayment() {
return new AlipayPayment();
}
}
/**
* 客户端代码
*
*/
public class Client {
private static Creator creator;
public static void main(String[] args) {
configure();
runBusinessLogic();
}
// 具体工厂的选择通常取决于配置项
static void configure() {
if (System.getProperty("payment").equals("Weixin")) {
creator = new WeixinCreator();
} else {
creator = new AlipayCreator();
}
}
// 所有的客户端代码都应该通过抽象接口与工厂交互,这样它就不在乎具体是什么工厂,也不在乎返回什么样的产品
static void runBusinessLogic() {
creator.pay();
}
}
Go
package main
import "fmt"
/**
* 罐头工厂接口
*
*/
type ICan interface {
setName(name string)
setCapacity(capacity int)
getName() string
getCapacity() int
}
/**
* 具体产品
*
*/
type Can struct {
name string
capacity int
}
func (c *Can) setName(name string) {
c.name = name
}
func (c *Can) getName() string {
return c.name
}
func (c *Can) setCapacity(capacity int) {
c.capacity = capacity
}
func (c *Can) getCapacity() int {
return c.capacity
}
/**
* 苹果罐头
*
*/
type AppleCan struct {
Can
}
func newAppleCan() ICan {
return &AppleCan{
Can: Can{
name: "苹果罐头",
capacity: 8,
},
}
}
/**
* 梨子罐头
*
*/
type PearCan struct {
Can
}
func newPearCan() ICan {
return &PearCan{
Can: Can{
name: "梨子罐头",
capacity: 7,
},
}
}
/**
* 罐头工厂
*
*/
func getCan(canType string) (ICan, error) {
if canType == "apple" {
return newAppleCan(), nil
}
if canType == "pear" {
return newPearCan(), nil
}
return nil, fmt.Errorf("错误的罐头类型")
}
/**
* 客户端代码
*
*/
func printDetails(g IGun) {
fmt.Printf("罐头:%s", g.getName())
fmt.Println()
fmt.Printf("容量:%d", g.getCapacity())
fmt.Println()
}
func main() {
apple, _ := getCan("apple")
pear, _ := getCan("pear")
printDetails(apple)
printDetails(pear)
}
适用场景
当无法预知对象的确切类型及其依赖关系,或者将来会有许多相似
产品
类型的扩展时,就可以使用工厂方法模式。如果希望未来能够扩展更多的组件时,可以使用工厂方法模式。例如一些UI框架中的组件。
如果希望复用现有对象以节省资源,而非每次都重新创建对象时,可以使用工厂方法模式。例如,数据库连接池、线程池。
优缺点
工厂方法模式的优点。
可以避免创建者和具体产品之间的紧密耦合。
根据
单一职责原则
,将产品创建代码放在程序的单一位置会使得代码更容易维护。客户端代码不用做任何修改就可以在程序中引入新的产品类型。
工厂方法模式的缺点。
- 需要引入额外的子类,导致代码变得复杂,不易读懂。
相关性
工厂方法模式
可以演化为抽象工厂模式
、原型模式
和构建器
模式。抽象工厂模式
通常基于一组工厂方法, 也可以使用原型模式
来生成这些类的方法。可以同时通过
工厂方法模式
和迭代器模式
来让子类返回不同的迭代器。工厂方法模式
是模板方法模式
的一种特殊形式,而且工厂方法可以作为模板方法中的子步骤。
与其他“工厂”的比较
简单工厂模式
简单工厂模式是《O'Reilly: Head First设计模式》 中定义的一种模式。
正如其名,简单工厂模式是工厂模式的简化版,它通过包含大量条件语句的工厂方法,根据不同的条件来选择对应的产品,并将其初始化后返回。
简单工厂模式通常没有子类。,但如果从简单工厂中抽取出子类,那么它基本就等同于经典的工厂方法模式了。
一个简单工厂模式的例子。
public class PaymentSimpleFactory {
public static Object create(String type) throws Exception {
switch (type) {
case "weixinpay": return new WeixinPay();
case "alipay": return new Alipay();
case "balance": return new Balance();
case "point": return new Point();
case "creditcard": return new CreditCard();
default:
throw new Exception("支付方式错误");
}
}
}
抽象工厂模式
抽象工厂模式
和工厂方法模式
同为创建型设计模式,它能创建一系列相关或相互依赖的对象
,而无需指定其具体类。
对于一系列相关或相互依赖的对象
的描述如下。
汽车 + 内燃机 + 方向盘。
飞机 + 喷气式发动机 + 操纵杆。
如果程序中不涉及这样一系列相关或相互依赖的对象
的话,就是不需要抽象工厂模式。
抽象工厂模式,以及在简单工厂模式
加入抽象方法,它们确实和工厂方法模式非常像,但只要仔细比较它们的类图就知道,这三者根本不是一回事。
感谢支持
更多内容,请移步《超级个体》。