程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 設計模式(3)--抽象工廠模式(Absrtact Factory Pattern),absrtactpattern

設計模式(3)--抽象工廠模式(Absrtact Factory Pattern),absrtactpattern

編輯:C#入門知識

設計模式(3)--抽象工廠模式(Absrtact Factory Pattern),absrtactpattern


定義

抽象工廠模式的實質就是提供接口來創建一系列相關或獨立的對象而不指定這些對象的具體類。

理解

在軟件系統中,經常面臨著“一系列相互依賴的對象”的創建工作;同時由於需求的變化,往往存在著更多系列對象的創建工作。如何應對這種變化?如何繞過常規的對象的創建方法(熟悉的new操作符),提供一種“封裝機制”來避免客戶程序和這種“多系列具體對象創建工作”的緊耦合?這就是我們要說的抽象工廠模式。抽象工廠模式提供了一種方式,可以將一組具有同一主題的單獨的工廠封裝起來。在正常使用中,客戶端程序需要創建抽象工廠的具體實現,然後使用抽象工廠作為接口來創建這一主題的具體對象。客戶端程序不需要知道(或關心)它從這些內部的工廠方法中獲得對象的具體類型,因為客戶端程序僅使用這些對象的通用接口。抽象工廠模式將一組對象的實現細節與他們的一般使用分離開來。

結構

優點

缺點

適用情形

代碼示例說明

(這裡借用TerryLee大神的例子和代碼)

中國企業需要一項簡單的財務計算:每月月底,財務人員要計算員工的工資。

員工的工資 = (基本工資 + 獎金 - 個人所得稅)。這是一個放之四海皆准的運算法則。

為了簡化系統,我們假設員工基本工資總是4000美金。

中國企業獎金和個人所得稅的計算規則是:

         獎金 = 基本工資(4000) * 10%

         個人所得稅 = (基本工資 + 獎金) * 40%

我們現在要為此構建一個軟件系統(代號叫Softo),滿足中國企業的需求。

此時采用最簡單的方式,類圖如下所示:

代碼如下所示:

using System;

namespace ChineseSalary
{    
    /// <summary>
    /// 計算中國個人所得稅
    /// </summary>
    public class ChineseTax
    {
        public double Calculate()
        {
            return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4;
        }
    }
}

using System;

namespace ChineseSalary
{
    /// <summary>
    /// 計算中國個人獎金
    /// </summary>
    public class ChineseBonus
    {
        public double Calculate()
        {
            return Constant.BASE_SALARY * 0.1;
        }
    }
}

namespace ChineseSalary
{
    /// <summary>
    /// 公用的常量
    /// </summary>
    public class Constant
    {
        public static double BASE_SALARY = 4000;
    }
}

using System;

namespace ChineseSalary
{
    /// <summary>
    /// 客戶端程序調用
    /// </summary>
    public class Calculator 
    {
        public static void Main(string[] args) 
        {
            ChineseBonus bonus = new ChineseBonus();
            double bonusValue  = bonus.Calculate();
    
            ChineseTax tax = new ChineseTax();
            double taxValue = tax.Calculate();
    
            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("Chinaese Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

美國企業工資的計算與中國大致相同,但是在獎金和個人所得稅計算規則不同於中國:

獎金 = 基本工資 * 15 %

個人所得稅 = (基本工資 * 5% + 獎金 * 25%)

我們僅僅將ChineseTax、ChineseBonus修改為AmericanTax、AmericanBonus。 只是修改了部分類的名稱和類方法的內容,結構沒有什麼變化,修改後的類圖如下所示:

代碼如下所示:

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 計算美國個人獎金
    /// </summary>
    public class AmericanBonus
    {
        public double Calculate()
        {
            return Constant.BASE_SALARY * 0.1;
        }
    }
}

using System;

namespace AmericanSalary
{    
    /// <summary>
    /// 計算美國個人所得稅
    /// </summary>
    public class AmericanTax
    {
        public double Calculate()
        {
            return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4;
        }
    }
}

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 公用的常量
    /// </summary>
    public class Constant
    {
        public static double BASE_SALARY = 4000;
    }
}

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 客戶端程序調用
    /// </summary>
    public class Calculator 
    {
        public static void Main(string[] args) 
        {
            AmericanBonus bonus = new AmericanBonus();
            double bonusValue  = bonus.Calculate();
    
            AmericanTax tax = new AmericanTax();
            double taxValue = tax.Calculate();
    
            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("American Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

開始只考慮將Softo系統運行於中國企業,但隨著MaxDO公司業務向海外拓展, MaxDO需要將該系統移植給美國使用。需要將上面中國和美國兩種情況整合在一個系統。移植時,MaxDO不得不拋棄中國企業的業務規則類ChineseTax和ChineseBonus, 然後為美國企業新建兩個業務規則類: AmericanTax,AmericanBonus。最後修改了業務規則調用Calculator類。

結果我們發現:每當Softo系統移植的時候,就拋棄原來的類。現在,如果中國華為要購買該系統,我們不得不再次拋棄AmericanTax,AmericanBonus,修改回原來的業務規則。一個可以立即想到的做法就是在系統中保留所有業務規則模型,即保留中國和美國企業工資運算規則。

前面系統的整合問題在於:當系統在客戶在美國和中國企業間切換時仍然需要修改Caculator代碼。

一個維護性良好的系統應該遵循“開閉原則”。即:封閉對原來代碼的修改,開放對原來代碼的擴展(如類的繼承,接口的實現)

我們發現不論是中國企業還是美國企業,他們的業務運規則都采用同樣的計算接口。 於是很自然地想到建立兩個業務接口類Tax,Bonus,然後讓AmericanTax、AmericanBonus和ChineseTax、ChineseBonus分別實現這兩個接口,類圖如下所示:

具體代碼如下所示:

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 獎金抽象類
    /// </summary>
    public abstract class Bonus
    {
        public abstract double Calculate();
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 計算中國個人獎金
    /// </summary>
    public class ChineseBonus:Bonus
    {
        public override double Calculate()
        {
            return Constant.BASE_SALARY * 0.1;
        }
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 個人所得稅抽象類
    /// </summary>
    public abstract class Tax
    {
        public abstract double Calculate();
    }
}

using System;

namespace InterfaceSalary
{    
    /// <summary>
    /// 計算中國個人所得稅
    /// </summary>
    public class ChineseTax:Tax
    {
        public override double Calculate()
        {
            return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4;
        }
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 公用的常量
    /// </summary>
    public class Constant
    {
        public static double BASE_SALARY = 4000;
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 客戶端程序調用
    /// </summary>
    public class Calculator 
    {
        public static void Main(string[] args) 
        {
            Bonus bonus = new ChineseBonus();
            double bonusValue  = bonus.Calculate();
    
            Tax tax = new ChineseTax();
            double taxValue = tax.Calculate();
    
            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("Chinaese Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

然而,上面增加的接口幾乎沒有解決任何問題,因為當系統的客戶在美國和中國企業間切換時Caculator代碼仍然需要修改。只不過修改少了兩處,但是仍然需要修改ChineseBonus,ChineseTax部分。致命的問題是:我們需要將這個移植工作轉包給一個叫Hippo的軟件公司。 由於版權問題,我們並未提供Softo系統的源碼給Hippo公司,因此Hippo公司根本無法修改Calculator,導致實際上移植工作無法進行。

為此,我們考慮增加一個工具類(命名為Factory),代碼如下:

using System;

namespace FactorySalary
{
    /// <summary>
    /// Factory類
    /// </summary>
    public class Factory
    {
        public Tax CreateTax()
        {
            return new ChineseTax();
        }

        public Bonus CreateBonus()
        {
            return new ChineseBonus();
        }
    }
}
修改後的客戶端代碼:
using System;

namespace FactorySalary
{
    /// <summary>
    /// 客戶端程序調用
    /// </summary>
    public class Calculator 
    {
        public static void Main(string[] args) 
        {
            Bonus bonus = new Factory().CreateBonus();
            double bonusValue  = bonus.Calculate();
    
            Tax tax = new Factory().CreateTax();
            double taxValue = tax.Calculate();
    
            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("Chinaese Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

不錯,我們解決了一個大問題,設想一下:當該系統從中國企業移植到美國企業時,我們現在需要做什麼?

答案是: 對於Caculator類我們什麼也不用做。我們需要做的是修改Factory類。

很顯然,前面的解決方案帶來了一個副作用:就是系統不但增加了新的類Factory,而且當系統移植時,移植工作僅僅是轉移到Factory類上,工作量並沒有任何縮減,而且還是要修改系統的源碼。 從Factory類在系統移植時修改的內容我們可以看出: 實際上它是專屬於美國企業或者中國企業的。名稱上應該叫AmericanFactory,ChineseFactory更合適.

解決方案是增加一個抽象工廠類AbstractFactory,增加一個靜態方法,該方法根據一個配置文件(App.config或者Web.config) 一個項(比如factoryName)動態地判斷應該實例化哪個工廠類,這樣,我們就把移植工作轉移到了對配置文件的修改。修改後的類圖如下:

抽象工廠類的代碼如下(使用反射模式):

using System;
using System.Reflection;

namespace AbstractFactory
{
    /// <summary>
    /// Factory類
    /// </summary>
    public abstract class AbstractFactory
    {
//        public AbstractFactory GetInstance()
//        {
//            string factoryName = Constant.STR_FACTORYNAME.ToString();
//
//            AbstractFactory instance;
//
//            if(factoryName == "ChineseFactory")
//                instance = new ChineseFactory();
//            else if(factoryName == "AmericanFactory")
//                instance = new AmericanFactory();
//            else
//                instance = null;
//
//            return instance;
//        }

        public AbstractFactory GetInstance()
        {
            string factoryName = Constant.STR_FACTORYNAME.ToString();

            AbstractFactory instance;

            if(factoryName != "")
                instance = (AbstractFactory)Assembly.Load(factoryName).CreateInstance(factoryName);
            else
                instance = null;

            return instance;
        }


        public abstract Tax CreateTax();

        public abstract Bonus CreateBonus();
    }
}

配置文件如下所示:


java 抽象工廠模式

工廠模式在項目中是常常用到的,有人說只有大項目才會用到,小項目是體會不出來.其實使用設計模式與項目的大小沒有實質性的聯系.設計模式是經驗的總結而不是衡量項目大小的標准.

以開發項目的DAO層為例,在項目中客戶的需求是常常變動的,臨時更換數據庫的需求也是常常發生的,那我們要如何解決跨數據庫的功能,這裡就要使用到抽象工廠模式了.工廠模式常常用於創建多系列化的對象(如Orale系列,MySql系列)

1.首先定義相關接口(與平常的做法沒什麼區別)

Java代碼
// 角色表DAO接口
interface IroleDao {
void insert();

void update();
}
// 用戶表DAO接口
interface IuserDao {
void find();

void delete();
}

// 角色表DAO接口
interface IroleDao {
void insert();

void update();
}
// 用戶表DAO接口
interface IuserDao {
void find();

void delete();
} 2.不同的數據庫有不同的SQL語句所以實現時必須分數據庫來實現

Java代碼
// 用戶表Oralce數據庫DAO
class OracleuserDao implements IuserDao {
public void delete() {
System.out.println("Oralce 刪除用戶表數據");
}

public void find() {
System.out.println("Oralce 查詢用戶表數據");
}
}

// 用戶表MySql數據庫DAO
class MySqluserDao implements IuserDao {
public void delete() {
System.out.println("MySql 刪除用戶數據");
}

public void find() {
System.out.println("MySql 查詢用戶數據");
}
}
// 角色表Oracle數據庫DAO
class OracleroleDao implements IroleDao {
public void insert() {
System.out.println("Oralce 對角色表插入數據");
}

public void update() {
System.out.println("Oracle 對角色表更新數據");
}
}

// 角色表MySql數據庫DAO
class MySqlrole......余下全文>>
 

設計模式都有什?

設計模式主要分三個類型:創建型、結構型和行為型。
其中創建型有:
一、Singleton,單例模式:保證一個類只有一個實例,並提供一個訪問它的全局訪問點
二、Abstract Factory,抽象工廠:提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們的具體類。
三、Factory Method,工廠方法:定義一個用於創建對象的接口,讓子類決定實例化哪一個類,Factory Method使一個類的實例化延遲到了子類。
四、Builder,建造模式:將一個復雜對象的構建與他的表示相分離,使得同樣的構建過程可以創建不同的表示。
五、Prototype,原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型來創建新的對象。
行為型有:
六、Iterator,迭代器模式:提供一個方法順序訪問一個聚合對象的各個元素,而又不需要暴露該對象的內部表示。
七、Observer,觀察者模式:定義對象間一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知自動更新。
八、Template Method,模板方法:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中,TemplateMethod使得子類可以不改變一個算法的結構即可以重定義該算法得某些特定步驟。
九、Command,命令模式:將一個請求封裝為一個對象,從而使你可以用不同的請求對客戶進行參數化,對請求排隊和記錄請求日志,以及支持可撤銷的操作。
十、State,狀態模式:允許對象在其內部狀態改變時改變他的行為。對象看起來似乎改變了他的類。
十一、Strategy,策略模式:定義一系列的算法,把他們一個個封裝起來,並使他們可以互相替換,本模式使得算法可以獨立於使用它們的客戶。
十二、China of Responsibility,職責鏈模式:使多個對象都有機會處理請求,從而避免請求的送發者和接收者之間的耦合關系
十三、Mediator,中介者模式:用一個中介對象封裝一些列的對象交互。
十四、Visitor,訪問者模式:表示一個作用於某對象結構中的各元素的操作,它使你可以在不改變各元素類的前提下定義作用於這個元素的新操作。
十五、Interpreter,解釋器模式:給定一個語言,定義他的文法的一個表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
十六、Memento,備忘錄模式:在不破壞對象的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。
結構型有:
十七、Composite,組合模式:將對象組合成樹形結構以表示部分整體的關系,Composite使得用戶對單個對象和組合對象的使用具有一致性。
十八、Facade,外觀模式:為子系統中的一組接口提供一致的界面,fa?ade提供了一高層接口,這個接口使得子系統更容易使用。
十九、Proxy,代理模式:為其他對象提供一種代理以控制對這個對象的訪問
二十、Adapter,適配器模式:將一類的接口轉換成客戶希望的另外一個接口,Adapter模式使得原本由於接口不兼容而不能一起工作那些類可以一起工作。
二十一、Decrator,裝飾模式:動態地給一個對象增加一些額外的職責,就增加的功能來說,Decorator模式相比生成子類更加靈活。
二十二、Bridge,橋模式:將抽象部分與它的實現部分......余下全文>>
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved