1、抽象工厂(Abstract Factory)模式,换DB的例子,从使用Acess换为使用SqlServer 从SqlServer换为Oracle由于数据库使用的命名空间不同,用到的方法不同,所以很难直接替换,往往要用时间摆平。,模拟数据库访问例子:,class Userprivate int _id;public int IDget return _id; set _id = value; private string _name;public string Nameget return _name; set _name = value; ,User表,操作User表,class Sqlser
2、verUserpublic void Insert(User user)Console.WriteLine(“在Sqlserver中给User表增加一条记录“);public User GetUser(int id)Console.WriteLine(“在Sqlserver中根据ID得到User表一条记录“);return null;,客户端代码,class Programstatic void Main(string args)User user = new User();SqlserverUser su = new SqlserverUser();su.Insert(user);su.Ge
3、tUser(1);Console.Read();,su对象被框死在Sql Server上了,再换数据库时,不够灵活。考虑多态,用工厂方法模式的数据访问程序,用户类不变class Userprivate int _id;public int IDget return _id; set _id = value; private string _name;public string Nameget return _name; set _name = value; ,IUser接口,解除与具体数据库访问的耦合,interface IUservoid Insert(User user);User Get
4、User(int id);,用于访问SqlServer的User,class SqlserverUser : IUserpublic void Insert(User user)Console.WriteLine(“在Sqlserver中给User表增加一条记录“);public User GetUser(int id)Console.WriteLine(“在Sqlserver中根据ID得到User表一条记录“);return null;,用于访问Access的User,class AccessUser : IUserpublic void Insert(User user)Console.W
5、riteLine(“在Access中给User表增加一条记录“);public User GetUser(int id)Console.WriteLine(“在Access中根据ID得到User表一条记录“);return null;,定义一个创建访问User表对象的工厂接口,interface IFactoryIUser CreateUser();,实例化SqlserverUser,class SqlServerFactory : IFactorypublic IUser CreateUser()return new SqlserverUser();,实例化AccessUser,class
6、AccessFactory : IFactorypublic IUser CreateUser()return new AccessUser();,客户端,static void Main(string args)User user = new User();IFactory factory = new AccessFactory();IUser iu = factory.CreateUser();iu.Insert(user);iu.GetUser(1);Console.Read();,若要改成SqlServer数据库,只须改此处,考虑另一个问题,数据库里除了有User表之外,可能还有其他表
7、,比方说Department等。,class Departmentprivate int _id;public int IDget return _id; set _id = value; ,private string _deptName;public string DeptNameget return _deptName; set _deptName = value; ,用于客户端访问,解除与具体数据库访问的耦合,interface IDepartmentvoid Insert(Department department);Department GetDepartment(int id);
8、,用于访问SqlServer的Department,class SqlserverDepartment : IDepartmentpublic void Insert(Department department)Console.WriteLine(“在Sqlserver中给Department表增加一条记录“);public Department GetDepartment(int id)Console.WriteLine(“在Sqlserver中根据ID得到Department表一条记录“);return null;,用于访问Access的Department,class AccessDep
9、artment : IDepartmentpublic void Insert(Department department)Console.WriteLine(“在Access中给Department表增加一条记录“);public Department GetDepartment(int id)Console.WriteLine(“在Access中根据ID得到Department表一条记录“);return null;,interface IFactoryIUser CreateUser();IDepartment CreateDepartment();,实例化SqlserverUser和S
10、qlserverDepartment,class SqlServerFactory : IFactorypublic IUser CreateUser()return new SqlserverUser();public IDepartment CreateDepartment()return new SqlserverDepartment();,实例化AccessUser和AccessDepartment,class AccessFactory : IFactorypublic IUser CreateUser()return new AccessUser();public IDepartm
11、ent CreateDepartment()return new AccessDepartment();,客户端,class Programstatic void Main(string args)User user = new User();Department dept = new Department();/ IFactory factory = new SqlServerFactory();IFactory factory = new AccessFactory();IUser iu = factory.CreateUser();iu.Insert(user);iu.GetUser(1
12、);IDepartment id = factory.CreateDepartment();id.Insert(dept);id.GetDepartment(1);Console.Read();,以上所使用的就是抽象工厂模式 抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。 定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类,为了方便引进抽象工厂模式,引进一个新概念:产品族(Product Family)。所谓产品族,是指位于不同产品等级结构,功能相关联的产品组成的家族。如图:,图中一共有四个产品族,分布于三个不同的产品等级结构中。 只要指明一个产品所处
13、的产品族以及它所属的等级结构, 就可以唯一的确定这个产品。,引进抽象工厂模式,所谓的抽象工厂是指一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。如果用图来描述的话,如下图:,Abstract Factory模式的结构,用产品族描述如下,抽象工厂(Abstract Factory)角色:担任这个角色的是工厂方法模式的核心,它是与应用系统商业逻辑无关的。 具体工厂(Concrete Factory)角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。,抽象产品(Abstract Product
14、)角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。 具体产品(Concrete Product)角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。,在什么情形下使用抽象工厂模式:,希望一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节时,即希望一个系统只是知道产品的接口,而不关心实现的时候。 一个系统有多于一个的产品族,而系统只消费其中某一产品族,AbstractFactory模式和Factory模式的区别,AbstractFactory模式是为创建一组(有多类)相关或依赖的对象
15、提供创建接口。Factory模式是为一类对象提供创建接口,从“开放封闭”原则谈优缺点,“开放封闭”原则要求系统对扩展开放,对修改封闭。通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增加包括两方面: 增加产品族:Abstract Factory很好的支持了“开放封闭“原则。 增加新产品的等级结构:需要修改所有的工厂角色,没有很好支持“开放封闭”原则。 分离接口和实现,使得客户端从具体的产品实例中解耦 综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,而不能为新的产品等级结构的增加提供这样的方便。,本质,选择产品簇的实现,用简单
16、工厂来改进抽象工厂,去除IFactory、SqlserverFactory和AccessFactory三个工厂类,取而代之的是DataAccess类,用一个简单工厂模式来实现,代码结构图,DataAccess类,class DataAccessprivate static readonly string db = “Sqlserver“;/ private static readonly string db = “Access“;public static IUser CreateUser()IUser result = null;switch (db)case “Sqlserver“:res
17、ult = 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;,客
18、户端,class Programstatic 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();,直接得到实际的数据库访问实例,而不存在任何依赖,客户端没有出现任何
19、一个SQL SERVER或Access的字样,达到了解耦的目的,问题:,switch问题 若要增加Oracle数据库访问,修改量较大 增加产品族:需要修改所有switch,没有很好支持“开放封闭“原则。 增加新产品的等级结构:较好的支持了“开放封闭“原则,用反射+抽象工厂的数据访问程序,依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是: 当某个角色需要另一个角色的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在“依赖注入”里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用
20、者 实例的工作通常由Ioc容器来完成,然后注入调用者,因此也称为依赖注入。,用反射来实现依赖注入,Using System.ReflectionAssembly.Load(“程序集名称”).CreateInstance(“命名空间.类名称”),字符串可以用变量来处理,class DataAccessprivate static readonly string AssemblyName = “抽象工厂模式“;private static readonly string db = “Sqlserver“;/private static readonly string db = “Access“;p
21、ublic 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)
22、.CreateInstance(className);,再更改数据库时,还是需要去改程序。 采用反射+配置文件实现数据访问程序,App.config,class DataAccessprivate static readonly string AssemblyName = “抽象工厂模式1“;private static readonly string db = ConfigurationManager.AppSettings“DB“;public static IUser CreateUser()string className = AssemblyName + “.“ + db + “Us
23、er“;return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);public static IDepartment CreateDepartment()string className = AssemblyName + “.“ + db + “Department“;return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);,JAVA中的反射,Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运
24、行时透过Reflection APIs取得任何一个已知名称的class的内部信息 Java反射相关的API在包java.lang.reflect中。,JDK 1.6.0的reflect包如下图:,常用类和接口,Member接口:可以获取有关类成员(域或者方法)后者构造函数的信息。 Array类:提供动态地生成和访问JAVA数组的方法 Constructor类:提供一个类的构造函数的信息以及访问类的构造函数的接口 Field类:提供一个类的域的信息以及访问类的域的接口 Method类:提供一个类的方法的信息以及访问类的方法的接口,例子,Package hello Public class Wor
25、ld/属性private String name;/无参构造public World() /有参构造 public World(String name)this.name = name; /setter;getter;public String getName()return name; ,public void setName(String name)this.name = name; /公共方法 public void printName()System.out.println(name); /公共方法2 public void printName(String name)System.o
26、ut.println(name); ,反射构造方法:,String clazzName = “hello.World”; /反射无参构造 Class clazz = Class.forName(clazzName); Object tWorld = clazz.newInstance(); /反射有参构造 Class clazzArray = new Class1;/反射参数的类型,有几个参数就定多长 Object objectArray = new Object1;/反射参数内容。 Constractor tConstract = clazz.getConstructor(clazzArra
27、y); Object tWorld1 = tConstract.newInstance(objectArry);,反射方法,Method m; /反射有参数的方法 m = tWorld.getClass().getMethod(“printName”,clazzArray); m.invoke(tWorld,objectArray);/反射无参的方法 m = tWorld.getClass().getMethod(“printName”,null); m.invoke(tWorld,null); /如果方法中有返回值 m = tWorld.getClass().getMethod(“printName”,null); String str = (String) m.invoke(tWorld,null);,