创建类模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的是由抽象类所定义的接口;这样,创建类模式在创建了什么,谁创建它的,它怎么被创建的,以及何时创建这些方面提供了很大的灵活性。创建类模式抽象了实例化的过程,允许客户用结构和功能差别很大的“产品”对象配置一个系统配置可以是静态的,即在编译时指定,也可以是动态的,就是运行时再指定。
一、简单工厂模式:
需要增加运算形式时只需要增加运算子类,同时修改简单工厂中的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; } } }//用户信息接口,AbstractProductA
interface 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; }}//部门信息接口,AbstractProductB
interface IDepartment { void Insert(Department department); Department GetDepartment(int id); }//SQL实现部门信息操作,ProductB1
class 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; } }//工厂接口,AbstractFactory
interface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); }//SQL具体工厂类,ConcreteFactory1
class SqlServerFactory : IFactory { public IUser CreateUser() { return new SqlserverUser(); } public IDepartment CreateDepartment() { return new SqlserverDepartment(); } }//Accese具体工厂类,ConcreteFactory2
class 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,解除分支判断带来的耦合。
五、总结
工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法模式使一个类的实例化延迟到其子类;
抽象工厂:提供一个创建一系列或相关依赖对象的接口,而无需指定它们具体的类。
设计通常从工厂方法开始,当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化;当设计者在设计标准之间进行权衡的时候,了解多个创建型模式可以给设计者更多的选择余地。
新闻热点
疑难解答