c# - 工厂设计模式(需要批判)

标签 c# design-patterns factory-pattern

我正在整理这个设计模式的解释和代码示例,试图帮助我周围的人掌握它(同时帮助我自己掌握这个模式)。

我正在寻找的是对我的解释和代码示例的意见或批评...谢谢!

什么是工厂模式? 工厂模式利用一个特定的专用“对象创建者对象”来处理对象的创建和大多数情况下的实例化,类似于现实世界的工厂。

现实世界的例子
将汽车工厂想象成各种类型汽车的制造商。该汽车厂的一条装配线可能有一天会生产一辆卡车,但在另一天可能会重新装配以生产汽车。假设一家经销商向其指定的客户处理部门下了 10 辆汽车的订单。该部门然后利用某个工厂并订购了 10 辆汽车。客户经理不关心自己制造汽车(想象一下糟糕的结果)他们只处理最终产品,确保经销商得到他们的车辆。

同款汽车的一款新车型在明年问世,订单开始涌入。客户处理人员(仍然不关心汽车的生产)下了订单,但现在他们收到的汽车不同了,组装方法甚至工厂可能完全不同,但帐户处理程序不必担心这一点。另一个想法:如果某个客户处理程序下订单,车辆的工厂组装商可能确切地知道要采取什么行动(即客户处理程序 X 下订单,工厂组装商知道对于客户处理程序 X,他们生产 10 辆 Y 型车辆).另一种选择可能是客户经理告诉装配商要生产什么类型的车辆。

如果帐户处理程序也处理车辆的创建(即它们是耦合的),则每次车辆以任何方式发生变化时,每个帐户处理程序都必须接受再培训以生产该车辆。这会造成质量问题,因为客户处理人员的数量远远多于工厂的数量……会发生错误,费用也会高得多。

回到 OOP
对象工厂作为一种应用于软件工程的设计模式在概念上类似于上面的例子......某种方式。汇编器可以检查请求的客户端和句柄,或者客户端可以告诉汇编器它需要什么对象。现在......你在一个项目中创建了一个对象工厂和各种装配器,在项目的后期,需求略有变化,你现在被要求改变对象内容以及它的客户如何处理该对象。由于您使用了工厂模式,因此这是一个简单的更改,并且在一个位置,您可以更改或添加工厂生成的对象,并更改汇编器布置对象内容的格式。

完成此操作的不幸方法是没有工厂方法,在客户端中实例化每个对象实例并格式化对象内容……假设您在 20 个客户端中使用了这个特定对象。现在你必须去每个客户端,改变每个对象实例和格式......多么浪费时间......偷懒......第一次就以正确的方式做,这样你就可以节省自己(和其他人)的时间和以后的努力。

代码示例 (C#)
下面是一个利用工厂生产食品和各种食品的例子

Factory module
    public enum FoodType
    {
    //enumerated foodtype value, if client wants to specify type of object, coupling still occurs
        Hamburger, Pizza, HotDog
    }
 
    /// <summary>
    /// Object to be overridden (logical)
    /// </summary>
    public abstract class Food
    {
        public abstract double FoodPrice { get; }
    }
 
    /// <summary>
    /// Factory object to be overridden (logical)
    /// </summary>
    public abstract class FoodFactory
    {
        public abstract Food CreateFood(FoodType type);
    }
 
    //-------------------------------------------------------------------------
    #region various food objects
    class Hamburger : Food
    {
        double _foodPrice = 3.59;
        public override double FoodPrice
        {
            get { return _foodPrice; }
        }
    }
 
    class Pizza : Food
    {
        double _foodPrice = 2.49;
        public override double FoodPrice
        {
            get { return _foodPrice; }
        }
    }
 
    class HotDog : Food
    {
        double _foodPrice = 1.49;
        public override double FoodPrice
        {
            get { return _foodPrice; }
        }
    }
    #endregion
    //--------------------------------------------------------------------------
 
 
    /// <summary>
    /// Physical factory
    /// </summary>
    public class ConcreteFoodFactory : FoodFactory
    {
        public override Food CreateFood(FoodType foodType)
        {
            switch (foodType)
            {
                case FoodType.Hamburger:
                    return new Hamburger();
                    break;
                case FoodType.HotDog:
                    return new HotDog();
                    break;
                case FoodType.Pizza:
                    return new Pizza();
                    break;
                default:
                    return null;
                    break;
            }
        }
    }
 
    /// <summary>
    /// Assemblers
    /// </summary>
    public class FoodAssembler
    {
        public string AssembleFoodAsString(object sender, FoodFactory factory)
        {
            Food food = factory.CreateFood(FoodType.Hamburger);
            if (sender.GetType().Name == "default_aspx")
            {
                return string.Format("The price for the hamburger is: ${0}", food.FoodPrice.ToString());
            }
            else
            {
                return food.FoodPrice.ToString();
            }  
        }
 
        public Food AssembleFoodObject(FoodFactory factory)
        {
            Food food = factory.CreateFood(FoodType.Hamburger);
            return food;
        }
    }

Calling factory
FoodFactory factory = new ConcreteFoodFactory(); //create an instance of the factoryenter code here
lblUser.Text = new FoodAssembler().AssembleFoodAsString(this, factory); //call the assembler which formats for string output

Object o = new FoodAssembler().AssembleFoodObject(factory); //example: instantiating anon object, initialized with created food object

最佳答案

对不起。那是一个相当不灵活的工厂。反射(reflection)可以给一些 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);
    }

}

用法:

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

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

编辑、反馈您的代码:

首先,工厂用于在添加新类型的实现时能够创建尽可能少的代码更改的对象。使用枚举意味着所有调用工厂的地方都需要使用枚举并在枚举更改时更新。

当然,它仍然比直接创建类型要好一些。

您的代码的第二个问题是您使用的是 switch 语句(但如果需要枚举,这是最好的方法)。最好能够以某种方式注册所有不同的类。从配置文件或允许实际实现(例如 Hamburger 类)自行注册。这就要求工厂遵循单例模式。

Reflection 来拯救你了。反射允许您遍历 DLL 和 EXE 中的所有类型。所以我们可以搜索所有实现我们接口(interface)的类,因此能够为所有类构建一个字典。

关于c# - 工厂设计模式(需要批判),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4099969/

相关文章:

java - 如何用UML类图表达Factory或Helper类和它能生成的对象的关系

c# - 简单的 xpath 无法找到节点

c# - 在 blazor 中单击按钮时执行异步方法

c# - Visual Studio 在运行不同的程序时显示相同的输出

c++ - 为可能改变其内部属性的类选择设计模式

c# - 游戏逻辑动态可扩展架构实现模式

c# - System.InvalidOperationException - CodeFirst

C++:类功能子集的推荐设计模式?

java - 使用泛型的工厂对象创建者

java - 具有静态方法的辅助类工厂?