簡體   English   中英

工廠設計模式(需要評論)

[英]Factory Design Pattern (needing critique)

我正在整理這個設計模式的解釋和代碼示例,試圖幫助我周圍的人抓住它(同時幫助自己掌握模式)。

我正在尋找的是對我的解釋和代碼示例的意見和批評......謝謝!

什么是工廠模式? 工廠模式利用特定的專用“對象創建器對象”來處理 - 對象的創建 - 並且大部分時間 - 實例化,類似於現實世界的工廠。

現實世界的例子
想象一下汽車工廠是各種類型汽車的創造者。 那個汽車廠的裝配線之一可能有一天會生產一輛卡車,但是在另一天可能會重新生產汽車。 假設經銷商向其指定的帳戶處理部門下達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

抱歉。 那是一個非常不靈活的工廠。 反射可以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>
    /// 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中的所有類型。 所以我們可以搜索實現我們接口的所有類,因此能夠為所有類構建字典。

我認為你的解釋包括現實世界的例子是好的。 但是,我不認為您的示例代碼顯示了該模式的真正好處。

一些可能的變化:

  • 我不會將枚舉與類型並行。 這看起來每次添加類型時都必須更新枚舉。 傳遞System.Type可能更合適。 然后,您甚至可以使用模板參數使工廠成為通用工具。
  • 我認為如果你用它來創建類似硬件接口的東西,那么這種模式會更“令人印象深刻”。 然后,您將擁有一個“AbstractNetworkDevice”,並且您的所有呼叫者都不知道您擁有哪種硬件設置。 但工廠可以根據啟動時的某些配置創建“TcpNetworkDevice”或“SerialNetworkDevice”或其他任何內容。

我建議你使用接口而不是抽象類/繼承。 除此之外,它看起來還不錯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM