首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >工厂设计模式(需要批判性)

工厂设计模式(需要批判性)
EN

Stack Overflow用户
提问于 2010-11-04 18:31:24
回答 3查看 6.2K关注 0票数 6

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

我正在寻找的是意见&或批评我的解释和代码sample...thanks!

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

真实世界示例

想象一家汽车厂是各种类型汽车的创造者。那家汽车厂的一条装配线可以某一天生产一辆卡车,但在另一天可以重新生产汽车。比如说,一家经销商向他们指定的账户处理部门订购了10辆汽车。然后,该部门利用某一工厂,订购了10辆汽车。帐户处理程序不关心汽车本身(想象糟糕的结果),他们只使用最终产品,以确保经销商获得他们的车辆。

第二年,同一辆车的新型号问世,订单开始源源不断。帐户处理程序(仍然不关心汽车的生产)下订单,但现在他们收到的汽车是不同的,装配方法,甚至工厂可能完全不同,但帐户处理程序不必担心这一点。另一个想法是:如果某个帐户处理程序下了订单,车辆的工厂汇编程序可能确切地知道该采取什么行动(例如,帐户处理程序X下订单,工厂汇编程序知道对于帐户处理程序X,它们生产10辆Y型车辆)。另一种选择可能是帐户处理程序告诉汇编程序具体要生产哪种类型的车辆。

如果帐户处理程序也处理车辆的创建(即它们是耦合的),那么每次车辆以任何方式改变时,每个帐户处理程序都必须在生产该车辆方面接受再培训。这将产生质量问题,因为有更多的帐户处理程序将发生比factories...mistakes会发生,费用将更大。

返回OOP

对象工厂作为应用于软件工程的设计模式类似于概念…中的上述示例。工厂生产各种类型的其他对象,您可以使用一条流水线( objects ),它生成某种对象类型,并以某种方式返回。汇编程序可以检查请求的客户端和句柄,或者客户端可以告诉汇编程序它需要什么对象。Now...you在一个项目上创建一个对象工厂和各种汇编程序,稍后在项目中,需求略有变化,您现在被要求更改对象内容及其客户端处理该对象的方式。由于您使用了工厂模式--这是一个简单的更改,并且在一个位置上,您可以更改或添加工厂生成的对象,并更改汇编程序放置对象内容的格式。

这样做的不幸方法是没有工厂方法,实例化每个对象实例并格式化客户机themselves...say中的对象内容,您在20个客户端中使用了这个特定对象。现在您必须访问每个客户端,修改每个对象实例,并formats...what浪费时间--…第一次要以正确的方式进行lazy...do,这样以后您就节省了自己(和其他人)的时间和精力。

代码示例(C#)

下面是一个使用工厂生产食物和各种食物的例子。

代码语言:javascript
复制
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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-11-04 18:49:25

抱歉的。那是一家相当不灵活的工厂。反射能给你一些好处!!

代码语言:javascript
复制
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);
    }

}

用法:

代码语言:javascript
复制
var factory = new FoodFactory();
factory.ScanForFood(Assembly.GetExecutingAssembly());

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

编辑,反馈您的代码:

首先,在添加新类型的实现时,工厂通常能够创建具有少量代码更改的对象。使用枚举意味着调用工厂的所有位置都需要使用枚举,并在枚举更改时进行更新。

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

代码的第二个问题是,您使用的是开关语句(但如果枚举是必需的,这是最好的方法)。最好能以某种方式注册所有不同的类。无论是从配置文件还是通过允许实际实现(例如,汉堡类)注册自己。这要求工厂遵循单例模式。

救援的倒影来了。反射允许您遍历DLL和EXE中的所有类型。因此,我们可以搜索实现我们接口的所有类,因此能够构建一个字典,所有类都可以。

票数 14
EN

Stack Overflow用户

发布于 2010-11-04 18:43:47

我认为你的解释,包括现实世界的例子是好的。但是,我不认为您的示例代码显示了该模式的真正好处。

一些可能的变化:

  • ,我不会将枚举与类型并行。这看起来就像每次添加类型时都必须更新枚举。传递System.Type可能更合适。然后,您甚至可以使工厂成为一个带有模板参数的泛型。
  • --我认为如果您使用它来创建类似于硬件接口的东西,这种模式会更“令人印象深刻”。然后,您将有一个"AbstractNetworkDevice“,所有调用方都不知道您有哪些硬件设置。但是工厂可以根据启动时的一些配置创建一个"TcpNetworkDevice“或"SerialNetworkDevice”或其他任何东西。
票数 3
EN

Stack Overflow用户

发布于 2010-11-04 18:38:36

我建议您使用接口而不是抽象类/继承。除此之外,它看起来还可以。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4099969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档