C# 什么时候应该使用抽象工厂模式

C# 什么时候应该使用抽象工厂模式,c#,oop,design-patterns,C#,Oop,Design Patterns,我甚至读过关于抽象工厂模式的书,但即使我理解它,我也不知道什么时候使用它。模式的定义是用来抽象对象实例化,但我不太清楚为什么我需要这样做,为什么我应该关心对象实例化,以及为什么(或何时)抽象它很重要。让我们假设您正在编写一个需要与数据库对话的应用程序。您可以有一个数据库类,它像工厂一样使用数据库.CreateCommand方法创建命令。如果需要使用不同的数据库引擎,则需要为每个引擎实现不同的数据库 您可能不知道在运行时需要哪个命令工厂,因此创建了一个DatabaseManager类,该类具有返回

我甚至读过关于抽象工厂模式的书,但即使我理解它,我也不知道什么时候使用它。模式的定义是用来抽象对象实例化,但我不太清楚为什么我需要这样做,为什么我应该关心对象实例化,以及为什么(或何时)抽象它很重要。

让我们假设您正在编写一个需要与数据库对话的应用程序。您可以有一个
数据库
类,它像工厂一样使用
数据库.CreateCommand
方法创建命令。如果需要使用不同的数据库引擎,则需要为每个引擎实现不同的
数据库

您可能不知道在运行时需要哪个命令工厂,因此创建了一个
DatabaseManager
类,该类具有返回特定类型数据库的
DatabaseManager.GetDatabase(databaseType)
函数
DatabaseType
可以来自配置文件,以便轻松更改

在本例中,每个
数据库
将是一个常规工厂,而
数据库管理器
将是一个抽象工厂。这是一个创造其他工厂的工厂

所以本质上你可以这样做:

Dim sqlCommand as ICommand = DatabaseManager.GetDatabase("MsSQL").CreateCommand


该模式的主要好处是,它通过将对象创建与对象使用分离,实现了干净且可重用的代码

不幸的是,我手头没有一个C#示例,但我本周遇到的一个示例用例是设计一个需要打开套接字的对象。这是在Android上,某些版本错误地实现了SSL/TLS

为了解决这个问题,在这些版本的Android上,我们必须对SSL环境进行大量定制,以便成功地与后端通信。通过使用抽象工厂,我们可以编写套接字客户机,这样它就不需要知道任何混乱的细节——它只需要从一个工厂获取套接字

例如:

// this is pretty gross, but what can you do
public class SocketFactorySupplier implements Supplier<SSLSocketFactory> {
  @Override public SSLSocketFactory get() {
    if (androidVersion >= 2.1 && androidVersion <= 2.6) {
      return createShiftyWorkaround();
    } else {
      return getDefaultSocketFactory();
    }
  }

  // here are ~500 lines of SSL code
}

...

public class NetworkClient {
  private final Supplier<SSLSocketFactory> supplier;
  private Socket socket;

  public NetworkClient(Supplier<SSLSocketFactory> supplier) {
    this.supplier = supplier;
  }

  public void connect() {
    socket = supplier.get().createSocket();
    socket.connect();

    // code that doesn't care about SSL at all and is simpler for it
  }
}
//这很恶心,但是你能做什么呢
公共类SocketFactorySupplier实现供应商{
@重写公共SSLSocketFactory get(){

如果(androidVersion>=2.1&&androidVersion工厂的主要优点是,它们允许调用对象保持简单,并且在向应用程序添加新功能时不必更改

假设您有一个简单的购物车UI,并且您的公司有一个产品,这段非常具体的代码一开始会很好地工作

public class MyShoppingCartUI()
{
    private List<IProduct> _productsInCart = new List<IProduct>();

    public void ClickAddProductButton()
    {
        IProduct product = new ProductOne();
        _productsInCart.Add(product);
    }
}
公共类MyShoppingCartUI()
{
私有列表_productsInCart=新列表();
公共作废ClickAddProductButton()
{
IPProduct product=新ProductOne();
_productsInCart.Add(产品);
}
}
但是,当您的公司添加新产品时,您必须更改UI代码以直接引用新产品。因此,您必须将UI代码更改为:

public class MyShoppingCartUI()
{
    private List<IProduct> _productsInCart = new List<IProduct>();

    public void ClickAddProductOneButton()
    {
        IProduct product = new ProductOne();
        _productsInCart.Add(product);
    }

    public void ClickAddProductTwoButton()
    {
        IProduct product = new ProductTwo();
        _productsInCart.Add(product);
    }
}
公共类MyShoppingCartUI()
{
私有列表_productsInCart=新列表();
公共作废ClickAddProductOneButton()
{
IPProduct product=新ProductOne();
_productsInCart.Add(产品);
}
公共作废ClickAddProductTwoButton()
{
i产品产品=新产品二();
_productsInCart.Add(产品);
}
}
正如你所看到的,每次你有一个新产品,你必须添加更多的代码,你的UI类变得越来越大。为什么你的购物车UI要依赖于什么产品是可用的

这可以通过工厂解决。以下是一个解耦代码示例:

public class ProductFactory()
{
    public IProduct Create(string productName)
    {
        if (productName == "Product1")
            return new ProductOne();
        else if (productName == "Product2")
            return new ProductTwo();            
    }
}

public class MyShoppingCartUI()
{
    private ProductFactory _factory = new ProductFactory();
    private List<IProduct> _productsInCart = new List<IProduct>();

    public void AddItem(string productName)
    {
        IProduct product = _factory.Create(productName);
        _productsInCart.Add(product);
    }
}
公共类ProductFactory()
{
公共IPProduct创建(字符串产品名)
{
如果(产品名称==“产品1”)
返回新ProductOne();
如果(产品名称==“产品2”)
返回新产品2();
}
}
公共类MyShoppingCartUI()
{
private ProductFactory _factory=new ProductFactory();
私有列表_productsInCart=新列表();
公共void附加项(字符串productName)
{
IPProduct product=\u factory.Create(产品名称);
_productsInCart.Add(产品);
}
}
这样,无论您添加多少产品,您都不必更改UI代码,因为它不关心您生产的产品。您可以将所有控件与产品名称或ID列表绑定,工厂将为您提供这些对象


另一个优点是,应用程序的其余部分也可以使用Factory来获取其产品对象,因此您不必单独维护创建产品对象的代码。Factory类中只有1-2行新行将永远覆盖您,无论您的应用程序有多大。

抽象Factory模式是一种不需要维护的模式对于学术性的例子,我更喜欢考虑设计模式,而不是基于它们是如何实现的,而是基于它们要解决的问题。在这种情况下,抽象工厂模式是针对一种情况,即您有一个工厂需要根据编译时不可用的信息进行更改。要以另一种方式陈述,如果您希望您的工厂根据只有在运行时才可用的信息创建不同类型的产品,这将非常有用。功能上的差异包含在工厂产品本身中,而不是使用它们的代码中。

我所见过的最好的学术例子是单元测试。当你测试你工厂产品的消费者时,你通常不想要产品的全部重量级功能,而是想要简单的填充,这样你可以隔离消费者的逻辑。对于工厂模式,这不起作用。对于抽象工厂模式rn,这是微不足道的。与通常使用的单一工厂不同,您有两个工厂——一个生产虚拟对象,另一个生产真实产品。
public class ProductFactory()
{
    public IProduct Create(string productName)
    {
        if (productName == "Product1")
            return new ProductOne();
        else if (productName == "Product2")
            return new ProductTwo();            
    }
}

public class MyShoppingCartUI()
{
    private ProductFactory _factory = new ProductFactory();
    private List<IProduct> _productsInCart = new List<IProduct>();

    public void AddItem(string productName)
    {
        IProduct product = _factory.Create(productName);
        _productsInCart.Add(product);
    }
}