Naming convention for GoF Factory?
Asked Answered
S

3

4

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.

For example:

public abstract class ChocolateFactory { };

public class MyChocolateFactory { } : ChocolateFactory

What's the standard convention here?

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;

Hopefully this isn't subjective.

Spreader answered 7/7, 2015 at 8:5 Comment(5)
Both names don't say anything, so they're both equally bad. It's usually best to use descriptive names, such as "WhiteChocolateFactory" which is a concrete factory. Just creating factories for the sake of creating factories doesn't make sense; you usually make these if there are different implementations that create something of a certain 'class'. Using the name of that class as prefix makes sense.Herbivorous
According to GoF (rather than simple factory), instead of just creating a simple Factory, e.g. CustomerFactory, you should make an abstract version, and then an implementation, so that in the future someone can swap out the factory for their own implementation.Spreader
Yes I know the pattern; (from the top of my mind...:) the context they explain is that f.ex. if you have a 'widget' set you can swap out the rendering engine. If you look at the example, you'll see that they use the names of the rendering engines as factory names. As for my remark: note here that it's quite likely that the rendering engine will change over time (as new UI toolkits are invented every day), so it makes sense to use a factory here; it doesn't make sense to use a factory if it's not changing.Herbivorous
I guess DefaultChocolateFactory() would make the most sense then.Spreader
If you have an Abstract factory , you should always provide a default implementation but since the Question was about naming: Default... sucks! What would a DefaultFactory do? Why is is the default? and are all other implementations special then?Buffer
H
7

The question and the answer

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).

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.

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 and factories

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.

Factory pattern

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

// ...

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.
}

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.

Generalizing abstract factories

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.

Decoupling factories and types

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;
        }
    }
}
Herbivorous answered 7/7, 2015 at 11:26 Comment(0)
B
1

I don't know about any convention here, but I think it highly depends on the situation. I am using AbstractFactory only in Dependency injection secenarios where I want to create types at runtime and I have to provide an abstraction.

  1. The "abstract" part could be an interface -> Convention would be IName
  2. Your base class should describe a "common" thing about it's implementations, the Information lies in the context: If in your context a ChocolateFactory could be an "abstract" concept the implementations (that should describe concrete things (MyChocolateFactory is not a good name)) should show their (is) relationship but also the concrete usecase.
  3. Regarding to your comment: Do not use the abstract factory if there is no need for any other factory implementations just for possible future usecases ;)
Buffer answered 7/7, 2015 at 8:19 Comment(0)
O
1

You can use something like this :

// general interface for abstract factory (this is optional)
public abstract class AbstractFactory { };

// interface that uses a type of factory and produce abstract product
public abstract class AbstractChocolateFactory : AbstractFactory  { };

// family of concrete factories that produce concrete products
public class NestleChocolateFactory { } : AbstractChocolateFactory 

public class SwissChocolateFactory { } : AbstractChocolateFactory 

It's just and idea but what implementation of Abstract Factory pattern to use depends entirely on the specific task you have.

Olympia answered 7/7, 2015 at 8:25 Comment(10)
What is the reason for the AbstractFactory base class? What members could be in there and what benefits do you get?Buffer
Abstract factory main goal is to support families of products right?! But what if you have 5 different types of products ? eg : PC, Chocolate, Pet and so on. So you create abstraction of ANY factory, then you create another abstraction of some type of product that this factory may produce and finally you add concrete factory that implements your product-factory for a specific company.Olympia
I don't really like the idea, it seems like you have a base class to show some role (should be in interface then) that appears as a relationship where no relationship is. Should you really be able to use a PCFactory where a PetFactory is used? Does that follow the Liskov substitution principle? But that is just my opinion... and can be fine for youBuffer
@Thorarins I disagree with you. It depends on a specific situation. Interfaces cannot contain implementation and in some cases they're not as useful. For example you got like 55 methods in interface but you don't need them all in the same time you would prefer to have only business logic in your concrete factory class. What you'll do? If you inherited from interface you HAVE TO implement all methods while with abstract class you don't (if this methods are already implemented in AC)Olympia
But with 55 methods you have a (horrible) class interface from a horrible class design ;)Buffer
Sometimes it's not you who design all things you know :) Maybe this interface was designed by someone else and you just need to work with it. I just wan't to point that interfaces are good but sometimes it's cool to use abstract class too. imho it's all problem-specificOlympia
According to GoF factory you'd create an abstract SnickersFactory() then implement it, so if someone wants to create snickers in a different way in the future, they can do so without altering code (open/closed principle), makes more sense if it's a SQLConnectionFactory() and they want to implement it in a different way.Spreader
@Olympia that is why abstract classes are poor, if you have abstract methods declared and never implements them is just bad design. Using interface you tell the developer this is your contract use it or stay away.Stripe
@Stripe actually you cannot leave abstract methods unimplemented as it'll lead to compile time exception. All members marked as abstract must be implemented somewhere in code right?.Olympia
ah yes you are right about that which makes it even more like an interface :)Stripe

© 2022 - 2024 — McMap. All rights reserved.