工厂设计模式(需要批评) [英] Factory Design Pattern (needing critique)

查看:115
本文介绍了工厂设计模式(需要批评)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我把这个设计模式的解释和代码示例放在一起,试图帮助我周围的人把握它(以及帮助自己掌握模式)。



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



什么是工厂模式?
工厂模式利用特定的专用对象创建者对象来处理类似于现实世界工厂的对象的创建(大部分时间实例化)。



现实世界的例子 _
想想汽车厂是各类汽车的创造者。该汽车工厂的装配线之一可能有一天可能生产一辆卡车,但是在另一天可能会重新生产汽车。说经销商下订单10辆汽车到其指定的帐户处理部门。那个部门然后利用一个工厂,订购了10辆车。客户处理程序不关心自己制作汽车(想像不好的结果),他们只能使用最终产品,确保经销商获得车辆。



一个新的模型同一辆汽车在明年出来,订单开始流入。帐号处理程序(仍然不关心汽车的生产)下订单,但现在他们收到的车是不同的,组装方式甚至是工厂总共可能会有所不同,但是帐户处理者不用担心。另外一个想法:车辆的工厂装配人员可能知道如果某个帐户处理机构下订单(即帐户处理程序X下达订单),工厂组装者知道对于客户处理程序X,他们生产10种Y型车辆)。另一个选择可能是帐户处理程序告诉汇编器究竟要生成哪种类型的车辆。



如果帐户处理程序也处理了车辆的创建(即它们被耦合),每当车辆以任何方式改变时,每个客户处理人员都必须重新培训生产该车辆。这会产生质量问题,因为有比工厂更多的帐户处理程序...错误会发生,费用会更大。



回滚到OOP

作为应用于软件工程的设计模式的对象工厂类似于上面的概念示例...工厂搅拌出各种类型的其他对象,可以使用装配线(对象汇编器)产生某种对象类型,以某种方式返回。汇编器可以检查请求的客户端和句柄,或者客户端可以告诉汇编器它需要什么对象。现在...你正在一个项目中,创建一个对象工厂和各种汇编程序,稍后在项目的路上,需求略有变化,现在要求您更改对象内容以及客户端如何处理该对象。由于您使用工厂模式,这是一个简单的更改,在一个位置,您可以更改或添加工厂生产的对象,并更改汇编器将对象内容放出的格式。



这样做的不幸的方式是没有工厂方法,实例化每个对象实例并在客户端本身格式化对象内容...说你使用这个特定的对象在20个客户端。现在你必须去每个客户端,改变每个对象实例和格式...什么浪费时间...懒惰...第一次做正确的方式,所以你保存自己(和其他人)时间以及以后的努力。



代码示例(C#)

以下是使用工厂进行食品和各种食物对象

 工厂模块
public enum FoodType
{
//枚举的食物类型值,如果客户想要指定对象的类型,则还会发生耦合
汉堡包,比萨,HotDog
}

///< summary>
///要覆盖的对象(逻辑)
///< / summary>
public abstract class Food
{
public abstract double FoodPrice {get; }
}

///< summary>
///要覆盖的工厂对象(逻辑)
///< / summary>
public abstract class FoodFactory
{
public abstract Food CreateFood(FoodType type);
}

// ----------------------------------- --------------------------------------
#region各种食物
类汉堡:食物
{
double _foodPrice = 3.59;
public override double FoodPrice
{
get {return _foodPrice;
}
}

class Pizza:食物
{
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>
///物理厂
///< / summary>
public class ConcreteFoodFactory:FoodFactory
{
public override Food CreateFood(FoodType foodType)
{
switch(foodType)
{
case FoodType 。汉堡:
return new Hamburger();
break;
case FoodType.HotDog:
返回新的HotDog();
break;
case FoodType.Pizza:
return new Pizza();
break;
默认值:
返回null;
break;
}
}
}

///< summary>
///汇编
///< / summary>
public class FoodAssembler
{
public string AssembleFoodAsString(object sender,FoodFactory factory)
{
食物食物= factory.CreateFood(FoodType.Hamburger);
if(sender.GetType()。Name ==default_aspx)
{
return string.Format(汉堡包的价格是:$ {0},food.FoodPrice的ToString());
}
else
{
return food.FoodPrice.ToString();
}
}

public Food AssembleFoodObject(FoodFactory factory)
{
食物食品= factory.CreateFood(FoodType.Hamburger);
返回食物;
}
}

调用工厂
FoodFactory factory = new ConcreteFoodFactory(); //在这里创建一个factoryenter代码的实例
lblUser.Text = new FoodAssembler()。AssembleFoodAsString(this,factory); //调用汇编器,其格式为字符串输出

Object o = new FoodAssembler()。AssembleFoodObject(factory); //示例:实例化anon对象,用创建的食物对象初始化


解决方案

p>对不起。这是一个相当僵硬的工厂。反思可以giva一些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>
///食物后扫描所有指定的组件。
///< / summary>
public void ScanForFood(params Assembly [] assembly)
{
var foodType = typeof(IFood);
foreach(程序集中的var assembly)
{
foreach(assembly中的var类型)
{
if(!foodType.IsAssignableFrom(type)| | type.IsAbstract || type.IsInterface)
continue;
_foundFoodTypes.Add(type.Name,type);
}
}

}

///< summary>
///创建一些食物!
///< / summary>
///< param name =name>< / param>
///< returns>< / returns>
public IFood Create(string name)
{
类型类型;
if(!_foundFoodTypes.TryGetValue(name,out type))
throw new ArgumentException(找不到食物命名+名称+'。

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

}

用法:

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

Console.WriteLine(是汉堡包可口吗?+ factory.Create(Hamburger)。IsTasty);

编辑,代码反馈



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



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



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



这里反映了救援。反思可以让您在DLL和EXE中查看所有类型。所以我们可以搜索实现我们的界面的所有类,因此能够构建一个字典将所有类。


I am putting together an explanation and code example of this design pattern, attempting to help others around me grasp it (along with helping myself to master the pattern as well).

What I am looking for is opinions & or critique of my explanation and code sample...thanks!

What is the factory pattern? The factory pattern utilizes a particular dedicated "object creator object" to handle creation of - and most times instantiation of - objects, similar to a real world factory.

Real world example
Think of an automobile factory being the creator of various types of automobiles. One of the assembly lines in that automobile factory may produce a truck one day, but on another day may be re-tooled for producing cars. Say a dealership places an order for 10 cars to their assigned account handling department. That department then utilizes a certain factory and orders up the 10 cars. The account handlers are not concerned with making the cars themselves (imagine the poor results) they only work with the final product, ensuring the dealership gets their vehicles.

A new model of that same car comes out the next year and orders begin flowing in. The account handlers (still not concerned with the production of the car) place the orders, but now the car they receive is different, the assembling method or even maybe the factory altogether may be different, yet the account handlers need not worry about this. An additional thought: the factory assemblers of the vehicles may know exactly what action to take if a certain account handler places an order (i.e. account handler X places an order, factory assembler knows that for account handler X, they produce 10 vehicles of type Y). Another option may be that the account handler tells the assembler exactly what type of vehicle to produce.

If the account handlers also handled the creation of the vehicles (i.e. they were coupled), every time a vehicle changed in any way, each of the account handlers would have to be retrained in producing that vehicle. This would create quality issues as there are far more account handlers than there would be factories...mistakes would happen, expense would be far greater.

Circling back to OOP
An object factory as a design pattern applied to software engineering is similar to the above example in concept… The factory churns out various types of other objects, you can utilize an assembly line (object assembler) which produces a certain object type, returned in a certain way. The assembler can either inspect the requesting client and handle, or the client may tell the assembler what object it requires. Now...you are on a project and create an object factory and various assemblers, later on down the road in the project, the requirements change slightly, you are now asked to alter object contents and how its clients are handling that object. Since you utilized the factory pattern this is a simple change and in one location, you can change or add the objects the factory produces, and alter the format in which the assemblers lay the object contents out.

The unfortunate way to have done this would have been without a factory method, instantiating each object instance and formatting object contents in the clients themselves...say you used this particular object in 20 clients. Now you must go to each one of the clients, alter each of the object instances and formats...what a waste of time…Be lazy...do it the right way the first time so you save yourself (and others) time and effort later.

Code example (C#)
Below is an example utilizing a factory for food and various food objects

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

解决方案

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.

这篇关于工厂设计模式(需要批评)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆