代码之家  ›  专栏  ›  技术社区  ›  dbobrowski

工厂设计模式(需要评论)

  •  6
  • dbobrowski  · 技术社区  · 14 年前

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

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


    想象一下汽车工厂是各种汽车的创造者。那家汽车厂的一条装配线可能有一天会生产一辆卡车,但另一天可能会重新加工生产汽车。假设经销商向其指定的客户处理部门订购了10辆车。然后那个部门利用某个工厂订购10辆车。客户经理并不关心汽车本身的制造(想象一下糟糕的结果),他们只处理最终产品,确保经销商得到他们的汽车。

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

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


    对象工厂作为一种应用于软件工程的设计模式,在概念上类似于上面的例子工厂生产出各种类型的其他对象,您可以使用一个生产某种对象类型的装配线(对象装配器),以某种方式返回。汇编程序可以检查请求的客户机并进行处理,或者客户机可以告诉汇编程序它需要什么对象。现在…您正在一个项目中创建一个对象工厂和各种汇编器,稍后在项目中,需求会稍微改变,您现在被要求更改对象内容及其客户机如何处理该对象。由于使用了工厂模式,这是一个简单的更改,在一个位置,您可以更改或添加工厂生成的对象,并更改汇编器布局对象内容的格式。

    代码示例(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
    
    3 回复  |  直到 14 年前
        1
  •  14
  •   jgauffin    11 年前

    对不起的。那是一个非常不灵活的工厂。反射可以给你一些力量!!

    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类)注册它们自己。这要求工厂遵循单例模式。

    这是对救援的反思。反射允许您浏览dll和exe中的所有类型。所以我们可以搜索实现接口的所有类,因此能够构建字典将所有类。

        2
  •  3
  •   Philipp    14 年前

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

    一些可能的变化:

    • 如果你用它来创建一个类似硬件接口的东西,我认为这个模式会更“令人印象深刻”。然后你就有了一个“抽象网络设备”,所有的呼叫者都不知道你有什么硬件设置。但是工厂可以根据启动时所做的一些配置来创建“TcpNetworkDevice”或“SerialNetworkDevice”或任何东西。
        3
  •  0
  •   Tim Coker    14 年前

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