题目1:数据访问,通过数据库对用户表单的进行访问,数据库包含SQL Server,对用户表单进行“新增用户”和“查询用户”信息等操作。
分析:
首先,确认用户表单,里面包含两个ID和Name两个字段,两种字段可以读写功能;
单独准备一个数据库的类,然后直接对用户直接进行操作即可。
实现:
1 using System; 2 3 namespace 抽象工厂模式 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 //向su数据库中添加user这个新的用户10 User user = new User();11 // bug:不能更换数据库的原因是su这个数据库已经被框死在了SQL Server上12 SqlserverUser su = new SqlserverUser();13 su.Insert(user);14 15 //在su的数据库中查找unknownUser这个用户16 User unknownUser = new User();17 unknownUser = su.GetUser(5);18 19 Console.Read();20 }21 }22 23 ///24 /// 用户类25 /// 准备ID和Name两个字段26 /// 27 class User28 {29 private int _id;30 private string _name;31 32 public int Id33 {34 get { return _id; }35 set { _id = value; }36 }37 public string Name38 {39 get { return _name; }40 set { _name = value; }41 }42 }43 44 ///45 /// 用于操作User表46 /// “增加用户”和“得到用户”两种方法47 /// 48 class SqlserverUser49 {50 ///51 /// 增加用户信息52 /// 53 /// 新的用户信息54 public void Insert(User user)55 {56 Console.WriteLine("在SQL server中给User增加一条记录");57 }58 ///59 /// 得到用户方法60 /// 61 /// (传入信息)通过用户的id值62 ///(返回信息)通过id值得到对应的用户数据 63 public User GetUser(int id)64 {65 Console.WriteLine("在SQL Server中根据ID得到User表一条记录");66 return null;67 }68 }69 }
题目2:在1的基础上,我们增加新的数据库Access,同样对刚才的数据库进行访问。
分析:
基于1的基础上,SqlserverUser su = new SqlserverUser();将数据库的对象定死在了SQL Server上了,这就出现了很大的局限性。
为了将SQL与Access灵活使用,我们就需要用到工厂方法模式来封装数据库,让子类决定实例化哪一个类。
实现:
1 using System; 2 3 namespace 抽象工厂模式2 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 //向iu数据库中添加user这个新的用户 10 User user = new User(); 11 IFactory factory = new SqlServerFactory(); //若要更改成Access数据库 将SqlServerFactory换成AccessFactory即可 12 IUser iu = factory.CreateUser(); 13 iu.Insert(user); 14 15 //在iu的数据库中查找unknownUser这个用户 16 User unknownUser = new User(); 17 unknownUser = iu.GetUser(5); 18 19 Console.Read(); 20 } 21 } 22 23 ///24 /// 用户类 25 /// 准备ID和Name两个字段 26 /// 27 class User 28 { 29 private int _id; 30 private string _name; 31 32 public int Id 33 { 34 get { return _id; } 35 set { _id = value; } 36 } 37 public string Name 38 { 39 get { return _name; } 40 set { _name = value; } 41 } 42 } 43 44 45 interface IUser 46 { 47 void Insert(User user); 48 User GetUser(int id); 49 } 50 51 ///52 /// 用于访问SQL Server的User 53 /// “增加用户”和“得到用户”两种方法 54 /// 55 class SqlserverUser:IUser 56 { 57 ///58 /// 增加用户信息 59 /// 60 /// 新的用户信息 61 public void Insert(User user) 62 { 63 Console.WriteLine("在SQL server中给User增加一条记录"); 64 } 65 ///66 /// 得到用户方法 67 /// 68 /// (传入信息)通过用户的id值 69 ///(返回信息)通过id值得到对应的用户数据 70 public User GetUser(int id) 71 { 72 Console.WriteLine("在SQL Server中根据ID得到User表一条记录"); 73 return null; 74 } 75 } 76 ///77 /// 用户访问Access的User 78 /// 79 class AccessUser : IUser 80 { 81 public User GetUser(int id) 82 { 83 Console.WriteLine("在Access中根据ID得到User表一条记录"); 84 return null; 85 } 86 87 public void Insert(User user) 88 { 89 Console.WriteLine("在Access中给User增加一条记录"); 90 } 91 } 92 ///93 /// 接口 94 /// 定义了一个创建访问User表对象的抽象的工厂接口 95 /// 96 interface IFactory 97 { 98 //因为sql和access都继承于IUser,所以返回值设定为IUser即可 99 IUser CreateUser();100 }101 ///102 /// 实现IFactory接口,实例化SqlserverUser103 /// 104 class SqlServerFactory : IFactory105 {106 public IUser CreateUser()107 {108 return new SqlserverUser();109 }110 }111 ///112 /// 实现IFactory接口,实例化AccessUser113 /// 114 class AccessFactory : IFactory115 {116 public IUser CreateUser()117 {118 return new AccessUser();119 }120 }121 }
题目3:在2是我基础上,我们再增加一个信息表,例如Department表来记录信息。
分析:
这里Department与User是一样信息,所以和Department的构造与User类似,再同时修改工厂类及其子类信息即可。
这里需要使用的就是抽象工厂模式(Abstract Factory),提供一个 创建一系列相关或者互相依赖对象的接口,而无需指定他们具体的类。
实现:
1 using System; 2 3 namespace 抽象工厂模式3 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 //向iu数据库中添加user这个新的用户 10 User user = new User(); 11 IFactory factory = new AccessFactory(); 12 IUser iu = factory.CreateUser(); 13 iu.Insert(user); 14 15 //在iu的数据库中查找unknownUser这个用户 16 User unknownUser = new User(); 17 unknownUser = iu.GetUser(5); 18 19 //向id数据库中添加dept这个新的用户 20 Department dept = new Department(); 21 IDepartment id = factory.CreateDepartment(); 22 id.Insert(dept); 23 24 //在id的数据库中查找unknownDept这个用户 25 Department unknownDept = new Department(); 26 unknownDept = id.GetDepartment(3); 27 28 Console.Read(); 29 } 30 } 31 32 33 ///34 /// 用户类 35 /// 准备ID和Name两个字段 36 /// 37 class User 38 { 39 private int _id; 40 private string _name; 41 42 public int Id 43 { 44 get { return _id; } 45 set { _id = value; } 46 } 47 public string Name 48 { 49 get { return _name; } 50 set { _name = value; } 51 } 52 } 53 ///54 /// 增加一个Department表 55 /// 56 class Department 57 { 58 private int _id; 59 private string _deptName; 60 61 public int Id 62 { 63 get { return _id; } 64 set { _id = value; } 65 } 66 67 public string DeptName 68 { 69 get { return _deptName; } 70 set { _deptName = value; } 71 } 72 } 73 74 75 76 interface IUser 77 { 78 void Insert(User user); 79 User GetUser(int id); 80 } 81 82 ///83 /// 用于访问SQL Server的User 84 /// “增加用户”和“得到用户”两种方法 85 /// 86 class SqlserverUser : IUser 87 { 88 ///89 /// 增加用户信息 90 /// 91 /// 新的用户信息 92 public void Insert(User user) 93 { 94 Console.WriteLine("在SQL server中给User增加一条记录"); 95 } 96 ///97 /// 得到用户方法 98 /// 99 /// (传入信息)通过用户的id值100 ///(返回信息)通过id值得到对应的用户数据 101 public User GetUser(int id)102 {103 Console.WriteLine("在SQL Server中根据ID得到User表一条记录");104 return null;105 }106 }107 ///108 /// 用户访问Access的User109 /// 110 class AccessUser : IUser111 {112 public User GetUser(int id)113 {114 Console.WriteLine("在Access中根据ID得到User表一条记录");115 return null;116 }117 118 public void Insert(User user)119 {120 Console.WriteLine("在Access中给User增加一条记录");121 }122 }123 124 125 126 ///127 /// 与User表一致128 /// 129 interface IDepartment130 {131 void Insert(Department department);132 Department GetDepartment(int id);133 }134 class SqlserverDepartment : IDepartment135 {136 public Department GetDepartment(int id)137 {138 Console.WriteLine("在SQL Server中根据ID得到Department表一条记录");139 return null;140 }141 142 public void Insert(Department department)143 {144 Console.WriteLine("在SQL server中给Department增加一条记录");145 }146 }147 class AccessDepartment : IDepartment148 {149 public Department GetDepartment(int id)150 {151 Console.WriteLine("在Access中根据ID得到Department表一条记录");152 return null;153 }154 155 public void Insert(Department department)156 {157 Console.WriteLine("在Access中给Department增加一条记录");158 }159 160 }161 162 163 164 165 ///166 /// 接口167 /// 定义了一个创建访问User表对象的抽象的工厂接口168 /// 169 interface IFactory170 {171 //因为sql和access都继承于IUser,所以返回值设定为IUser即可172 IUser CreateUser();173 IDepartment CreateDepartment();174 }175 ///176 /// 实现IFactory接口,实例化SqlserverUser177 /// 178 class SqlServerFactory : IFactory179 {180 public IDepartment CreateDepartment()181 {182 return new SqlserverDepartment();183 }184 185 public IUser CreateUser()186 {187 return new SqlserverUser();188 }189 190 }191 ///192 /// 实现IFactory接口,实例化AccessUser193 /// 194 class AccessFactory : IFactory195 {196 public IDepartment CreateDepartment()197 {198 return new AccessDepartment();199 }200 201 public IUser CreateUser()202 {203 return new AccessUser();204 }205 }206 }
题目4:在3中我们可以得知,如果增加一个信息表Project,就意味着需要增加信息表本身的三个类:IProject、SqlserverProject、AccessProject,同时还需要更改Ifactory、SqlserverFactory和AccessFactory才可以完全实现,那么如果我有100个调用数据库访问的类,工作量将变得巨大。
分析:
信息表本身的类是无法删减的,所以我们只有从工厂类来入手,我们将IFactory、SqlserverFactory和AccessFactory三个工厂类去除,用一个DataAccess类来代替,简化代码。
实现:
1 using System; 2 3 namespace 抽象工厂模式4 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 //向iu数据库中添加user这个新的用户 10 User user = new User(); 11 IUser iu = DataAccess.CreateUser(); //直接得到实际的数据库访问实例,不存在任何依赖 12 iu.Insert(user); 13 14 //在iu的数据库中查找unknownUser这个用户 15 User unknownUser = new User(); 16 unknownUser = iu.GetUser(5); 17 18 //向id数据库中添加dept这个新的用户 19 Department dept = new Department(); 20 IDepartment id = DataAccess.CreateDepartment(); 21 id.Insert(dept); 22 23 //在id的数据库中查找unknownDept这个用户 24 Department unknownDept = new Department(); 25 unknownDept = id.GetDepartment(3); 26 27 Console.Read(); 28 } 29 } 30 31 32 ///33 /// 用户类 34 /// 准备ID和Name两个字段 35 /// 36 class User 37 { 38 private int _id; 39 private string _name; 40 41 public int Id 42 { 43 get { return _id; } 44 set { _id = value; } 45 } 46 public string Name 47 { 48 get { return _name; } 49 set { _name = value; } 50 } 51 } 52 ///53 /// 增加一个Department表 54 /// 55 class Department 56 { 57 private int _id; 58 private string _deptName; 59 60 public int Id 61 { 62 get { return _id; } 63 set { _id = value; } 64 } 65 66 public string DeptName 67 { 68 get { return _deptName; } 69 set { _deptName = value; } 70 } 71 } 72 73 74 75 interface IUser 76 { 77 void Insert(User user); 78 User GetUser(int id); 79 } 80 81 ///82 /// 用于访问SQL Server的User 83 /// “增加用户”和“得到用户”两种方法 84 /// 85 class SqlserverUser : IUser 86 { 87 ///88 /// 增加用户信息 89 /// 90 /// 新的用户信息 91 public void Insert(User user) 92 { 93 Console.WriteLine("在SQL server中给User增加一条记录"); 94 } 95 ///96 /// 得到用户方法 97 /// 98 /// (传入信息)通过用户的id值 99 ///(返回信息)通过id值得到对应的用户数据 100 public User GetUser(int id)101 {102 Console.WriteLine("在SQL Server中根据ID得到User表一条记录");103 return null;104 }105 }106 ///107 /// 用户访问Access的User108 /// 109 class AccessUser : IUser110 {111 public User GetUser(int id)112 {113 Console.WriteLine("在Access中根据ID得到User表一条记录");114 return null;115 }116 117 public void Insert(User user)118 {119 Console.WriteLine("在Access中给User增加一条记录");120 }121 }122 123 124 125 ///126 /// 与User表一致127 /// 128 interface IDepartment129 {130 void Insert(Department department);131 Department GetDepartment(int id);132 }133 class SqlserverDepartment : IDepartment134 {135 public Department GetDepartment(int id)136 {137 Console.WriteLine("在SQL Server中根据ID得到Department表一条记录");138 return null;139 }140 141 public void Insert(Department department)142 {143 Console.WriteLine("在SQL server中给Department增加一条记录");144 }145 }146 class AccessDepartment : IDepartment147 {148 public Department GetDepartment(int id)149 {150 Console.WriteLine("在Access中根据ID得到Department表一条记录");151 return null;152 }153 154 public void Insert(Department department)155 {156 Console.WriteLine("在Access中给Department增加一条记录");157 }158 159 }160 161 162 class DataAccess163 {164 //在此确定需要使用的数据库信息165 private static readonly string db = "Sqlserver";166 //private static readonly string db = "Access";167 public static IUser CreateUser()168 {169 IUser result = null;170 switch (db)171 {172 case "Sqlserver":173 result = new SqlserverUser();174 break;175 case "Access":176 result = new AccessUser();177 break;178 }179 return result;180 }181 public static IDepartment CreateDepartment()182 {183 IDepartment result = null;184 switch (db)185 {186 case "Sqlserver":187 result = new SqlserverDepartment();188 break;189 case "Access":190 result = new AccessDepartment();191 break;192 }193 return result;194 }195 }196 }
题目5:从4中我们可以看出简单工厂的方法就不需要输入参数,这样在客户端就只需要db的值,客户端没有出现任何一个SQL或者Access的字样,达到了解耦的目的,但是同时的问题也出现了,如果我增加了数据库,例如Oracle的数据库访问,那么就需要在DataAccess类中每个方法的switch中加case。
分析:
在实例化的过程中我们不难发现:如果是Sqlserver就会去实例化SQL Server数据库相关类,如果是Access就去实例化Access相关类,对应的数据库类可以通过字符串得出来,所以我们可以用到“反射”这种方法:Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")
实现:
1 using System; 2 using System.Reflection;//反射 3 4 namespace 抽象工厂模式5 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 //向iu数据库中添加user这个新的用户 11 User user = new User(); 12 IUser iu = DataAccess.CreateUser(); //直接得到实际的数据库访问实例,不存在任何依赖 13 iu.Insert(user); 14 15 //在iu的数据库中查找unknownUser这个用户 16 User unknownUser = new User(); 17 unknownUser = iu.GetUser(5); 18 19 //向id数据库中添加dept这个新的用户 20 Department dept = new Department(); 21 IDepartment id = DataAccess.CreateDepartment(); 22 id.Insert(dept); 23 24 //在id的数据库中查找unknownDept这个用户 25 Department unknownDept = new Department(); 26 unknownDept = id.GetDepartment(3); 27 28 Console.Read(); 29 } 30 } 31 32 33 ///34 /// 用户类 35 /// 准备ID和Name两个字段 36 /// 37 class User 38 { 39 private int _id; 40 private string _name; 41 42 public int Id 43 { 44 get { return _id; } 45 set { _id = value; } 46 } 47 public string Name 48 { 49 get { return _name; } 50 set { _name = value; } 51 } 52 } 53 ///54 /// 增加一个Department表 55 /// 56 class Department 57 { 58 private int _id; 59 private string _deptName; 60 61 public int Id 62 { 63 get { return _id; } 64 set { _id = value; } 65 } 66 67 public string DeptName 68 { 69 get { return _deptName; } 70 set { _deptName = value; } 71 } 72 } 73 74 75 76 interface IUser 77 { 78 void Insert(User user); 79 User GetUser(int id); 80 } 81 82 ///83 /// 用于访问SQL Server的User 84 /// “增加用户”和“得到用户”两种方法 85 /// 86 class SqlserverUser : IUser 87 { 88 ///89 /// 增加用户信息 90 /// 91 /// 新的用户信息 92 public void Insert(User user) 93 { 94 Console.WriteLine("在SQL server中给User增加一条记录"); 95 } 96 ///97 /// 得到用户方法 98 /// 99 /// (传入信息)通过用户的id值100 ///(返回信息)通过id值得到对应的用户数据 101 public User GetUser(int id)102 {103 Console.WriteLine("在SQL Server中根据ID得到User表一条记录");104 return null;105 }106 }107 ///108 /// 用户访问Access的User109 /// 110 class AccessUser : IUser111 {112 public User GetUser(int id)113 {114 Console.WriteLine("在Access中根据ID得到User表一条记录");115 return null;116 }117 118 public void Insert(User user)119 {120 Console.WriteLine("在Access中给User增加一条记录");121 }122 }123 124 125 126 ///127 /// 与User表一致128 /// 129 interface IDepartment130 {131 void Insert(Department department);132 Department GetDepartment(int id);133 }134 class SqlserverDepartment : IDepartment135 {136 public Department GetDepartment(int id)137 {138 Console.WriteLine("在SQL Server中根据ID得到Department表一条记录");139 return null;140 }141 142 public void Insert(Department department)143 {144 Console.WriteLine("在SQL server中给Department增加一条记录");145 }146 }147 class AccessDepartment : IDepartment148 {149 public Department GetDepartment(int id)150 {151 Console.WriteLine("在Access中根据ID得到Department表一条记录");152 return null;153 }154 155 public void Insert(Department department)156 {157 Console.WriteLine("在Access中给Department增加一条记录");158 }159 160 }161 162 163 class DataAccess164 {165 //在此确定需要使用的数据库信息166 private static readonly string AssemblyName = "抽象工厂模式5";167 private static readonly string db = "Sqlserver";168 //private static readonly string db = "Access";169 170 171 public static IUser CreateUser()172 {173 string className = AssemblyName + "." + db + "User";174 //Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")175 return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);176 }177 public static IDepartment CreateDepartment()178 {179 string className = AssemblyName + "." + db + "Department";180 return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);181 }182 }183 }
题目6:在5的基础上,我们只有一点遗憾了:更换数据库访问时,我们还是需要去修改程序,也就是db这个字符串的值
分析:
我们可以利用配置文件来更改DataAccess的信息了,这样就不用去程序中修改了。
实现:
1 using System; 2 using System.Reflection;//反射 3 using System.Configuration; 4 5 namespace 抽象工厂模式6 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 //向iu数据库中添加user这个新的用户 12 User user = new User(); 13 IUser iu = DataAccess.CreateUser(); //直接得到实际的数据库访问实例,不存在任何依赖 14 iu.Insert(user); 15 16 //在iu的数据库中查找unknownUser这个用户 17 User unknownUser = new User(); 18 unknownUser = iu.GetUser(5); 19 20 //向id数据库中添加dept这个新的用户 21 Department dept = new Department(); 22 IDepartment id = DataAccess.CreateDepartment(); 23 id.Insert(dept); 24 25 //在id的数据库中查找unknownDept这个用户 26 Department unknownDept = new Department(); 27 unknownDept = id.GetDepartment(3); 28 29 Console.Read(); 30 } 31 } 32 33 34 ///35 /// 用户类 36 /// 准备ID和Name两个字段 37 /// 38 class User 39 { 40 private int _id; 41 private string _name; 42 43 public int Id 44 { 45 get { return _id; } 46 set { _id = value; } 47 } 48 public string Name 49 { 50 get { return _name; } 51 set { _name = value; } 52 } 53 } 54 ///55 /// 增加一个Department表 56 /// 57 class Department 58 { 59 private int _id; 60 private string _deptName; 61 62 public int Id 63 { 64 get { return _id; } 65 set { _id = value; } 66 } 67 68 public string DeptName 69 { 70 get { return _deptName; } 71 set { _deptName = value; } 72 } 73 } 74 75 76 77 interface IUser 78 { 79 void Insert(User user); 80 User GetUser(int id); 81 } 82 83 ///84 /// 用于访问SQL Server的User 85 /// “增加用户”和“得到用户”两种方法 86 /// 87 class SqlserverUser : IUser 88 { 89 ///90 /// 增加用户信息 91 /// 92 /// 新的用户信息 93 public void Insert(User user) 94 { 95 Console.WriteLine("在SQL server中给User增加一条记录"); 96 } 97 ///98 /// 得到用户方法 99 /// 100 /// (传入信息)通过用户的id值101 ///(返回信息)通过id值得到对应的用户数据 102 public User GetUser(int id)103 {104 Console.WriteLine("在SQL Server中根据ID得到User表一条记录");105 return null;106 }107 }108 ///109 /// 用户访问Access的User110 /// 111 class AccessUser : IUser112 {113 public User GetUser(int id)114 {115 Console.WriteLine("在Access中根据ID得到User表一条记录");116 return null;117 }118 119 public void Insert(User user)120 {121 Console.WriteLine("在Access中给User增加一条记录");122 }123 }124 125 126 127 ///128 /// 与User表一致129 /// 130 interface IDepartment131 {132 void Insert(Department department);133 Department GetDepartment(int id);134 }135 class SqlserverDepartment : IDepartment136 {137 public Department GetDepartment(int id)138 {139 Console.WriteLine("在SQL Server中根据ID得到Department表一条记录");140 return null;141 }142 143 public void Insert(Department department)144 {145 Console.WriteLine("在SQL server中给Department增加一条记录");146 }147 }148 class AccessDepartment : IDepartment149 {150 public Department GetDepartment(int id)151 {152 Console.WriteLine("在Access中根据ID得到Department表一条记录");153 return null;154 }155 156 public void Insert(Department department)157 {158 Console.WriteLine("在Access中给Department增加一条记录");159 }160 161 }162 163 164 class DataAccess165 {166 //在此确定需要使用的数据库信息167 private static readonly string AssemblyName = "抽象工厂模式6";168 //app.config是配置文件,是标准的XML文件169 //我们常常访问的是appSettings,它是由.NET预定义配置的170 private static readonly string db = ConfigurationSettings.AppSettings["DB"];171 172 public static IUser CreateUser()173 {174 string className = AssemblyName + "." + db + "User";175 //Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")176 return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);177 }178 public static IDepartment CreateDepartment()179 {180 string className = AssemblyName + "." + db + "Department";181 return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);182 }183 }184 185 /**186 * 用简单工厂的地方,可以考虑用反射技术来去除switch或者if,接触分支判断来带的耦合187 */188 }
总结:
抽象工厂模式(Abstract Factory),提供了一个创建一系列相关或者相互依赖对象的接口,而无需指定他们的具体的类。
抽象工厂模式让具体的创建实例过程与客户端分离。
注:文中所有代码及知识点均来自于《大话设计模式》,本人属于边学边看边敲代码边总结的阶段。