抽象工厂模式

47
  1. C#中抽象工厂模式的好例子是什么?
  2. 抽象工厂模式在C#中有哪些优点?
  3. 如何在C#中使用泛型与抽象工厂模式?
  4. 如何使用抽象工厂模式进行单元测试?

1
http://www.dofactory.com/Patterns/PatternAbstract.aspx - Habib
2
问题应该更具体。看起来,你不想找关于你的主题的信息,只是想让别人解释这个主题给你听。 - Larry Foobar
此外,您还可以对比抽象工厂模式中的其他模式进行研究。http://www.dofactory.com/Patterns/Patterns.aspx - NoWar
2
可能是为什么我们需要抽象工厂设计模式?的重复问题。 - nawfal
1个回答

143
首先,我建议您阅读有关抽象工厂模式的文章,例如这里。现在我将尝试解释为什么要使用此模式。
通常,如果您使用工厂模式,您将在工厂中创建对象。当您拥有给定类(或类)的多个实现时,问题就会出现。现在,这些多个实现已分组。当您希望将对象的创建按组分组时,您将使用抽象工厂模式
好的,上面的解释可能不是完全清晰的,因此我将举个例子。
假设您有一个数据代理的类库。数据代理提供了访问和存储不同数据的方法。当然,有多种存储数据的方式。例如:在数据库中、在XML文件中、通过服务。对于每种可能的方式,您都想要数据代理。现在的问题是,您不希望某人将DataAgentA用于XML文件,并将DataAgentB用于数据库(假设我们有实体A和B)。用户应使用仅一个存储引擎。
让我向您介绍抽象工厂模式。
您将确保用户无法直接实例化您的数据代理,而是必须从工厂中获取这些数据代理。(另一个优点是,当您使用例如数据库(EF)时,您可以进行内部布线以确保您的数据代理使用相同的上下文等。)我们如何实现这一点?我们将我们的数据代理构造函数设置为“internal”。除此之外,我们为每个存储引擎创建不同的工厂。现在,由于这些工厂都是相同的,因此我们还有这些接口(就像我们的数据代理一样,因为它们都必须执行相同的任务!)。
下面是我们的接口。基本上,这就是工厂模式,但现在我们正在谈论接口而不是
public interface IAgentA 
{
    // Add some methods here!
}

public interface IAgentB
{
    // Add some methods here!
}

public interface IAgentFactory
{
    IAgentA CreateAgentA();
    IAgentB CreateAgentB();
}

现在针对这两个代理,我们有两种可能的实现方式,一种是用于 XML 存储,另一种是用于数据库存储(再次说明:这只是一个示例,你可以拥有任意多个实现类型)。这些实现看起来像这样(见下文)。请注意,我将构造函数设置为 internal !这对于接下来的代码块非常重要。

public class AgentA_Xml : IAgentA
{
    internal AgentA_Xml()
    { /* Construction here */}

    // IAgentA method implementations
}

public class AgentB_Xml : IAgentB
{
    internal AgentB_Xml()
    { /* Construction here */}

    // IAgentB method implementations
}


public class AgentA_Database : IAgentA
{
    internal AgentA_Database()
    { /* Construction here */}

    // IAgentA method implementations
}

public class AgentB_Database : IAgentB
{
    internal AgentB_Database()
    { /* Construction here */}

    // IAgentB method implementations
}

由于构造函数是内部的,所以你无法在程序集之外实例化这些类,而通常这种情况下你会这样做。现在我们必须创建我们自己的工厂。

public class XMLAgentFactory : IAgentFactory
{
    public IAgentA CreateAgentA()
    {
        return new AgentA_Xml();
    }

    public IAgentB CreateAgentB()
    {
        return new AgentB_Xml();
    }
}


public class DatabaseAgentFactory : IAgentFactory
{
    public IAgentA CreateAgentA()
    {
        return new AgentA_Database();
    }

    public IAgentB CreateAgentB()
    {
        return new AgentB_Database();
    }
}
由于两个工厂都实现了IAgentFactory接口,用户可以轻松地更改AgentFactory的实现(如果他想使用不同的存储引擎),而无需更改编写的任何其他代码(针对代理),只要他针对接口进行了编程(显然)。
以上解释希望回答您的第一个和第二个问题。
如何使用C#泛型实现抽象工厂模式?您仍然可以使用泛型,这在使用抽象工厂模式时不会有任何变化。当然,您将需要创建通用工厂方法(create方法),但这不应该成为任何问题。
回答您的第四个问题:如何使用抽象工厂模式进行单元测试?
与测试其他任何类一样。唯一不同的是,由于您可能还想测试类的构造函数(或其他内部方法),因此需要使内部构造函数(方法)对您的单元测试项目可见(并且您不希望将internal改为public)。这可以通过将以下行添加到您的项目的AssemblyInfo.cs文件中来轻松完成(其中包含您的工厂和类):
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("My.UnitTest.Namespace")]

你可以在MSDN上找到有关InternalsVisibleTo属性的更多信息(和备注)。

希望这种回答可以解决你的问题。


4
这个答案是那些人们有时会谈论的隐藏宝石之一。非常清晰地解释了。谢谢。 - Piotr Kula

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接