Factory Design Pattern (needing critique)

﹥>﹥吖頭↗ 提交于 2019-12-02 19:47:47

Sorry. That is a quite inflexxible factory. Reflection can giva some POWWAH!!

public interface IFood
{
    bool IsTasty { get; }
}
public class Hamburger : IFood
{
    public bool IsTasty {get{ return true;}}
}
public class PeaSoup : IFood
{
    public bool IsTasty { get { return false; } }
}

public class FoodFactory
{
    private Dictionary<string, Type> _foundFoodTypes =
        new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    /// Scan all specified assemblies after food.
    /// </summary>
    public void ScanForFood(params Assembly[] assemblies)
    {
        var foodType = typeof (IFood);
        foreach (var assembly in assemblies)
        {
            foreach (var type in assembly.GetTypes())
            {
                if (!foodType.IsAssignableFrom(type) || type.IsAbstract || type.IsInterface)
                    continue;
                _foundFoodTypes.Add(type.Name, type);
            }
        }

    }

    /// <summary>
    /// Create some food!
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public IFood Create(string name)
    {
        Type type;
        if (!_foundFoodTypes.TryGetValue(name, out type))
            throw new ArgumentException("Failed to find food named '" + name + "'.");

        return (IFood)Activator.CreateInstance(type);
    }

}

Usage:

var factory = new FoodFactory();
factory.ScanForFood(Assembly.GetExecutingAssembly());

Console.WriteLine("Is a hamburger tasty? " + factory.Create("Hamburger").IsTasty);

Edit, feedback on your code:

First of all, factories are used to be able to create objects with a little code changes as possible when adding new types of implementations. Using an enum means that all places that are invoking the factory need to use an enum and be updated when the enum changes.

Sure, it's still a bit better than creating types directly.

The second problem with your code is that you are using a switch statement (but that's the best way to do it if the enum is an requirement). It's better to be able to register all different classes in some way. Either from a config file or by allowing the actual implementations (for instance the Hamburger class) to register themselves. This requires that the factory follows the singleton pattern.

Here comes Reflection to the rescue. Reflection allows you to go through all types in DLLs and EXEs. So we can search for all classes that implements our interface and therefore be able to build a dictionary will all classes.

I think your explanation including the real world example is good. However, I don't think your example code shows the real benefits of the pattern.

Some possible changes:

  • I wouldn't have the enum in parallel to the types. This looks like you have to update the enum every time a type is added. It might be more appropriate to pass the System.Type. Then you can even make the factory a generic with a template argument.
  • I think the pattern is more "impressive" if you use it for creating something like a hardware interface. You'd then have a "AbstractNetworkDevice" and all your callers don't know which hardware setup you have. But the factory can create a "TcpNetworkDevice" or a "SerialNetworkDevice" or whatever based on some configuration which was made at startup.

I would suggest you use interfaces instead of abstract classes/inheritance. Other than that, it looks OK.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!