这是否遵循抽象的工厂模式 [英] Does this follow abstract factory pattern

查看:90
本文介绍了这是否遵循抽象的工厂模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据设计模式:抽象工厂的定义。 InformIT的。从2009-10-23原始归档。检索2012-05-16,对象创建 - >抽象工厂:。


意图:为
提供界面,创建相关或依赖对象的系列,而不指定
他们的具体课程。


以下是我在抽象设计模式下的尝试,这是我第一次使用工厂方法。请帮助我。

 枚举ProductType 
{
BeautySoap = 1,
DetergentSoap = 2,
HairWax = 3,
BodyWax = 4,
}
界面ISoap
{
string创建(字符串名称);

}
接口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} CREA ted,name);
}
}
public class BeautySoapFactory:ISoap
{
public string Create(string name)
{
return string.Format(厕所肥皂{0}创建,名称);
}

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



//工厂工厂(Bike Factory,Scooter Factory)
///< summary>
///'AbstractFactory'接口。
///< / summary>
接口IBeautyProduct
{
ISoap CreateSoap(ProductType type);
IWax CreateWax(ProductType类型);
}

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}无法创建,键入));
//Console.WriteLine(string.Format(\"Soap'{0}'无法创建,name));
// break;
}
}
public IWax CreateWax(ProductType type)
{
switch(type)
{
case ProductType.HairWax:
返回新的HarWax();
case ProductType.BodyWax:
return new BodyWax();
default:
throw new ApplicationException(string.Format(Wax{0}无法创建,键入));
}
}
}

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}无法创建,键入));
//Console.WriteLine(string.Format(\"Soap'{0}'无法创建,name));
// break;
}
}
public IWax CreateWax(ProductType type)
{
switch(type)
{
case ProductType.HairWax:
返回新的HarWax();
case ProductType.BodyWax:
return new BodyWax();
default:
throw new ApplicationException(string.Format(Wax{0}无法创建,键入));
}
}
}


解决方案

这是一个虚构的例子(至少在我看来),所以很难给出真正有用的建议,但有一些事情立即出现在我的眼前:那个长长的冗长的错误的 开关 语句



首先不要以为要遵循这种模式你必须使用界面。还有一个抽象的基类是好的,最好使用的是已经在数千个论坛,帖子和教程中讨论过的一个广泛的话题。



了解为什么开关不好让我简化你的方案(你有工厂的工厂...),我们假设你只有一个:

  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();
}
}
}

此代码有什么问题?



您正在抛出错误异常类型,因为 无效 code> enum value你应该使用 InvalidEnumArgumentException

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

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

  public enum SoapTypes {
美容,
洗涤剂
}

您的工厂方法将是:

 code> public ISoap Create(SoapType type)

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

现在,我们假设您添加了新的产品类型,您必须执行以下所有操作:




  • 创建一个新类以实现 ISoap

  • 添加一个新值到 ProductType

  • 向您的交换机添加新案例。



您有一个步骤超过什么应该是必要的理想情况下,每个更改只会影响代码中的一个位置,这里您不能避免前两个步骤,但第三个可能是多余。您有几个选项。



如果此代码是性能关键(度量!),您可以使用字典来获取更多可读代码(IMO):

 静态字典< 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)]洗涤剂,
}

您的工厂方法将是:

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

FactoryClassAttribute (请注意,此代码可能会被大致推广)。请注意,枚举常量是字段

  [Serializable] 
密封类FactoryClassAttribute:属性{
public FactoryClassAttribute(Type type){
Type = type;
}

public Type Type {get;组; }

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


As per defination "Design Patterns: Abstract Factory". informIT. Archived from the original on 2009-10-23. Retrieved 2012-05-16,Object Creational -> Abstract Factory:.

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

Below is my try at abstract design pattern. This is my first hands on factory method. Can anyone please help me on this.

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));
        }
    }
}

解决方案

It's a fictional example (at least it seems to me) so it's hard to give really useful suggestions but something comes to my eyes immediately: that long verbose error-prone switch statements.

First of all don't think that to follow this pattern you must use interfaces. Also an abstract base class is fine, what is better to use is a vast topic already discussed in thousands forums, posts and tutorials.

To understand why switch is bad let me simplify your scenario (you have factories of factories...), let's assume you just have one:

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();
        }
    }
}

What's wrong in this code?

You're throwing wrong exception type, for an unknown or invalid enum value you should use InvalidEnumArgumentException:

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

Second mistake is to put together different things in same enum. You have soap and wax. They're completely unrelated and you even have two different factory methods to create them. This is confusing (in a more complex scenario when you have a ProductType parameter you may expect to be free to use them all) then let's split them:

public enum SoapTypes {
    Beauty,
    Detergent
}

Your factory method will then be:

public ISoap Create(SoapType type)

Note that you don't even need different names for your methods because you now have different parameter types. You default case will now be executed only for unknown values (you add a new item to SoapType but you forget to update your code) or for clearly wrong usage (for example calling Create((SoapType)100) where 100 isn't a literal (!!!) value but it's calculated/read somewhere).

Now let's imagine you add a new product type, you have to do all of these:

  • Create a new class to implement ISoap.
  • Add a new value to ProductType.
  • Add a new case to your switch.

You have one step more than what should be necessary. Ideally each change should affect only one place in your code, here you can't avoid first two steps but third one may be superfluous. You have few options.

If this code is performance critical (measure!) you may use a dictionary to have more readable code (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]);
}

It looks more readable (to me) and it's open to dynamic changes (class to instantiate may be not hard-coded in your source files but come from configuration). You would probably use this method if mapping is configurable.

However you still have to update your enum and your factory method/dictionary. Probably they're in different source files and you may forget something. If performance isn't a (measured!) issue you may want to add metadata to your enum values, it'll be hard to forget it when you have everything on the same screen:

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

Your factory method will be:

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

FactoryClassAttribute is, for example, this (note that this code may be greatly generalized). Note that 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();
    }    
}

这篇关于这是否遵循抽象的工厂模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆