。 工廠模式同樣是項目中最常用的設計模式,工廠模式中又分為簡單工廠,工廠方法,抽象工廠。下面我們由簡單的開始逐一介紹。
1.簡單工廠模式
簡單工廠又被稱為靜態工廠,在設計模式中屬於創建型模式。主要解決的問題是封裝了實例化的過程,通過傳入參數來獲不同實例。下面我們舉一個項目中可能會用到的例子。
假設我們程序的數據保存在幾個不同的數據庫中,有MySql,SQLServer和MongoDB。數據庫都有增刪改查的操作,我們就聲明一個接口定義這些操作。
1 public interface IDatabase 2 { 3 void Insert(); 4 void Delete(); 5 }
然后我們讓三個數據庫類分別實現這個接口。
1 public class SqlServer : IDatabase 2 { 3 public void Delete() 4 { 5 Console.WriteLine("delete data from sqlserver"); 6 } 7 8 public void Insert() 9 { 10 Console.WriteLine("insert data to sqlserver"); 11 } 12 } 13 14 public class MySql : IDatabase 15 { 16 public void Delete() 17 { 18 Console.WriteLine("delete data from mysql"); 19 } 20 21 public void Insert() 22 { 23 Console.WriteLine("insert data to mysql"); 24 } 25 } 26 27 public class MongoDB : IDatabase 28 { 29 public void Delete() 30 { 31 Console.WriteLine("delete data from mongoDb"); 32 } 33 34 public void Insert() 35 { 36 Console.WriteLine("insert data to mongoDb"); 37 } 38 }
之后我們再聲明一個工廠類,這個類中的靜態方法可以根據傳入不同的參數來創建不同的實例。
1 public static class DatabaseFactory 2 { 3 public static IDatabase CreateDatabase(string dbType) 4 { 5 IDatabase db = null; 6 7 switch (dbType) 8 { 9 case "MySql": 10 db = new MySql(); 11 break; 12 case "SqlServer": 13 db = new SqlServer(); 14 break; 15 case "MongoDB": 16 db = new MongoDB(); 17 break; 18 default: 19 break; 20 } 21 22 return db; 23 } 24 }
最后我們再Main函數里聲明三個接口,然后給工廠類傳入不同的參數來創建三個不同的實例,再分別調用接口中聲明的方法。
1 static void Main(string[] args) 2 { 3 IDatabase db1 = DatabaseFactory.CreateDatabase("MySql"); 4 db1.Insert(); 5 db1.Delete(); 6 7 IDatabase db2 = DatabaseFactory.CreateDatabase("SqlServer"); 8 db2.Insert(); 9 db2.Delete(); 10 11 IDatabase db3 = DatabaseFactory.CreateDatabase("MongoDB"); 12 db3.Insert(); 13 db3.Delete(); 14 }
來看一些控制台的輸出:
這就是簡單工廠模式。
我們可以看到簡單工廠模式的優點:
1.拓展性好,如果這時候我們又添加了一個Oracle數據庫,只需要再添加一個新的類並實現IDatabase這個這個接口就行了。
2.我們只需要關注接口中的方法,不需要關注具體類的實現。
缺點:只適用於工廠需要創建比較少的具體類這樣的情況。如果具體類多,代碼的復雜程度會增加。
2.工廠模式
工廠模式在簡單工廠模式的基礎上進行了更加全面的面向對象封裝,可以讓我們不要單獨的工廠方法就能創建出具體的實例。做法就是為每一個具體的類創建單獨的工廠。接下來我們對剛剛幾個類稍加改造。
首先發DatabaseFactory修改成一個接口,接口中定義一個用來創建實例的方法。
1 interface IDatabaseFactory 2 { 3 IDatabase CreateDatabase(); 4 }
然后我們然后我們為每個具體類單獨創建一個工廠類,工廠類實現剛剛定義的接口。
1 public class MongoDbFactory : IDatabaseFactory 2 { 3 public IDatabase CreateDatabase() 4 { 5 return new MongoDB(); 6 } 7 } 8 9 public class MySqlFactory : IDatabaseFactory 10 { 11 public IDatabase CreateDatabase() 12 { 13 return new MySql(); 14 } 15 } 16 17 public class SqlServerFactory : IDatabaseFactory 18 { 19 public IDatabase CreateDatabase() 20 { 21 return new SqlServer(); 22 } 23 }
最后我們在main函數中創建工廠的實例。
static void Main(string[] args) { IDatabaseFactory dbFactory1 = new MySqlFactory(); IDatabase db1 = dbFactory1.CreateDatabase(); db1.Insert(); db1.Delete(); IDatabaseFactory dbFactory2 = new SqlServerFactory(); IDatabase db2 = dbFactory1.CreateDatabase(); db2.Insert(); db2.Delete(); IDatabaseFactory dbFactory3 = new MongoDbFactory(); IDatabase db3 = dbFactory3.CreateDatabase(); db3.Insert(); db3.Delete(); }
結果輸出是和剛剛一模一樣的。工廠模式的好處便是它符合開閉原則(對擴展開放,對修改封閉)。在剛剛的簡單工廠模式中,如果我們擴展一個新的類,除了添加一個新的類之外,我們還需要去修改CrateDatabase(string dbType)這個方法,這是違反開閉原則的。在工廠模式中我們就不需要修改CreateDatabase這個方法,只需要實現工廠類這個接口便能完場擴展。缺點便是我們需要寫更多的代碼。
3.抽象工廠模式
有了前面工廠模式的鋪墊,抽象工廠應該不難理解吧。我看到過很多的博客都寫着很多概念,什么產品層級,產品族,抽象產品等等,感覺不是特別容易理解。我的理解是這樣的:把多個不同的工廠再抽象出來,再用一個抽象工廠(超級工廠)來創建這些工廠。也就是說抽象工廠是工廠的工廠。為了說明這個模式我想出了一個例子(其實我在工作中沒有遇到過使用抽象工廠的例子):
操作系統有Windows操作系統,Linux操作系統。每個操作系統都可以啟動關閉。於是我們就創建一個操作系統工廠,用來創建(安裝)這些操作系統,方法和上面的創建數據庫工廠是一樣的。
// 操作系統具有的操作 public interface IOpSystem { void Start(); void Shutdown(); } // 操作系統工廠 public interface IOpSystemFactory { IOpSystem InstallSystem(); } // windows操作系統 public class WindowsSystem : IOpSystem { public void Shutdown() { Console.WriteLine("windows shutdown"); } public void Start() { Console.WriteLine("windows start"); } } // linux操作系統 public class LinuxSystem : IOpSystem { public void Shutdown() { Console.WriteLine("linux shutdown"); } public void Start() { Console.WriteLine("linux start"); } } // windows操作系統工廠,用來創建windows系統實例 public class WindowsFactory : IOpSystemFactory { public IOpSystem InstallSystem() { return new WindowsSystem(); } } // linux操作系統工廠,用來創建linux系統實例 public class LinuxFactory : IOpSystemFactory { public IOpSystem InstallSystem() { return new LinuxSystem(); } }
我們可以看到操作系統工廠和數據庫工廠是完全兩個不同的工廠。假設一台服務器上需要安裝操作系統和數據庫,我們便可以用一個超級工廠來把這兩個不同的工廠抽象出來。
public interface ISuperFactory { IDatabaseFactory InstallDB(); IOpSystemFactory InstallOpSystem(); }
然后我們定義一個具體的服務器類來實現這個超級工廠,在接口的實現中我們讓這個服務器類安裝windows system和mysql db。
public class ServerWithWindowsAndMySql : ISuperFactory { public IDatabaseFactory InstallDB() { return new MySqlFactory(); } public IOpSystemFactory InstallOpSystem() { return new WindowsFactory(); } }
最后在Main函數中調用看看。
static void Main(string[] args) { ISuperFactory server1 = new ServerWithWindowsAndMySql(); server1.InstallDB().CreateDatabase().Delete(); server1.InstallDB().CreateDatabase().Insert(); server1.InstallOpSystem().InstallSystem().Start(); server1.InstallOpSystem().InstallSystem().Shutdown(); }
下面是運行結果。
我們可以看到實現了超級工廠的服務器類同時擁有了創建數據庫和安裝操作系統的功能。這就是抽象工廠的用法。我們來看看抽象工廠的優缺點。優點:
1.實現了不同工廠之間的解耦。
缺點:
1.代碼量成倍的增加
2.抽象工廠並不符合開閉原則。如果這個時候我們需要在超級工廠中添加一個新的工廠,那么具體類也必須要作出修改。
4.總結
工廠模式同樣是設計模式中比較常用而且比較容易理解(抽象工廠除外)的設計模式。同時也能加深我們對面向對象中“多態”這個概念的理解:我們只需要關注接口中方法的聲明,不用知道具體類有什么方法方法如何實現。換句話說我們只需要聲明了一個接口,便可以直接調用接口的方法,當然前提是接口必須由實現該接口的具體類來實例化。同時我們在工作中也必須對設計模式的使用稍加思考,只有我們需要去使用這個設計模式的時候才去使用,如果我們為了使用設計模式而去使用設計模式,我們反而會得到糟糕的效果。