抽象工厂模式
前面我们介绍了简单工厂模式和工厂方法模式,现在简单的回顾一下:
简单工厂模式是充分利用面向对象的三大特性,将操作要素和操作方法对象化,通过定义工厂方法,决定实例化哪个对象。但简单工厂模式不利于程序的扩展,在需要扩展的时候需要将整个工程类开放,不符合开放-封闭原则。
在简单工厂模式的基础上,我们通过继承接口实现个操作对象的实例化,各具体操作类各自实现相应的方法,只需实现工厂类接口方法即可,在对功能进行扩展时,不需要对工厂类接口进行修改,只需新增操作类和实例化自己的工厂类即可。
那我们为什么需要抽象工厂模式呢?
当我们项目的数据库需要从sqlserver改成oracle时,当初用抽象工厂模式设计的优点就体现出来了。
抽象工厂模式:创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
以数据访问为例:
1 interface IUser 2 { 3 void Insert(User user); 4 5 User GetUser(int id); 6 }
1 class SqlserverUser : IUser 2 { 3 public void Insert(User user) 4 { 5 Console.WriteLine("在Sqlserver中给User表增加一条记录"); 6 } 7 8 public User GetUser(int id) 9 { 10 Console.WriteLine("在Sqlserver中根据ID得到User表一条记录"); 11 return null; 12 } 13 } 14 15 class AccessUser : IUser 16 { 17 public void Insert(User user) 18 { 19 Console.WriteLine("在Access中给User表增加一条记录"); 20 } 21 22 public User GetUser(int id) 23 { 24 Console.WriteLine("在Access中根据ID得到User表一条记录"); 25 return null; 26 } 27 }
1 interface IFactory 2 { 3 IUser CreateUser(); 4 } 5 6 class SqlServerFactory : IFactory 7 { 8 public IUser CreateUser() 9 { 10 return new SqlserverUser(); 11 } 12 } 13 14 class AccessFactory : IFactory 15 { 16 public IUser CreateUser() 17 { 18 return new AccessUser(); 19 } 20 }
客户端调用:
1 User user = new User();
//IFactory factory=new SqlServerFactory();//SqlServer调用 3 IFactory factory = new AccessFactory();//Access调用 4 5 IUser iu = factory.CreateUser(); 6 7 iu.Insert(user); 8 iu.GetUser(1); 9 10 Console.Read();
当我们新增了一个表时,我们至少要新增四个类:表对象类,表对应接口类,表对象操作在sqlserver类,表对象操作在access类;修改三个类:工厂接口类,工厂接口类在sqlserver中实现类,工厂接口类在access中实现类。
还有就是当我们更改数据库时,程序中所有对工厂类进行实例化的地方都要修改。为此,我们可以通过简单工厂来改进抽象工厂:
将工厂接口类,工厂接口类在sqlserver中实现类,工厂接口类在access中实现类融合到一个类,也就是说将数据访问判断从客户端移到统一类:
1 class DataAccess 2 { 3 private static readonly string db = "Sqlserver"; 4 //private static readonly string db = "Access"; 5 6 public static IUser CreateUser() 7 { 8 IUser result = null; 9 switch (db) 10 { 11 case "Sqlserver": 12 result = new SqlserverUser(); 13 break; 14 case "Access": 15 result = new AccessUser(); 16 break; 17 } 18 return result; 19 } 20 21 public static IDepartment CreateDepartment() 22 { 23 IDepartment result = null; 24 switch (db) 25 { 26 case "Sqlserver": 27 result = new SqlserverDepartment(); 28 break; 29 case "Access": 30 result = new AccessDepartment(); 31 break; 32 } 33 return result; 34 } 35 }
但现在又有新的问题:当我们需要增加oracle数据访问时,需要在dataaccess类中的每个方法新增switch。
为了解决这个问题,我们需要用到反射机制。
使用.net里面的system.Reflection实现反射:
1 class DataAccess 2 { 3 private static readonly string AssemblyName = "抽象工厂模式";//命名空间 4 private static readonly string db = "Sqlserver";//要实例化的类前缀 5 //private static readonly string db = "Access"; 6 7 public static IUser CreateUser() 8 { 9 string className = AssemblyName + "." + db + "User"; 10 return (IUser)Assembly.Load(AssemblyName).CreateInstance(className); 11 } 12 13 public static IDepartment CreateDepartment() 14 { 15 string className = AssemblyName + "." + db + "Department"; 16 return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); 17 } 18 }
当然为了在更换数据库时完全不修改程序,我们可以用配置文件代替,在程序中读取配置文件。
private static readonly string db = "Sqlserver";//要实例化的类前缀
这样我们就可以在不修改DataAccess类的情况下更换数据库。