工厂方法模式

# 分类

创建型模式

# 定义

工厂方法模式(Factory Methond Pattern) 这种模式提供了一种创建对象的方式,在工厂方法模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且通过使用一个共同的接口来指向新创建的对象。

# 意图

工厂方法模式定义一个创建对象的接口,让其子类自己决定实例化哪个具体类,工厂方法模式使其创建过程延迟到子类进行。

# 应用场景

  • 客户程序需要隔离它与需要创建的具体类型间的耦合关系。
  • 很多情况下,客户程序在开发过程中还无法预知生产环境中实际要提供给客户程序创建的具体类型
  • 将创建具体实例工作隔离在客户程序之外,客户程序仅需要执行自己的业务逻辑,把这部分职责交给外部对象完成。
  • 如果目标对象虽然可以统一抽象为某个抽象类型,但它们的继承关系太过复杂,层次性比较复杂,这时同样可以通过工厂方法解决这个问题。

应用案例

  • 你需要一辆汽车,可以直接从工厂里提货,而不是去管这辆车是怎么造出来的,以及这个车里面的具体实现。
  • 数据库访问:当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  • 设计一个连接服务器的框架,需要三个协议,“POP3”,"IMAP","HTTP",可以把这三个作为产品类,共同实现一个接口。

# 角色与结构图

  • Product:抽象产品角色 工厂要加工的对象所具有的抽象特征实体。
  • Concrete Product:具体产品角色 实现客户程序所需要的抽象特质的类型,它是工厂需要延迟实例的备选对象。
  • Factory:抽象工厂角色 定义一个工厂方法的默认实现,它返回抽象产品类型 Product。
  • ConcreteFactory:具体工厂角色 具体产品类型 Concrete Product的具体工厂,方法用来生成具体产品。
  • Client:客户程序 作为 Product 消费者的时候,抽象类在构造实例的时候到底将其"挂"在哪个实体类型由 Concrete Factory 决定。

下图解释了工厂方法模式中各角色的作用

工厂方法模式结构图

# 示例代码

//抽象产品类型
public interface IProduct
{
    // 约定的抽象产品所必须具有的特征
    string Name { get;}    
}
// 具体产品类型
public class ProductA : IProduct
{
    public string Name { get { return "A"; } }
}
public class ProductB : IProduct
{
    public string Name { get { return "B"; } }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 一个系列的工厂,生产一个系列的产品
public interface IFactory
{
    // 每个工厂所需要具有的工厂方法——创建产品
    IProduct Create();  
}
// A工厂创建A产品
public class FactoryA : IFactory
{
    public IProduct Create()
    {
        return new ProductA();
    }
}
// B工厂创建B产品
public class FactoryB : IFactory
{
    public IProduct Create()
    {
        return new ProductB();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 客户程序
class Client
{
    public static void Main(string[] args)
    {
        IFactory factory = new FactoryA();
        IProduct product = factory.Create();
        product.Use();
    }
}
1
2
3
4
5
6
7
8
9
10

# 优点

  • 一个调用者想创建一个对象,只要知道具体工厂的名称就可以了。
  • 扩展性高,如果想要增加一个产品,只要扩展一个工厂类一个产品类就可以。
  • 屏蔽产品的具体实现,调用者只关心产品的接口。

# 缺点

  • 每次增加一个产品时,都需要增加一个具体产品类一个具体工厂类,使得系统中类的个数成倍增长,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖,这并不是好事。

# 小结

  • 直观上客户程序可以把频繁变化的某个 Product 隔离在自己的视线之外,自身不会受到它的影响。

  • 工厂方法给我们设计应用一个很好的模式化new()思维,通过引入新的对象,在很大程度上解放了客户程序对具体类型的依赖,其方法就是将具体产品对象延迟构造到子类,但依赖关系是始终存在的,解放一个对象的时候等于把麻烦转嫁到另一个对象上。为此,工程中往往最后把推不掉的依赖关系推到配置文件上,也就是推到.NET Framework 上,这样工厂方法模式往往在项目中被演绎成“工厂方法 + 依赖注入 + 配置文件访问”的组合方式。

  • 主要解决?主要解决接口选择的问题。

  • 何时使用?我们明确地计划不同条件下创建不同实例时。

  • 如何解决?让其子类实现工厂接口,返回的也是一个抽象产品。

  • 关键代码?创建过程在其子类执行。

# 大结

耦合关系直接决定着软件面对变化时的行为。 模块与模块之间的紧耦合使得软件面对变化时,相关的模块都要随之更改。 模块与模块之间的松耦合使得软件面对变化时,一些模块更容易被替换或者更改,但其他模块保持不变。 软件的变化如果不大。使用紧耦合也能很好高效的工作,如果软件变化频繁,则需要使用设计模式的松耦合来组织。

软件系统的主逻辑应该是变动较小的,如果主逻辑都在剧烈变化,那么软件就相当不稳定。

动机:在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是他却拥有比较稳定的接口。 如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其他依赖该对象的对象”不随着需求改变而改变。 意图:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使得一个类的实例化延迟到子类。

# 一句话概括

一个工厂类根据创建与其对应的产品实例。比如:汽车工厂创建汽车。

上次更新: 2025/03/22, 13:47:44
最近更新
01
Git问题集合
01-29
02
安装 Nginx 服务器
01-25
03
安装 Docker 容器
01-25
更多文章>
×
×