菜狗U3D程序猿的经验积累——抽象工厂模式

249 阅读6分钟

意图

抽象工厂模式时一种创建型设计模式,它能创建一系列相关的对象,而无需指定具体类。

问题

假设你正在开发一款家具商店模拟器。你的代码中包括一些类,用于表示:

1.一系列相关的产品,例如椅子,沙发和咖啡桌。

2.系列产品的不同变体。例如,你可以使用现代、维多利亚、装饰风艺术等风格生成椅子、沙发和咖啡桌。

你需要设法单独生成每件家居对象,这样才能确保其风格一致。如果顾客收到的家具风格不一样,他们可不会开心。

此外,你也不希望在添加新产品或新风格时修改已有的代码。家具供应商对于产品目录的更新非常频繁,你不会想在每次更新时都去修改核心代码的。

解决方案

首先,抽象工厂模式建议为系列中的每件产品明确声明接口(例如椅子,沙发和咖啡桌)。然后,确保所有产品变体都继承这些接口。例如,所有风格的椅子都实现椅子接口;所有风格的咖啡桌都实现咖啡桌接口,以此类推。

接下来,我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。例如,创建椅子,创建沙发和创建咖啡桌。这些方法必须返回抽象产品类型,即我们之前抽取的那些接口:椅子,沙发和咖啡桌等等。

那么该如何处理产品变体呢?对于系列产品的每个变体,我们都将基于抽象工厂接口创建不同的工厂类。每个工厂类都只能返回特定类别的产品,例如,现代家具工厂只能创建现代椅子,现代沙发和现代咖啡桌对象。

客户端代码可以通过相应的抽象接口调用工厂和产品类。你无需改变实际客户端代码,就能更改传递给客户端的工厂类,也能更改客户端代码接收的产品变体。

假设客户端想要工厂创建一把椅子。客户端无需了解工厂类,也不用管工厂类创建出的椅子类型。无论时现代风格,还是维多利亚风格的椅子,对于客户端来说没有分别,它只需调用抽象椅子接口就可以了。这样一来,客户端只需知道椅子以某种方式实现了坐下的方法就足够了。此外,无论工厂返回的时何种椅子变体,它都会和由同一工厂对象创建的沙发或咖啡桌风格一致。

最后一点说明:如果客户端仅接触抽象接口,那么谁来创建实际的工厂对象呢?一般情况下,应用程序会在初始化阶段创建具体工厂对象。而在此之前,应用程序必须根据配置文件或环境设定选择工厂类别。

抽象工厂模式适合应用场景

如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂。

抽象工厂为你提供了一个接口,可用于创建每个系列产品的对象。只要代码通过该接口创建对象,那么你就不会生成与应用程序已生成的产品类型不一致的产品。

如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用工厂模式。

在设计良好的程序中,每个类仅负责一件事。如果一个类与多种类型产品交互,就可以考虑将工厂方法抽取到独立的工厂类或具备完整的抽象工厂类中。

实现方式

1.以不同的产品类型与产品变体为维度绘制矩阵。

2.为所有产品声明抽象产品接口。然后让所有具体产品类实现这些接口。

3.声明抽象工厂接口,并且在接口中为所有抽象产品提供一组构建方法。

4.为每种产品变体实现一个具体工厂类。

5.在应用程序中开发初始化代码。该代码根据应用程序配置或当前环境,对特定具体工厂类进行初始化。然后将该工厂对象传递给所有需要创建产品的类。

6.找出代码中所有对产品构造函数的直接调用,将其替换为工厂对象中相应构建方法的调用。

抽象工厂模式优缺点

优点:

你可以确保同一工厂生成的产品相匹配。

你可以避免客户端和具体产品代码的耦合。

单一职责原则。你可以将产品生成代码抽取到同一位置,使得代码易于维护。

开闭原则。向应用程序中引入新产品变体时,你无需修改客户端代码。

缺点:

由于采用该模式需要向应用中引入众多接口和类,代码可能会比之前更加复杂。

与其他模式的关系

· 在许多设计工作的初期都会使用工厂方法模式,随后演化为使用抽象工厂模式、原型模式或建造者模式。

· 建造者模式重点关注如何分布生成复杂对象 。抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品,建造者则允许你在获取产品前执行一些额外构造步骤。

· 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。

· 当只需对客户端代码隐藏子系统创建对象的方式时,你可以使用抽象工厂来代替外观模式。

· 你可以将抽象工厂和桥接模式搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。

· 抽象工厂、建造者和原型都可以用单例来实现。

代码

using System;

public interface IAbstractFactory
{
 IAbstractProductA CreateProductA();
 IAbstractProductB CreateProdictB();
}

class ConcreteFactory1:IAbstracFactory
{
 public IAbstracProductA CreateProductA()
 {
 	return new ConcreteProductA1();
 }
 public IAbstractProductB()
 {
 	return new ConreteProductB1();
 
 }
 }
 class ConcreteFactory2:IAbstractFactory
 {
   public IAbstracProductA CreateProductA()
   {
      return new ConcreteProductA2();
   }
   public IAbstractProductB()
   {
      return new ConreteProductB2();
   }
  }
  public interface IAbstactProductA
  {  
     string UsefulFunctionA();
  }
  class ConcreteProductA1:IAbstracProductA
  {
    public string UsefulFunctionA()
    { 
    	return "The result of the Product A1.";
     }
  }
  class ConcreteProductB1:IAbstractProductB
  {
   public string UsefulFunctionB()
   {
     return "The result  of the product B1.";
   }
  public string AnotherUsefulFunctionB(IAbstractProductA collaborator)
  {
    var result =collaborator.UsefulFunctionA();
    
    return $"The result of the B1 collaborating with the ({result})";
   }
     
}
class  ConcreteProductB2:IAbstractProductB()
{
 public string UsefulFunctionB()
 {
   return "The result of the product B2.";
 }
 
 public string AnotherUsefulFunctionB(IAbstractProductA collaborator)
 {
  var result = collarator.UsefulFunctionA();
  
  return $"The result of the B2 collaborating with the ({result})";
}
}