设计模式(二)(Factory method)工厂方法设计模式
- 定义:
1.) 工厂方法模式是用来封装对象的创建,通过让子类来决定创建的对象是什么,来达到将对象创建的过程封装的目的;
2.) 定义了一个创建对象的接口,但由子类决定要实例的泪是哪一个。工厂方法让类把实例化推迟到子类。
- 适用场景:
代码中经常会出现:根据条件创建实现了同一个接口的不用类实例,而这些判定条件可能随时变动,导致我们这个不得不去改动很多代码。
备注:简单工厂在Head First中,被认为不是设计模式的一种,但人们经常使用,大家习惯性的叫,久了也就言传为“工厂方法模式”。
- 简单工厂方法
- 解决了什么问题
例如:
string dbName=string.Empty; dbName="Access"; MemberDaoBase memberDao=null; if(dbName.ToUpper()=="ACCESS"){ memberDao=new Access.MemberDao(); } else if(dbName.ToUpper()=="SQLSERVER") { memberDao=new SqlServer.MemberDao(); } memberDao.Create(new Member()); memberDao.Modify(new Member()); memberDao.Remove(0);
如果代码中不再变动还好,但这种情况很多时候是不存在的,当来了一个新的领导,领导说Sqlite的性能好且免费,那你就不得不去重新去写一个Sqlite.MemberDao,之后再在这里的判断条件中添加上
1 else if(dbName.ToUpper()=="SQLITE") 2 { 3 memberDao=new SqlServer.SqliteDao(); 4 }
当某天另外一个领导说我们不再支持SqlServer了,我们就不得不把else if(dbName.ToUpper()=="SQLSERVER") {...} 这块代码给注释了。我们就痛苦在这种“变动”中。
问题分析:
不稳定部分:
1 string dbName=string.Empty; 2 dbName="Access"; 3 MemberDaoBase memberDao=null; 4 5 if(dbName.ToUpper()=="ACCESS"){ 6 memberDao=new Access.MemberDao(); 7 } 8 else if(dbName.ToUpper()=="SQLSERVER") 9 { 10 memberDao=new SqlServer.MemberDao(); 11 }稳定部分:
1 memberDao.Create(new Member()); 2 3 memberDao.Modify(new Member()); 4 5 memberDao.Remove(0);
如果我们将不稳定的部分给交给两外一个类单独管理,就能把这种耦合变得低一点,当我们需要“变动”时,只用去修改这个管理类;而这管理来是用来new 实例的,我们习惯。
- “new管理类(简单工厂方法核心类)”:
1 /// <summary> 2 /// Description of MemberDaoFactory. 3 /// </summary> 4 public class MemberDaoFactory 5 { 6 public MemberDaoBase Create(string dbName) 7 { 8 if(dbName.ToUpper()=="ACCESS") 9 { 10 Console.WriteLine("new Access.MemberDao()"); 11 return new Access.MemberDao(); 12 } 13 else if(dbName.ToUpper()=="SQLSERVER") 14 { 15 Console.WriteLine("new SqlServer.MemberDao()"); 16 return new SqlServer.MemberDao(); 17 } 18 else 19 { 20 throw new NotSupportedException(string.Format("Not supported {0}",dbName)); 21 } 22 } 23 }
- 按照简单工厂方法的定义调整
代码结构图:
Member.cs
1 /// <summary> 2 /// Description of Member. 3 /// </summary> 4 public class Member 5 { 6 private int id; 7 private string name; 8 9 public Member() 10 { 11 } 12 13 public int Id 14 { 15 get { return id; } 16 set{id=value;} 17 } 18 19 public string Name 20 { 21 get { return name; } 22 set { name = value; } 23 } 24 }
Dao/MemberBO.cs
1 using System; 2 3 namespace FactoryMethod 4 { 5 /// <summary> 6 /// Description of MemberBO. 7 /// </summary> 8 public class MemberBO 9 { 10 private MemberDaoFactory memberDaoFactory=null; 11 private string dbName=string.Empty; 12 13 public MemberBO(string dbName,MemberDaoFactory memberDaoFactory) 14 { 15 this.dbName=dbName; 16 this.memberDaoFactory=memberDaoFactory; 17 } 18 19 public void Create(Member member) 20 { 21 if(member==null) 22 { 23 throw new ArgumentNullException("member is null"); 24 } 25 26 MemberDaoBase memberDao=this.memberDaoFactory.Create(this.dbName); 27 28 memberDao.Create(member); 29 } 30 } 31 }
Dao/MemberDaoBase.cs
1 using System; 2 3 namespace FactoryMethod 4 { 5 public abstract class MemberDaoBase 6 { 7 public abstract void Create(Member member); 8 9 public abstract void Modify(Member member); 10 11 public abstract void Remove(int id); 12 } 13 }
Dao/SqlServer/MemberDao.cs
1 using System; 2 3 namespace FactoryMethod.SqlServer 4 { 5 /// <summary> 6 /// Description of MemberDao. 7 /// </summary> 8 public class MemberDao:MemberDaoBase 9 { 10 public MemberDao() 11 { 12 } 13 14 public override void Create(Member member) 15 { 16 //... 17 Console.WriteLine("Insert member into sqlserver db"); 18 } 19 20 public override void Modify(Member member) 21 { 22 //... 23 Console.WriteLine("modify member from sqlserver db"); 24 } 25 26 public override void Remove(int id) 27 { 28 //... 29 Console.WriteLine("remove member from sqlserver db"); 30 } 31 } 32 }
Dao/Access/MemberDao.cs
1 using System; 2 3 namespace FactoryMethod.Access 4 { 5 /// <summary> 6 /// Description of MemberDao. 7 /// </summary> 8 public class MemberDao:MemberDaoBase 9 { 10 public MemberDao() 11 { 12 } 13 14 public override void Create(Member member) 15 { 16 //... 17 Console.WriteLine("Insert member into access db"); 18 } 19 20 public override void Modify(Member member) 21 { 22 //... 23 Console.WriteLine("modify member from access db"); 24 } 25 26 public override void Remove(int id) 27 { 28 //... 29 Console.WriteLine("remove member from access db"); 30 } 31 } 32 }
客户端调用:
1 class Program 2 { 3 public static void Main(string[] args) 4 { 5 Console.WriteLine("Hello World!"); 6 7 // TODO: Implement Functionality Here 8 MemberBO memberBO0=new MemberBO("access",new MemberDaoFactory()); 9 memberBO0.Create(new Member()); 10 11 MemberBO memberBO1=new MemberBO("sqlserver",new MemberDaoFactory()); 12 memberBO1.Create(new Member()); 13 14 Console.Write("Press any key to continue . . . "); 15 Console.ReadKey(true); 16 } 17 }
输出结果:
- 工厂方法
当系统要求不再是只有普通人员了,分出了管理层和基层,而且程序中要求不同等级的数据放在不同网络上,且数据库中的字段名称不同,但意义还是一样时。
我们当前系统的结构就不能很好的应对了,于是我们就需要这样处理:
提取出对应的抽象MemberBO类,和MemberBO的具体实现类ManagerBO,EmployeeBO,MemberBO包含所有的外部需要的功能外还包含一个抽象的FactoryMethod函数,ManagerBO和EmployeeBO要实现FactoryMethod函数:
对应的类:
1 public class Member 2 { 3 public Member() { } 4 5 public int ID { get; set; } 6 7 public string Name { get; set; } 8 } 9 10 public abstract class MemberDaoBase 11 { 12 public abstract void Create(Member member); 13 14 public abstract void Modify(Member member); 15 } 16 17 public abstract class MemberBOBase 18 { 19 protected virtual void Create(Member member, string dbName) 20 { 21 if (member == null) 22 { 23 throw new ArgumentNullException("member is null!"); 24 } 25 26 this.FactoryMethod(dbName).Create(member); 27 } 28 29 protected virtual void Modify(Member member, string dbName) 30 { 31 if (member == null) 32 { 33 throw new ArgumentNullException("member is null!"); 34 } 35 36 this.FactoryMethod(dbName).Modify(member); 37 } 38 39 40 public abstract MemberDaoBase FactoryMethod(string dbName); 41 } 42 43 public class ManagerBO : MemberBOBase 44 { 45 public override MemberDaoBase FactoryMethod(string dbName) 46 { 47 if (dbName.ToUpper() == "ACCESS") 48 { 49 return new ManagerAccessDao(); 50 } 51 else if (dbName.ToUpper() == "SQLSERVER") 52 { 53 return new ManagerSqlServerDao(); 54 } 55 else 56 { 57 throw new NotImplementedException(string.Format("{0} not implemented", dbName)); 58 } 59 } 60 } 61 62 public class EmployeeBO : MemberBOBase 63 { 64 public override MemberDaoBase FactoryMethod(string dbName) 65 { 66 if (dbName.ToUpper() == "ACCESS") 67 { 68 return new EmployeeAccessDao(); 69 } 70 else if (dbName.ToUpper() == "SQLSERVER") 71 { 72 return new EmployeeSqlServerDao(); 73 } 74 else 75 { 76 throw new NotImplementedException(string.Format("{0} not implemented", dbName)); 77 } 78 } 79 } 80 81 public class ManagerAccessDao : MemberDaoBase 82 { 83 public override void Create(Member member) 84 { 85 throw new NotImplementedException(); 86 } 87 88 public override void Modify(Member member) 89 { 90 throw new NotImplementedException(); 91 } 92 } 93 94 95 public class ManagerSqlServerDao : MemberDaoBase 96 { 97 public override void Create(Member member) 98 { 99 throw new NotImplementedException(); 100 } 101 102 public override void Modify(Member member) 103 { 104 throw new NotImplementedException(); 105 } 106 } 107 108 public class EmployeeAccessDao : MemberDaoBase 109 { 110 public override void Create(Member member) 111 { 112 throw new NotImplementedException(); 113 } 114 115 public override void Modify(Member member) 116 { 117 throw new NotImplementedException(); 118 } 119 } 120 121 122 public class EmployeeSqlServerDao : MemberDaoBase 123 { 124 public override void Create(Member member) 125 { 126 throw new NotImplementedException(); 127 } 128 129 public override void Modify(Member member) 130 { 131 throw new NotImplementedException(); 132 } 133 }
- 简单工厂和工厂方法之间的差异
简单工厂是将全部的事情,在一个地方都处理完了,而工厂方法却是创建了一个框架,让子类决定要如何实现。
例如:在工厂方法中我们有一个FactoryMethod()方法提供了一般的框架,一边创建Dao,FactoryMethod()方法依赖工厂方法创建具体类,决定制造出的Dao是什么。
简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。
- 这样的调整好处在于什么?
当我们需要条件一个新扩展Member数据操作类时,我们有统一的接口来,具体的操作方法都可不变,改动部分只需要扩展现有的MemberDaoFactory.cs,降低了应用层的耦合度。
- 优缺点:
1.) 优点:
a.) 降低了应用层次调用的耦合度;b.) 当扩展时,有统一的接口类,开发更规范;
c.) 代码逻辑更清晰,代码更易管理维护。
2.) 缺点:
a.) 适用性很有限,前提条件有局限性;当我们的接口类MemberDaoBase.cs变动时,我们就需要改动多处代码;
b.) 当需要扩展时,我们依然避免不了要改动代码(尽管可以进一步改进通过设置配置和反射达到避免MemberDaoFactory变动,但是当我们需要添加新的扩展时还避免不了改动代码)。
参考资料:《Head First 设计模式》
欢迎拍砖!请牛人们给指点。
基础才是编程人员应该深入研究的问题,比如:
1)List/Set/Map内部组成原理|区别
2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
3)JVM运行组成与原理及调优
4)Java类加载器运行原理
5)Java中GC过程原理|使用的回收算法原理
6)Redis中hash一致性实现及与hash其他区别
7)Java多线程、线程池开发、管理Lock与Synchroined区别
8)Spring IOC/AOP 原理;加载过程的。。。
【+加关注】。