一、簡單工廠模式:
需要增加運算形式時只需要增加運算子類,同時修改簡單工廠中的switch增加分支即可,這樣客戶端調用代碼基本不動。
// 運算類 public class Operation { private double _numberA = 0; private double _numberB = 0; // 數字A屬性
public double NumberA { get { return _numberA; } set { _numberA = value; } } // 數字B屬性 public double NumberB { get { return _numberB; } set { _numberB = value; } } // 得到運算結果 public virtual double getResult() { double result = 0; return result; } }
// 加法類 class OperationAdd : Operation { public override double getResult() { double result = 0; result = NumberA + NumberB; return result; } } // 減法類 class OperationSub : Operation { public override double getResult() { double result = 0; result = NumberA - NumberB; return result; } } // 乘法類 class OperationMul : Operation { public override double getResult() { double result = 0; result = NumberA * NumberB; return result; } } // 除法類 class OperationDiv : Operation { public override double getResult() { double result = 0; if (NumberB==0) throw new Exception("除數不能為0。"); result = NumberA / NumberB; return result; } }
// 簡單類工廠 class OperationFactory { public static Operation createOperate(string operate) { Operation oper = null; switch (operate) { case "+": { oper = new OperationAdd(); break; } case "-": { oper = new OperationSub(); break; } case "*": { oper = new OperationMul(); break; } case "/": { oper = new OperationDiv(); break; } } return oper; } }
//控制台調用 static void Main(string[] args) { try { Console.Write("請輸入數字A:"); string strNumberA = Console.ReadLine(); Console.Write("請選擇運算符號(+、-、*、/):"); string strOperate = Console.ReadLine(); Console.Write("請輸入數字B:"); string strNumberB = Console.ReadLine(); string strResult = ""; Operation oper; //聲明運算類 oper = OperationFactory.createOperate(strOperate); //根據運算符調用工廠類初始化相應運算子類 oper.NumberA = Convert.ToDouble(strNumberA); oper.NumberB = Convert.ToDouble(strNumberB); strResult = oper.GetResult().ToString(); Console.WriteLine("結果是:" + strResult); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("您的輸入有錯:" + ex.Message); } }
二、工廠方法模式:
與簡單工廠模式相比,增加了工廠類的虛類,並針對每一個運算子類編寫了一個工廠方法;當增加運算方式時只需增加運算子類和工廠子類,不需對原有類進行修改。運算類代碼見上。
工廠方法模式實現時,客戶端需要決定實例化哪一個工廠來實現運算類,只是將簡單工廠類的內部邏輯判斷(switch)移到了客戶端代碼來進行。
// 工廠類,接口,Creator interface IFactory { Operation CreateOperation(); }
// 加法工廠,ConcreteCreator class AddFactory : IFactory { public Operation CreateOperation() { return new OperationAdd(); // 初始化加法類(ConcreteProduct)
} } //減法工廠,ConcreteCreator class SubFactory : IFactory { public Operation CreateOperation() { return new OperationSub(); } } // 乘法工廠,ConcreteCreator class MulFactory : IFactory { public Operation CreateOperation() { return new OperationMul(); } } ///除法工廠,ConcreteCreator class DivFactory : IFactory { public Operation CreateOperation() { return new OperationDiv(); } }
static void Main(string[] args) //客戶端調用 { IFactory operFactory = new AddFactory(); //初始化加法工廠 Operation oper = operFactory.CreateOperation(); //初始化加法類 oper.NumberA = 1; oper.NumberB = 2; double result=oper.GetResult(); Console.WriteLine(result); Console.Read(); }
三、抽象工廠模式
抽象工廠模式與工廠方法模式的區別是在於當涉及到多個產品系列又是不同的分類時,對專門的工廠模式的叫法而已。從圖上理解就是工廠方法模式解決的問題只有一個A產品,而抽象工廠模式解決A、B、……甚至更多的產品。
優點:1.具體類只初始化一次,修改代碼時方便;2.讓具體的創建實例過程與客戶端分離,產品的具體類名被具體工廠的實現分離,不會出現在客戶代碼中。
增加產品時需增加AbstractProductC、ProductC1、ProductC2,修改AbstractFactory、ConcreteFactory1和ConcreteFactory2增加CreateProductC()方法。
//用戶表,用戶信息
class User { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _name; public string Name { get { return _name; } set { _name = value; } } } //部門表,部門信息class Department { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _deptName; public string DeptName { get { return _deptName; } set { _deptName = value; } } }//用戶信息接口,AbstractProductAinterface IUser { void Insert(User user); User GetUser(int id); }//SQL實現用戶信息操作,ProductA1class SqlserverUser : IUser { public void Insert(User user) { Console.WriteLine("在Sqlserver中給User表增加一條記錄"); } public User GetUser(int id) { Console.WriteLine("在Sqlserver中根據ID得到User表一條記錄"); return null; } }//ACCESS實現用戶信息操作,ProductA2class AccessUser : IUser { public void Insert(User user) { Console.WriteLine("在Access中給User表增加一條記錄"); } public User GetUser(int id) { Console.WriteLine("在Access中根據ID得到User表一條記錄"); return null; } }//部門信息接口,AbstractProductBinterface IDepartment { void Insert(Department department); Department GetDepartment(int id); }//SQL實現部門信息操作,ProductB1class SqlserverDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Sqlserver中給Department表增加一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Sqlserver中根據ID得到Department表一條記錄"); return null; } } //ACCESS實現部門信息操作,ProductB2class AccessDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Access中給Department表增加一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Access中根據ID得到Department表一條記錄"); return null; } }//工廠接口,AbstractFactoryinterface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); }//SQL具體工廠類,ConcreteFactory1class SqlServerFactory : IFactory { public IUser CreateUser() { return new SqlserverUser(); } public IDepartment CreateDepartment() { return new SqlserverDepartment(); } }//Accese具體工廠類,ConcreteFactory2class AccessFactory : IFactory { public IUser CreateUser() { return new AccessUser(); } public IDepartment CreateDepartment() { return new AccessDepartment(); } }//客戶端調用 static void Main(string[] args) { //初始化兩個產品 User user = new User(); Department dept = new Department(); //選擇實現哪個工廠 IFactory factory = new AccessFactory(); IUser iu = factory.CreateUser(); iu.Insert(user); iu.GetUser(1); IDepartment id = factory.CreateDepartment(); id.Insert(dept); id.GetDepartment(1); Console.Read(); } }
四、對抽象工廠模式的改進
改進手段:簡單工廠+反射
第一步:取消AbstractFactory、ConcreteFactory1和ConcreteFactory2,用簡單工廠類來實現。
//取消接口,只用一個類 class DataAccess { //指定用什麼數據庫 private static readonly string db = "Sqlserver"; //private static readonly string db = "Access"; //用戶信息維護,根據指定判斷用哪個具體產品 public static IUser CreateUser() { IUser result = null; switch (db) { case "Sqlserver": result = new SqlserverUser(); break; case "Access": result = new AccessUser(); break; } return result; } //部門信息維護,根據指定判斷用哪個具體產品 public static IDepartment CreateDepartment() { IDepartment result = null; switch (db) { case "Sqlserver": result = new SqlserverDepartment(); break; case "Access": result = new AccessDepartment(); break; } return result; } }//在客戶端程序中不用指定用哪個數據庫,只需要操作即可 static void Main(string[] args) { //初始化 User user = new User(); Department dept = new Department(); IUser iu = DataAccess.CreateUser(); iu.Insert(user); iu.GetUser(1); IDepartment id = DataAccess.CreateDepartment(); id.Insert(dept); id.GetDepartment(1); Console.Read(); }第二步,替換簡單工廠中部分代碼,采用反射,可以將原執行語句部分變成字符串形式易於變化。
//使用反射時,必須引用 using System.Reflection; //Assembly.Load(‘程序集名稱’).CreateInstance('命名空間.類名稱'); class DataAccess { //指定程序集名稱,同時在配置文件中獲取‘DB’的值,在配置文件中指定數據庫類型 private static readonly string AssemblyName = "抽象工廠模式"; private static readonly string db = ConfigurationManager.AppSettings["DB"]; //指定類名稱,遵循命名規則 public static IUser CreateUser() { string className = AssemblyName + "." + db + "User"; return (IUser)Assembly.Load(AssemblyName).CreateInstance(className); } public static IDepartment CreateDepartment() { string className = AssemblyName + "." + db + "Department"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); } }//配置文件中的寫法 <configuration> <appSettings> <add key="DB" value="Sqlserver"/> </appSettings> </configuration>
PS:所有在用簡單工廠的地方,都可以考慮用反射技術來去除switch或if,接觸分支判斷帶來的耦合。