C#設計模式之6:抽象工廠模式


前面分析了簡單工廠模式和工廠方法模式,接着來看一下抽象工廠模式,他與工廠方法模式有一些相似的地方,也有不同的地方。

先來看一個不用工廠方法模式實現的訂購披薩的代碼:

 

對象依賴的問題:當你直接實例化一個對象時,就是在依賴他的具體類。接着上面的例子,如果在一個PizzaStore里面直接創建很多對象時,他們的依賴關系是這樣的:

這里引出一個概念:依賴倒置。很清楚的代碼里減少具體類的依賴是一件好事。依賴倒置的定義是:要依賴抽象,不要依賴實現。這個原則說說明了:不能讓高層組建依賴底層組件,而且,不管是高層組件還是底層組件,他們都要依賴抽象。所謂的高層組件,是由其它底層組件定義其行為的類。例如。PizzaStore是個高層組件,因為他的行為是由比薩定義的:PizzaStore創建所有不同的比薩對象,准備、烘烤、切片、裝盒;而披薩本身屬於低層組件。

原則的應用

上圖展示的問題在於,它依賴每個比薩類型,因為它是在自己的orderPizza方法中,實例化這些具體類型的。雖然我們已經創建了一個抽象,就是Pizza,但是我們仍然在代碼中,實際的創建了具體的pizza,所以,這個抽象沒有什么影響力。如何在OrderPizza方法中,將這些實例化對象的代碼獨立出來,我們都知道,工廠方法剛好能排上用場。所以,應用工廠方法之后。類圖看起來就像這:

 在應用了工廠方法模式之后(參考工廠方法模式),你將注意到,高層組件和底層組件都依賴了抽象(Pizza),要遵循依賴倒置原則,工廠方法並非唯一的技巧,但是,確實最有威力的技巧之一。

如何更好的遵循依賴倒置原則

變量不可以持有具體類的引用。如果使用new,就會持有具體類的引用,就會對具體的類產生依賴。可以改用工廠來避免這種做法。

不要讓類派生自具體的類。如果派生自具體類,那你就會依賴具體類。請派生自一個抽象。

不要覆蓋基類中已經實現的方法。基類中已實現的方法,應該由所有的子類共享。

上面三點只是說盡量去做到,並不是一定要做到。

抽象工廠模式

這個模式涉及的類有點兒多。。。所以決定線上UML類圖,然后根據類圖來一步一步的說明:

實際上單就拿抽象工廠來說,不是太難說明這個模式的含義,但是抽象工廠一般是和工廠方法模式配合使用的:

看圖:首先,NYPizzaStore還是繼承自PizzaStore

public abstract   class PizzaStore
    {
        public Pizza OrderPizza(string pizzaType)
        {
            Pizza pizza = CreatePizza(pizzaType);
            pizza.Prepare();
            pizza.Bake();
            pizza.Cut();
            pizza.Box();
            return pizza;
        }
        public abstract Pizza CreatePizza(string pizzaType);
    }
View Code

 PizzaStroe依賴一個抽象的Pizza,而NYPizzaStore繼承了PizzaStore,覆寫了CreatePIzza:

 public class NyPizzaStore:PizzaStore
    {
        public override Pizza CreatePizza(string pizzaType)
        {
            Pizza pizza=null;
            var indigredientFactory=new NyPizzaIngredientFactory();
            if (pizzaType=="Cheese")
            {
                pizza = new CheesePizza(indigredientFactory);
            }
            else if (pizzaType=="Clam")
            {
                pizza=new ClamPizza(indigredientFactory);
            }
            return pizza;
        }
    }
View Code

實現的這個NYPizzaStore的耦合比較高,因為他在倆面new了三個對象:一個抽象工廠NyPizzaIngredientFactory,兩個具體的披薩CheesePizza和ClamPizza。但是因為這個部分應該是不易變的部分,以后也不會進行修改了(如果不是這樣的話那這個類還得繼續抽象不是么?)此外,NYPizzaStore還針對一個Pizza抽象進行了編程,Pizza的代碼如下:

public abstract class Pizza
    {
        public string Name { get; set; }
        public Dough Dough { get; set; }
        public Sauce Sauce { get; set; }
        public Veggie[] Veggies { get; set; }
        public Repperoni Repperoni { get; set; }
        public Clams Clams { get; set; }
        public Cheese Cheese { get; set; }
        public abstract void Prepare();

       public void Bake()
        {
            Console.WriteLine("bake...");
        }

       public void Cut()
        {
            Console.WriteLine("Cut...");
        }

       public void Box()
        {
            Console.WriteLine("Box...");
        }

        public override string ToString()
        {
            return this.Name;
        }
    }
View Code

Pizza里面依賴了很多的抽象,也就是說Pizza是針對抽象在編程,具體是一個什么披薩,應該交給Pizza的子類來進行詳細的描述,此外,看到Pizza里面還有一個抽象的Prepare方法,由此判斷出來Pizza類也實現了工廠方法模式。看一下Pizza的具體實現類(其中一個):

 public class CheesePizza : Pizza
    {
        private IPizzaIngredientFactory _factory;

        public CheesePizza(IPizzaIngredientFactory factory)
        {
            _factory = factory;
        }
        public override void Prepare()
        {
            Console.WriteLine($"Preparing {Name}");
            Dough = _factory.CreateDough();
            Sauce = _factory.CreateSauce();
            Cheese = _factory.CreateCheese();
        }
    }
View Code

CheesePizaa針對一個抽象的IPizzaIngredientFactory進行編程,這種利用組合的方式避免了繼承帶來的靜態的編譯所造成的不便,使得代碼可以在運行時體現出更靈活和可擴展的特性。IPizzaIngredientFactory是一個抽象工廠的接口,抽象工廠又叫做原料工廠,就是說生產產品所需要的各種原料都可以從抽象工廠來獲取:

 public class NyPizzaIngredientFactory : IPizzaIngredientFactory
    {
        public Dough CreateDough()
        {
            return new ThinCruseDough();
        }

        public Sauce CreateSauce()
        {
            return new MarinaraSauce();
        }

        public Cheese CreateCheese()
        {
            return new ReggianoChesse();
        }

        public Veggie[] CreateVeggies()
        {
            return new Veggie[]
            {
               new Garlic(), new Mushroom(), new Onion(), new RedPepper(),
            };
        }

        public Repperoni CreateRepperoni()
        {
            return new SlicedRepperoni();
        }

        public Clams CreateClams()
        {
            return new FreshClams();
        }
    }
View Code

下面給出抽象工廠的定義:提供一個接口,用於創建相關或依賴對象的家族,而不需要明確指定具體類。在這個示例中,這個接口指的就是IPizzaInfredientFactory。

最重要的結論是,抽象工廠並不是單獨使用的,而是在工廠方法模式中進行擴展使用的。有的時候,我們要創建一些相關聯的或依賴的一族對象,這個時候可以把創建這些對象用抽象工廠來實現,而不是用工廠方法模式來實現,如果用工廠方法模式來實現,一個抽象產品對應一個抽象工廠,那樣的情況叫做“類型爆炸”。

最最重要的是:工廠方法模式遵循控制反轉的設計原則,並在此基礎上定義了一個框架,在框架中,如果又必要的話,將抽象工廠的邏輯放到這個框架中的適當位置上。所以,工廠方法模式和抽象工廠模式一般是配合使用的。抽象工廠的作用就是防止"類型爆炸”。

 

 

要點:

所有工廠都是用來封裝對象的創建。

簡單工廠提供給我們更多的是一種想法,讓我們萌生了將系統做一些分隔的想法,比如將對象的使用和實現進行解耦,等等。

工廠方法使用的是繼承和多態,吧對象的創建委托給子類。

抽象工廠使用的是對象的組合。對象的創建被實現在工廠接口所暴露的方法中來,比如Pizza類就實現工廠方法模式,里面的Prepare就是一個工廠方法。

所有工廠模式都是通過減少應用程序和類之間的依賴促進耦合。

依賴倒置原則,指導我們要盡量避免依賴具體,而要依賴抽象。

工廠是很有威力的技巧,幫我們盡量針對接口編程,而不是針對具體。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM