工廠方法模式介紹:
工廠方法(Factory Method)模式的意義是定義一個創建產品對象的工廠接口,將實際創建工作推遲到子類當中。核心工廠類別不再負責產品的創建,這樣核心類別成為一個抽象工廠角色,僅負責具體工廠子類別必須實現的接口,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引進新的產品。
工廠方法模式結構圖:
角色分類:
抽象工廠角色:是工廠方法模式的核心,與應用程式無關。任何在模式中建立的物件的工廠類別必須實作這個介面。
具體工廠角色:這是實現抽象工廠介面的具體工廠類,包含與應用程式密切相關的邏輯,並且受到應用程式呼叫以建立產品物件
抽象產品角色:工廠方法模式所建立的物件的超類型,也就是產品物件的共同父類或共同擁有的介面。在上圖中,這個角色是Light。
具體產品角色:這個角色實作了抽象產品角色所定義的介面。某具體產品有專門的具體工廠創建,它們之間往往一一對應。
引入實際例子:
在上一篇博文簡單工廠模式中,使用簡單工廠模式進行了以下實現:如果有一個住戶管理系統,裡面的住戶類型是可變的,每一種租戶類型的租金計算公式都存在差異例如:A類型的住戶租金額=天數*單價+績效*0.005;B類型的住戶租金額=月份*(每月價格+performance*0.001)這裡我們雖然實現了客戶的需求,但是如果客戶後期需要增加了C類型商店和D類型商店,而它們的演算法要求又不一樣,這個時候我們就需要進行C,D類型商店的創建,並繼承Ishop接口,實現裡面的方法,同時還得繼續修改工廠類別在switc中增加case進行捕捉創建相應的商店對象,一旦出現這樣的情況,是不利於程序的擴展性和項目後期的維護性的。
1.分析:商店有共同的行為特徵,都要進行店鋪租金計算行為,我們抽象了Ishop ,裡面有待實現的計算商店租金方法行為。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace FactoryEntiy { public interface Ishop { double Getrent(int days, double dayprice, double performance); } }
2.我們實作Isho介面裡面的方法,創造A,B類型店鋪。
using FactoryEntiy; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ProductEnity { /// <summary> /// 继承商店接口,实现里面的行为方法,即算法 /// </summary> public class Ashop:Ishop { /// <summary> /// /// A类型商店租金额,天数*单价+绩效*0.005 /// </summary> /// <param name="days">天数</param> /// <param name="dayprice">每天单价</param> /// <param name="performance">日平均绩效</param> /// <returns></returns> public double Getrent(int days, double dayprice, double performance) { Console.WriteLine("A商店的租金算法"); return days * dayprice + performance * 0.01; } } }
using FactoryEntiy; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ProductEnity { /// <summary> /// 继承商店接口,实现里面的行为方法,即算法 /// </summary> public class Bshop:Ishop { /// <summary> /// B类型商店的租金=月份*(每月价格+performance*0.001) /// </summary> /// <param name="month">月数</param> /// <param name="monthprice">月单价</param> /// <param name="performance">月平均绩效</param> /// <returns></returns> public double Getrent(int month, double monthprice, double performance) { Console.WriteLine("B商店的租金算法"); return month * (monthprice + performance * 0.001); } } }
3.現在考慮在什麼情況下創建商店的實體,對不同的商店進行租金計算,並且方便以後的增加和修改。於是我們創建IFactroy接口,裡面有待實現的創建商店對象的方法。
using FactoryEntiy; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace FactoryMethod { /// <summary> /// 工厂类,创建商店类型实体 /// </summary> public interface IFactory { Ishop CreateShop(); } }
4.現在我們就可以繼承自IFactory,實現裡面創建對應的商店對象了。
using FactoryEntiy; using FactoryMethod; using ProductEnity; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ProductEnity { /// <summary> /// 继承工厂类,创建A类型商店实体 /// </summary> public class CreateBshop : IFactory { public Ishop CreateShop() { return new Ashop(); } } }
using FactoryEntiy; using FactoryMethod; using ProductEnity; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ProductEnity { /// <summary> /// 继承工厂类,创建B类型商店实体 /// </summary> public class CreateAshop : IFactory { public Ishop CreateShop() { return new Bshop(); } } }
5.之後根據目前的商店類型進行判斷,該類型的商店應該進行哪一種演算法:
using FactoryEntiy; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Reflection; using System.Text; namespace FactoryMethod.App { class Program { static void Main(string[] args) { string shopname = ConfigurationManager.AppSettings["CreateShopClassName"]; //shopname为创建商店类名称,此处=CreateAshop IFactory af = (IFactory)Assembly.Load("ProductEnity").CreateInstance("ProductEnity." + shopname); //第一个ProductEnity是dll的名称,第二个ProductEnity是项目的命名空间。 Ishop As = af.CreateShop(); double total = As.Getrent(30, 300, 2000); //30 天/100元 日平均绩效为2000 Console.WriteLine("该A类型商店的租金为:" + total); Console.WriteLine("============="); IFactory bf = (IFactory)Assembly.Load("ProductEnity").CreateInstance("ProductEnity." + "CreateBshop"); //CreateBshop可以保存为配置或者存在数据库中, //注意该保存字符串应该与项目中创建的类名一样, //否则反射的方式会找不到该项目下的类。 Ishop Bs = bf.CreateShop(); total = Bs.Getrent(30, 300, 2000); //30 天/100元 日平均绩效为2000 Console.WriteLine("该A类型商店的租金为:" + total); } } }
之前的工廠類別透過switch創建物件的方式,有利於後面的新類型商店增加和演算法修改增加和維護以後在專案需求在有變革,我們只需要重新在專案中增加C,D類型商店,繼承Ishop實現裡面的方法,同時,添加繼承IFactroy接口,創建對應的商店對象編譯該項目的ProductEnity.dll,以後再進行計算該C,D類型的商店算法就可以通過反射的方式進行計算,不需要修改原來的工程類代碼。
整個計畫的結構圖如下: