首页 > 解决方案 > 为什么我们已经有了具体工厂,还需要抽象工厂

问题描述

这是文章

https://www.codeproject.com/Articles/328373/Understanding-and-Implementing-Abstract-Factory-Pa

我不知道为什么我们需要有一个抽象工厂IPhoneFactory,在客户端,我们不能做某事吗

class PhoneTypeChecker
{
   ISmart sam;
   IDumb htc;
   //IPhoneFactory factory;    get rid of this abstract factory
   MANUFACTURERS manu;

   ...
   public void CheckProducts()
    {
        switch (manu)
        {
            case MANUFACTURERS.SAMSUNG:
                Console.WriteLine(new SamsungFactory().GetSmart().Name()); 
                Console.WriteLine(new SamsungFactory().GetDumb().Name());               
                break;
            case MANUFACTURERS.HTC:
                Console.WriteLine(new HTCFactory().GetSmart().Name());  
                Console.WriteLine(new HTCFactory().GetDumb().Name());               
                break;
            case MANUFACTURERS.NOKIA:
                Console.WriteLine(new NokiaFactory().GetSmart().Name()); 
                Console.WriteLine(new NokiaFactory().GetDumb().Name());                
                break;
        }

        Console.WriteLine(manu.ToString() + ":\nSmart Phone: " + 
        factory.GetSmart().Name() + "\nDumb Phone: " + factory.GetDumb().Name());
    }
}

我唯一能想到使用IPhoneFactory的是我们需要从具体工厂类中获取两种不同的类型(Smart 和 Dumb),但它只会节省一些击键,不是吗?

标签: c#.netdesign-patterns

解决方案


您链接到的文章存在一个核心问题,这意味着它没有给出一个很好的例子来说明如何使用抽象工厂来使应用程序更具可扩展性:

问题是,两者enum Manufacturersclass PhoneTypeChecker.CheckProducts必须了解所有类型的制造商。这几乎完全违背了抽象工厂模式的目的(除非您将其解释CheckProducts()为“抽象工厂”方法——在这种情况下,欢迎来到 Enterprise Java hell

...但是如果我们对构图进行一些调整,优势就会变得明显。

首先,删除enum MANUFACTURER(无论如何都不应该大写)并将CheckProducts其替换为IManufacturerRepository.

  • 重要提示:如果您实际上正在编写与 ORM 接口的真实代码,那么不要创建自己的存储库类型,因为您的 ORM 为您提供了存储库。对于实体框架尤其如此。尤其要避免“通用存储库”反模式。

IManufacturerRepository应该只有一个实现(或用于测试/模拟的单独实现)。

public interface IManufacturerRepository
{
    List<IManufacturer> GetManufacturers();
}

public interface IManufacturer
{
    String        Name         { get; }
    IPhoneFactory PhoneFactory { get; } // <-- this is the abstract factory
}

然后PhoneTypeChecker可以接收IManufacturervia 构造函数参数:

  • 我注意到这个例子很愚蠢,因为内部没有有意义的实例状态突变,PhoneTypeChecker也不代表PhoneTypeChecker由抽象类型或接口表示的任何类型的操作 - 这最好通过制作CheckProducts一个static接受IManufacturer作为参数的方法来完成)。
  • 在原始文章中,该CheckProducts方法确实改变了PhoneTypeChecker的状态(通过设置factory, sam, htc),但这是一个糟糕的设计- 因为它实际上只是在初始化自身,这应该在构造函数中完成 - 但我离题了。

反正:

public class PhoneTypeChecker
{
    private readonly IManufacturer mfg;

    public PhoneTypeChecker( IManufacturer mfg )
    {
        this.mfg = mfg ?? throw new ArgumentNullExceptio(nameof(mfg));
    }

    public void CheckProducts()
    {
        Console.WriteLine( this.mfg.Name + ":\nSmart Phone: " + 
    this.mfg.PhoneFactory.GetSmart().Name() + "\nDumb Phone: " + this.mfg.PhoneFactory.GetDumb().Name() );
    }
}

然后该Main方法将重做为:

static void Main(string[] args)
{
    IManufacturerRepository repo = new ActualIManufacturerRepository();
    List<IManufacturer> mfgs = repo.GetManufacturers();

    PhoneTypeChecker checker = new PhoneTypeChecker( mfgs.Single( m => m.Name == "Samsung" ) );

    checker.CheckProducts();

    Console.ReadLine();

    checker = new PhoneTypeChecker( mfgs.Single( m => m.Name == "HTC" ) );

    checker.CheckProducts();
    Console.ReadLine();

    checker = new PhoneTypeChecker( mfgs.Single( m => m.Name == "Nokia" ) );

    checker.CheckProducts();
    Console.Read();
}

这个例子还有其他问题——所以不要拿这个例子来说明如何编写结构良好或生产质量的代码。

简而言之:只有 的实现才IManufacturerRepository应该知道 的实际实现IManufacturer(并且只有 的实现才IManufacturer应该知道 的实现IPhoneFactory)。


推荐阅读