工廠方法模式簡介:
工廠方法(Factory Method)模式的意義是定義一個創建產品對象的工廠接口,將實際創建工作推遲到子類當中。核心工廠類不再負責產品的創建,這樣核心類成為一個抽象工廠角色,僅負責具體工廠子類必須實現的接口,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引進新的產品。
過程方法模式結構:
抽象工廠角色:是工廠方法模式的核心,與應用程序無關。任何在模式中創建的對象的工廠類必須實現這個接口。
具體工廠角色:這是實現抽象工廠接口的具體工廠類,包含與應用程序密切相關的邏輯,并且受到應用程序調用以創建產品對象
抽象產品角色:工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的接口。在上圖中,這個角色是Light。
具體產品角色:這個角色實現了抽象產品角色所定義的接口。某具體產品有專門的具體工廠創建,它們之間往往一一對應。
在上一篇博文【C#設計模式-簡單工廠模式】中,我們使用簡單工廠模式實現了:
如果有一個住戶管理系統,里面的住戶類型是可變的,每一種租戶類型的租金計算公式都存在差異
例如:A類型的住戶租金額=天數單價+績效0.005;B類型的住戶租金額=月份(每月價格+performance0.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 CreateAshop : 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 CreateBshop : 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類型的商店算法就可以通過反射的方式進行計算,不需要修改原來的工程類代碼。
整個項目的結構圖如下: