c# - 这是否遵循抽象工厂模式

标签 c# oop design-patterns

根据定义“设计模式:抽象工厂”。通知 IT。原始存档于 2009-10-23。检索于 2012-05-16,Object Creational -> Abstract Factory:.

Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes."

下面是我对抽象设计模式的尝试。这是我第一次接触工厂方法。谁能帮我解决这个问题。

enum ProductType
{
    BeautySoap = 1,
    DetergentSoap = 2,
    HairWax = 3,
    BodyWax = 4,
}
interface ISoap
{
    string Create(string name);

}
interface IWax
{
    string Create(string name);
}

public class HarWax : IWax
{
    public string Create(string name)
    {
        return string.Format("Hair Wax {0} created", name);
    }
}

public class BodyWax : IWax
{

    public string Create(string name)
    {
        return string.Format("Body wax {0} created", name);
    }
}
public class BeautySoapFactory : ISoap
{
    public string Create(string name)
    {
         return string.Format("Toilet soap {0} created", name);
    }

}
public class DetergentSoapFactory : ISoap
{
    public string Create(string name)
    {
         return string.Format("Detergent bar {0} created", name);
    }
}



//factory of factories(Bike Factory, Scooter Factory)
/// <summary>
/// The 'AbstractFactory' interface. 
/// </summary>
interface IBeautyProduct
{
    ISoap CreateSoap(ProductType type);
    IWax CreateWax(ProductType type);
}

class HULFactory : IBeautyProduct
{
    public ISoap CreateSoap(ProductType type)
    {
        switch (type)
        { 
            case ProductType.BeautySoap:
                return new BeautySoapFactory();
            case ProductType.DetergentSoap:
                return new DetergentSoapFactory();
            default:
                throw new ApplicationException(string.Format("Soap '{0}' cannot be created", type));
                //Console.WriteLine(string.Format("Soap '{0}' cannot be created", name));
                //break;
        }
    }
    public IWax CreateWax(ProductType type)
    {
        switch (type)
        {
            case ProductType.HairWax:
                return new HarWax();
            case ProductType.BodyWax:
                return new BodyWax();
            default:
                throw new ApplicationException(string.Format("Wax '{0}' cannot be created", type));
        }
    }
}

class LotusherbalsFactory : IBeautyProduct
{
    public ISoap CreateSoap(ProductType type)
    {
        switch (type)
        {
            case ProductType.BeautySoap:
                return new BeautySoapFactory();
            case ProductType.DetergentSoap:
                return new DetergentSoapFactory();
            default:
                throw new ApplicationException(string.Format("Soap '{0}' cannot be created", type));
            //Console.WriteLine(string.Format("Soap '{0}' cannot be created", name));
            //break;
        }
    }
    public IWax CreateWax(ProductType type)
    {
        switch (type)
        {
            case ProductType.HairWax:
                return new HarWax();
            case ProductType.BodyWax:
                return new BodyWax();
            default:
                throw new ApplicationException(string.Format("Wax '{0}' cannot be created", type));
        }
    }
}

最佳答案

这是一个虚构的例子(至少在我看来是这样),所以很难给出真正有用的建议,但我立即想到了一些东西:那个冗长的错误倾向 switch 语句

首先,不要认为要遵循此模式,您必须使用接口(interface)。抽象基类也很好,更好用的是已经在数以千计的论坛、帖子和教程中讨论过的广泛主题。

为了理解为什么 switch 不好让我简化你的场景(你有工厂的工厂......),让我们假设你只有一个:

class Factory 
{
    public ISoap CreateSoap(ProductType type)
    {
        switch (type)
        {
            case ProductType.BeautySoap:
                return new BeautySoapFactory();
            case ProductType.DetergentSoap:
                return new DetergentSoapFactory();
            default:
                throw new ApplicationException();
        }
    }
}

这段代码有什么问题?

您正在抛出错误的异常类型,因为未知无效 枚举 值您应该使用InvalidEnumArgumentException:

throw new InvalidEnumArgumentException("Unknown product type.", type, typeof(ProductType));

第二个错误是将不同的东西放在同一个枚举中。你有肥皂和蜡。它们完全不相关,您甚至可以使用两种不同的工厂方法来创建它们。这是令人困惑的(在更复杂的情况下,当您有一个 ProductType 参数时,您可能希望可以自由使用它们)然后让我们拆分它们:

public enum SoapTypes {
    Beauty,
    Detergent
}

您的工厂方法将是:

public ISoap Create(SoapType type)

请注意,您甚至不需要为您的方法使用不同的名称,因为您现在拥有不同的参数类型。您的默认案例现在将仅针对未知值(您将新项目添加到 SoapType 但您忘记更新代码)或明显错误的用法(例如调用Create((SoapType)100) 其中 100 不是文字 (!!!) 值,但它是在某处计算/读取的)。

现在假设您添加了一种新产品类型,您必须执行所有这些操作:

  • 创建一个新类来实现 ISoap
  • ProductType 添加一个新值。
  • 为您的交换机添加一个新案例。

您比必要的步骤多了一步。理想情况下,每个更改应该只影响代码中的一个位置,在这里您无法避免前两个步骤,但第三个可能是多余的。您的选择不多。

如果此代码对性能至关重要(度量!),您可以使用字典来获得更具可读性的代码(IMO):

static Dictionary<SoapType, Type> _soaps = new Dictionary<SoapType, Type>
{
    { SoapType.Beauty, typeof(BeautySoapFactory) }
    { SoapType.Detergent, typeof(DetergentSoapFactory) }
};

public ISoap CreateSoap(SoapType type) {
    return (ISoap)Activator.CreateInstance(_soaps[type]);
}

它看起来更具可读性(对我而言)并且它对动态 更改是开放的(要实例化的类可能不是硬编码在您的源文件中,而是来自配置)。如果映射是可配置的,您可能会使用此方法。

但是您仍然需要更新您的枚举您的工厂方法/字典。可能它们在不同的源文件中,您可能会忘记一些东西。如果性能不是(衡量!)问题,您可能希望将元数据添加到您的枚举值,当您将所有内容都显示在同一屏幕上时,将很难忘记它:

public enum SoapType {
    [FactoryClass(typeof(BeautySoapFactory)] Beauty,
    [FactoryClass(typeof(DetergentSoapFactory)] Detergent,
}

您的工厂方法将是:

public ISoap CreateSoap(SoapType type) {
    var descriptor = FactoryClassAttribute.From(type);
    return (ISoap)Activator.CreateInstance(descriptor.Type);
}
例如,

FactoryClassAttribute 就是这个(请注意,此代码可能会被极大地概括)。注意 enum constants are fields :

[Serializable]
sealed class FactoryClassAttribute : Attribute {
    public FactoryClassAttribute(Type type) {
        Type = type;
    }

    public Type Type { get; set; }

    public static FactoryClassAttribute From(Enum value) {
    {
        var type = value.GetType();
        return type.GetField(Enum.GetName(type, value))
            .GetCustomAttributes(false)
            .OfType<FactoryClassAttribute>()
            .FirstOrDefault();
    }    
}

关于c# - 这是否遵循抽象工厂模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32736250/

相关文章:

c# - 每个算法具有不同方法签名的策略模式

c# - Veracode目录遍历问题c#

c# - 故意导致致命异常

c# - 如何使用 Selenium WebDriver C# 从下拉列表中选择一个选项?

php - 使用 fnmatch() 测试 level1.level2.level3 中的权限

c++ - 将抽象类实现为其他类的接口(interface)而无需 vtable 开销

c# - 如何告诉构造函数它应该只使用原始类型

c++ - 销毁单例对象

c# - 使用带有日期时间和 bool 值的自动映射器来表示 POCO 中的半天

c# - 评论我的简单 MVP Winforms 应用程序