GoF 工厂的命名约定? [英] Naming convention for GoF Factory?

查看:18
本文介绍了GoF 工厂的命名约定?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此模式使用抽象工厂,然后是工厂的实现.

This pattern uses an abstract factory, and then an implementation of the factory.

我确定这两个类有标准的命名约定,但我不知道它是什么.

I am sure there is a standard naming convention for these two classes, but I don't know what it is.

例如:

public abstract class ChocolateFactory { };

public class MyChocolateFactory { } : ChocolateFactory

这里的标准约定是什么?

What's the standard convention here?

我在考虑 ChocolateFactoryBase 或 ConcreteChocolateFactory,但也许还有别的东西(很像 Enum 往往以 Enum 为后缀,例如 PetTypeEnum 这样你就可以做 PetTypeEnum PetType;

I'm thinking either ChocolateFactoryBase, or ConcreteChocolateFactory, but perhaps there is something else (much like Enums tend to be suffixed with Enum, e.g. PetTypeEnum so that you can do PetTypeEnum PetType;

希望这不是主观的.

推荐答案

问题与答案

好的,这个问题从抽象工厂的命名问题开始.根据经验,始终使用正式"名称(例如 Factory、Decorator 等)和具体实现的描述(例如 Snickers、Mars、MotifWidget 等).

OK, this question started with a naming question of Abstract factories. As a rule of thumb, always use the 'formal' name what you're doing (e.g. Factory, Decorator, etc) and a description of the concrete implementation (e.g. Snickers, Mars, MotifWidget, etc, etc).

因此,基本上,您创建了一个 MSSQLConnection,它是您所描述的具体事物,而 Factory 意味着它遵循工厂模式的特征.

So basically, you create a MSSQLConnection which is the concrete thing you're describing, and Factory which means it follows the characteristics of the factory pattern.

好的,到目前为止命名和原始问题.现在是很酷的东西.讨论转到在 C# 中实现抽象工厂的最佳方法,这是一个不同的主题.我在用 C# 实现所有设计模式方面做了很多工作,为此我将在这里分享有关工厂的一些细节.这是:

OK, so far for naming and the original question. Now for the cool stuff. The discussion goes to the best way to implement abstract factories in C#, which is a different topic. I did quite some work on implementing all design patterns in C#, for which I'll share some details on Factories here. Here goes:

抽象工厂和工厂

抽象工厂基本上是基类或接口和具体实现的组合.如果您共享大量代码,则需要一个基类,如果不共享,则需要一个接口.

Abstract factories are basically a combination of a base class or interface, and a concrete implementation. A base class is what you want if you share a lot of code, an interface if you don't.

我通常会区分工厂"和抽象工厂".工厂是创建对象(某种类型)的东西,抽象工厂"是创建任意类型对象的东西.因此,抽象工厂的实现就是一个工厂.这与下一条信息相关.

I usually make a distinction between 'factories' and 'abstract factories'. A factory is a thing that creates objects (of a certain type), an 'abstract factory' is a thing that creates objects of arbitrary types. As such, it follows that an implementation of an abstract factory is a factory. This is relevant for the next piece of info.

工厂模式

支持 RTTI 的语言能够实现工厂模式.工厂模式是一种创建对象的东西.最简单的实现是一个只包含创建对象的方法的类,例如:

Languages that support RTTI are able to implement the factory pattern. A factory pattern is a thing that creates objects. The most trivial implementation is a class that only contains methods that create objects, e.g.:

// ...

public void CreateConnection()
{
    return new SqlConnection();
}

// ...

您通常使用它来抽象事物.例如,生成 XML 节点的 HTML 解析器中的事物会根据 HTML 标记创建某种类型的节点.

You usually use this to abstract things away. For example, the thing in a HTML parser that generates XML nodes creates a node of a certain type based on the HTML tag.

工厂通常根据运行时信息做出决策.因此,可以推广工厂模式以实现以下内容:

Factories often make decisions based on runtime information. It is therefore possible to generalize the Factory pattern to implement something like:

public T Create(string name) 
{
    // lookup constructor, invoke.
}

使用 RTTI 创建一个通用的工厂模式非常容易,该模式为每个名称存储一个 Type.查找名称,使用反射创建对象.完成.

It's pretty easy to create a generalized factory pattern using RTTI that stores a Type for each name. Lookup the name, create the object using reflection. Done.

哦,作为奖励,与手工制作所有工厂相比,您必须编写更少的代码.因为所有的实现都是一样的,你不妨把它放在一个基类中,并在一个静态构造函数中填充字典.

Oh and as a bonus you have to write way less code than making all the factories by hand. Because all implementations are the same, you might as well just put it in a base class and fill the Dictionary in a static constructor.

抽象工厂的泛化

抽象工厂基本上是工厂的集合,它们以与工厂模式相同的方式创建对象.唯一共享的是接口(例如,创建或您可以使用继承来创建抽象).

Abstract factories are basically collections of factories that create objects the same way as a Factory pattern. The only thing that's shared is the interface (e.g. Create or you can use inheritance to create an abstraction).

实现非常简单,所以我就先把它放在那里.

The implementation is quite trivial, so I'll just leave it at that.

工厂和类型解耦

让我们回到 GoF 的例子.他们谈论一个 MotifFactory 和一个 PMFactory.将来我们会遇到另一个 UI 问题,我们将需要一个 ASPNETFactory 或一个 SilverlightFactory.然而,未来是未知的,如果我们不需要,我们宁愿不发布旧的 DLL - 毕竟,这不灵活.

Let's go back to the GoF example. They talk about a MotifFactory and a PMFactory. In the future we will encounter yet another UI thingie and we'll need an ASPNETFactory or a SilverlightFactory. However, the future is unknown and we'd rather not ship our old DLL's if we don't need to - after all, that's not flexible.

如果我们想向我们的工厂添加一个新方法,就会出现第二个问题.因此,这样做将涉及更改所有工厂.您可能已经猜到了,我不想在多个地方更改此设置.

A second problem arrises if we want to add a new method to our factory. It follows that doing this will involve changing all factories. As you might have guessed, I don't want to change this in multiple places.

幸运的是,我们可以解决这两个问题.接口是相同的(甚至可以泛化),因此我们可以在运行时简单地向我们的工厂添加新功能.

Fortunately we can solve both these issues. The interface is the same (and it can even be generalized) so we can simply add new features to our factory at runtime.

不是告诉工厂要创建什么对象,我们可以使用属性告诉类它应该由某个工厂实现.我们还可以在加载程序集期间扫描所有类型,因此如果加载了程序集,我们可以简单地即时构建新工厂.

Instead of telling the factory what objects to create, we can use an attribute to tell a class that it should be materialized by a certain factory. We can also scan all types during the load of an assembly, so if an assembly is loaded, we can simply build new factories on-the-fly.

我为此牺牲的是编译时检查,但因为工厂模式通常使用运行时信息,所以这不一定是问题.

What I sacrifice for this is compile-time checks, but because the Factory pattern usually uses runtime information, that's not necessarily a problem.

总结一下,这是我工厂的代码:

Wrapping it all up, here's the code of my Factory:

/// <summary>
/// This attribute is used to tag classes, enabling them to be constructed by a Factory class. See the <see cref="Factory{Key,Intf}"/> 
/// class for details.
/// </summary>
/// <remarks>
/// <para>
/// It is okay to mark classes with multiple FactoryClass attributes, even when using different keys or different factories.
/// </para>
/// </remarks>
/// <seealso cref="Factory{Key,Intf}"/>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class FactoryClassAttribute : Attribute
{
    /// <summary>
    /// This marks a class as eligible for construction by the specified factory type.
    /// </summary>
    /// <example>
    /// [FactoryClass("ScrollBar", typeof(MotifFactory))]
    /// public class MotifScrollBar : IControl { }
    /// </example>
    /// <param name="key">The key used to construct the object</param>
    /// <param name="factoryType">The type of the factory class</param>
    public FactoryClassAttribute(object key, Type factoryType)
    {
        if ((factoryType.IsGenericType &&
             factoryType.GetGenericTypeDefinition() == typeof(Factory<,>)) ||
            factoryType.IsAbstract || 
            factoryType.IsInterface)
        {
            throw new NotSupportedException("Incorrect factory type: you cannot use GenericFactory or an abstract type as factory.");
        }
        this.Key = key;
        this.FactoryType = factoryType;
    }

    /// <summary>
    /// The key used to construct the object when calling the <see cref="Factory{Key,Intf}.Create(Key)"/> method.
    /// </summary>
    public object Key { get; private set; }

    /// <summary>
    /// The type of the factory class
    /// </summary>
    public Type FactoryType { get; private set; }
}

/// <summary>
/// Provides an interface for creating related or dependent objects.
/// </summary>
/// <remarks>
/// <para>
/// This class is an implementation of the Factory pattern. Your factory class should inherit this Factory class and 
/// you should use the [<see cref="FactoryClassAttribute"/>] attribute on the objects that are created by the factory.
/// The implementation assumes all created objects share the same constructor signature (which is not checked by the Factory). 
/// All implementations also share the same <typeparamref name="Intf"/> type and are stored by key. During runtime, you can 
/// use the Factory class implementation to build objects of the correct type.
/// </para>
/// <para>
/// The Abstract Factory pattern can be implemented by adding a base Factory class with multiple factory classes that inherit from 
/// the base class and are used for registration. (See below for a complete code example).
/// </para>
/// <para>
/// Implementation of the Strategy pattern can be done by using the Factory pattern and making the <typeparamref name="Intf"/>
/// implementations algorithms. When using the Strategy pattern, you still need to have some logic that picks when to use which key.
/// In some cases it can be useful to use the Factory overload with the type conversion to map keys on other keys. When implementing 
/// the strategy pattern, it is possible to use this overload to determine which algorithm to use.
/// </para>
/// </remarks>
/// <typeparam name="Key">The type of the key to use for looking up the correct object type</typeparam>
/// <typeparam name="Intf">The base interface that all classes created by the Factory share</typeparam>
/// <remarks>
/// The factory class automatically hooks to all loaded assemblies by the current AppDomain. All classes tagged with the FactoryClass
/// are automatically registered.
/// </remarks>
/// <example>
/// <code lang="c#">
/// // Create the scrollbar and register it to the factory of the Motif system
/// [FactoryClass("ScrollBar", typeof(MotifFactory))]
/// public class MotifScrollBar : IControl { }
/// 
/// // [...] add other classes tagged with the FactoryClass attribute here...
///
/// public abstract class WidgetFactory : Factory&lt;string, IControl&gt;
/// {
///     public IControl CreateScrollBar() { return Create("ScrollBar") as IScrollBar; }
/// }
///
/// public class MotifFactory : WidgetFactory { }
/// public class PMFactory : WidgetFactory { }
///
/// // [...] use the factory to create a scrollbar
/// 
/// WidgetFactory widgetFactory = new MotifFactory();
/// var scrollbar = widgetFactory.CreateScrollBar(); // this is a MotifScrollbar intance
/// </code>
/// </example>
public abstract class Factory<Key, Intf> : IFactory<Key, Intf>
    where Intf : class
{
    /// <summary>
    /// Creates a factory by mapping the keys of the create method to the keys in the FactoryClass attributes.
    /// </summary>
    protected Factory() : this((a) => (a)) { }

    /// <summary>
    /// Creates a factory by using a custom mapping function that defines the mapping of keys from the Create 
    /// method, to the keys in the FactoryClass attributes.
    /// </summary>
    /// <param name="typeConversion">A function that maps keys passed to <see cref="Create(Key)"/> to keys used with [<see cref="FactoryClassAttribute"/>]</param>
    protected Factory(Func<Key, object> typeConversion)
    {
        this.typeConversion = typeConversion;
    }

    private Func<Key, object> typeConversion;
    private static object lockObject = new object();
    private static Dictionary<Type, Dictionary<object, Type>> dict = null;

    /// <summary>
    /// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
    /// </summary>
    /// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed 
    /// to the constructor if this was defined.</param>
    /// <returns>An instance of the factory class</returns>
    public virtual Intf Create(Key key)
    {
        Dictionary<Type, Dictionary<object, Type>> dict = Init();
        Dictionary<object, Type> factoryDict;
        if (dict.TryGetValue(this.GetType(), out factoryDict))
        {
            Type t;
            return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t) : null;
        }
        return null;
    }

    /// <summary>
    /// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
    /// </summary>
    /// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed 
    /// to the constructor if this was defined.</param>
    /// <param name="constructorParameters">Additional parameters that have to be passed to the constructor</param>
    /// <returns>An instance of the factory class</returns>
    public virtual Intf Create(Key key, params object[] constructorParameters)
    {
        Dictionary<Type, Dictionary<object, Type>> dict = Init();
        Dictionary<object, Type> factoryDict;
        if (dict.TryGetValue(this.GetType(), out factoryDict))
        {
            Type t;
            return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t, constructorParameters) : null;
        }
        return null;
    }

    /// <summary>
    /// Enumerates all registered attribute keys. No transformation is done here.
    /// </summary>
    /// <returns>All keys currently known to this factory</returns>
    public virtual IEnumerable<Key> EnumerateKeys()
    {
        Dictionary<Type, Dictionary<object, Type>> dict = Init();
        Dictionary<object, Type> factoryDict;
        if (dict.TryGetValue(this.GetType(), out factoryDict))
        {
            foreach (object key in factoryDict.Keys)
            {
                yield return (Key)key;
            }
        }
    }

    private void TryHook()
    {
        AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(NewAssemblyLoaded);
    }

    private Dictionary<Type, Dictionary<object, Type>> Init()
    {
        Dictionary<Type, Dictionary<object, Type>> d = dict;
        if (d == null)
        {
            lock (lockObject)
            {
                if (dict == null)
                {
                    try
                    {
                        TryHook();
                    }
                    catch (Exception) { } // Not available in this security mode. You're probably using shared hosting

                    ScanTypes();
                }
                d = dict;
            }
        }
        return d;
    }

    private void ScanTypes()
    {
        Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>();
        foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
        {
            AddAssemblyTypes(classDict, ass);
        }
        dict = classDict;
    }

    private void AddAssemblyTypes(Dictionary<Type, Dictionary<object, Type>> classDict, Assembly ass)
    {
        try
        {
            foreach (Type t in ass.GetTypes())
            {
                if (t.IsClass && !t.IsAbstract &&
                    typeof(Intf).IsAssignableFrom(t))
                {
                    object[] fca = t.GetCustomAttributes(typeof(FactoryClassAttribute), false);
                    foreach (FactoryClassAttribute f in fca)
                    {
                        if (!(f.Key is Key))
                        {
                            throw new InvalidCastException(string.Format("Cannot cast key of factory object {0} to {1}", t.FullName, typeof(Key).FullName));
                        }
                        Dictionary<object, Type> keyDict;
                        if (!classDict.TryGetValue(f.FactoryType, out keyDict))
                        {
                            keyDict = new Dictionary<object, Type>();
                            classDict.Add(f.FactoryType, keyDict);
                        }
                        keyDict.Add(f.Key, t);
                    }
                }
            }
        }
        catch (ReflectionTypeLoadException) { } // An assembly we cannot process. That also means we cannot use it.
    }

    private void NewAssemblyLoaded(object sender, AssemblyLoadEventArgs args)
    {
        lock (lockObject)
        {
            // Make sure new 'create' invokes wait till we're done updating the factory
            Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>(dict);
            dict = null;
            Thread.MemoryBarrier();

            AddAssemblyTypes(classDict, args.LoadedAssembly);
            dict = classDict;
        }
    }
}

这篇关于GoF 工厂的命名约定?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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