第四篇、抽象工厂模式的优化
2017-02-17 15:25
253 查看
在上一篇的博文中,我们研究了抽象工厂,我们简单的回顾一下:
首先,抽象工厂是为了应对产品簇的概念而生的。
其次,它使我们在不影响现有产品等级结构的基础上,方便的在不同产品系列当中进行切换。
最后,它封装了产品对象的创建过程,使之与客户端进行分离,无需客户端考虑他的组装过程。
但是,如果需求来自功能的增加,我们不仅要增加新的功能父类与产品系列子类,还需修改工厂接口以及所有子类工厂。
——————————————————————————————————————————————————————————————————
那我们有什么方式可以对抽象工厂进行优化呢,大家随我一起来看一下。
用简单工厂+抽象工厂的方式进行实现:
首先,对于产品的子类,我们不做修改
先是IUser以及其子类
public abstract class IUser {
public abstract void insert(User user);
public abstract User getUser(String id);
}
public abstract class IDepartment {
public abstract void insert(Department deprecated);
public abstract Department getDepartment(String id);
}
我们要修改的是工厂类,去掉IFactory以及他的子类SqlServerFactory和AccessFactory。用一个简单工厂类DataFactory代替。在写DataFactory的代码之前,我们先来简单了解一下反射,根据我们代码的需要,这里只简单讲解一下通过反射来实例化对象。
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。我们知道,类的实例化过程,就是通过new的方式调用它的构造函数,从而获得这个类的对象,那么这个构造函数就是这个类的一个特殊方法,我们就可以通过反射来调用,从而获取对象:
1、假如我们有一个学生类
public class Student {
String name = "新生";
public Student() {
super();
}
public Student(String name) {
super();
this.name = name;
}
}
2、然后我们通过反射来实例化这个学生类,从而获得学生对象
Student stu = null;
try {
stu = (Student) Class.forName("my.demo.milltly.jd_cx.Student").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(stu.name);
输出结果为:新生。我们通过Class.forName()来获得这个Student类,其中作为参数的字符串my.demo.milltly.jd_cx.Student是这个类的全路径名,newInstance就是调用这个类的无参构造函数。
那么我们要调用它的有参构造函数,使之实例化对象的同时,赋予它名字,要怎么做呢。
Student stu = null;
Class[] cla = { String.class };
Object[] ob = { "小明" };
try {
stu = (Student) Class.forName("my.demo.milltly.jd_cx.Student").getDeclaredConstructor(cla).newInstance(ob);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(stu.name);
首先,我们要声明两个数组,Class数组是用来放置传入参数的类型,Object数组是对应参数的实际值。在调用的时候,我们需要先调用getDeclaredConstructor,将Class数组传入,以获取类中对应参数的构造器,再通过newInstance将需要赋值的参数数组传给构造器,从而获取对象。
好了,回来看一下DataFactory要怎么写吧
public class DataFactory {
private static String className_User = "my.demo.milltly.jd_cx.SqlServerUser";
private static String className_Department = "my.demo.milltly.jd_cx.SqlServerDepartment";
// private static String className_User = "my.demo.milltly.jd_cx.AccessUser";
// private static String className_Department = "my.demo.milltly.jd_cx.AccessDepartment";
public static IUser createUser() {
IUser u = null;
try {
u = (IUser) Class.forName(className_User).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return u;
}
public static IDepartment createDepartment() {
IDepartment d = null;
try {
d = (IDepartment) Class.forName(className_Department).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return d;
}
}
好了,这样我们就可以获得所需要的对象了,我们在客户端调用一下:
4000
IUser u = DataFactory.createUser();
u.insert(new User());
User user = u.getUser("1");
IDepartment d = DataFactory.createDepartment();
d.insert(new Department());
Department department = d.getDepartment("1");
对于一个应用,我们要用的数据库,一般都是一个,应该不会出现一个程序即用Sql Server又用Access的,所以,我将所需要用的数据库标识写到了DataFactory中,免去了客户端调用Create方法的时候还要传递参数的过程,这样,需要切换数据库的时候,我只需要将上面声明的标识字符串替换就好了,无需去改客户端代码。
通过反射技术,如果需要增加新的数据库Oracle的时候,我只需要增加新的功能子类,然后将字符串替换成Oracle的就好了,避免了之前第一篇中简单工厂类修改switch...case的尴尬。
如果需求来自功能的增加,比如添加Project表,我们也无需像之前抽象工厂那样,修改IFactory及其子类。而只需在DataFactory中增加新的create方法,无需对已有方法进行修改,即可完成功能增加。是不是比之前设计方便了很多,这也是反射的魅力所在。
有些朋友会说,切换数据库的时候,我还是要修改代码,修改就要重新编译,能不能不修改代码呢。可以,因为我们是通过字符串变量来实例化对象,变量意味着,我可以在运行时来确定它的值,而无需在编译期就要指定它的值,这就给了我们灵活应用的空间。
用配置文件来优化
首先,书写配置文件a.properties
userclass = my.demo.milltly.jd_cx.SqlServerUser
departmentclass = my.demo.milltly.jd_cx.SqlServerDepartment
然后通过读取配置文件信息,来实例化对象
public class DataFactory {
private static String getUserClassName() {
Properties prop = new Properties();
try {
prop.load(DataFactory.class.getClassLoader().getResourceAsStream("a.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String userClass = prop.getProperty("userclass");
return userClass;
}
private static String getDepartmentClassName() {
Properties prop = new Properties();
try {
prop.load(DataFactory.class.getClassLoader().getResourceAsStream("a.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String departmentClass = prop.getProperty("departmentclass");
return departmentClass;
}
public static IUser createUser() {
IUser u = null;
try {
u = (IUser) Class.forName(getUserClassName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return u;
}
public static IDepartment createDepartment() {
IDepartment d = null;
try {
d = (IDepartment) Class.forName(getDepartmentClassName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return d;
}
}这样,当发生数据库切换的时候,我只需修改配置文件为所需数据库功能类,就可完成切换,无需修改代码,是不是更方便了。好了,这次就先说到这里吧。
首先,抽象工厂是为了应对产品簇的概念而生的。
其次,它使我们在不影响现有产品等级结构的基础上,方便的在不同产品系列当中进行切换。
最后,它封装了产品对象的创建过程,使之与客户端进行分离,无需客户端考虑他的组装过程。
但是,如果需求来自功能的增加,我们不仅要增加新的功能父类与产品系列子类,还需修改工厂接口以及所有子类工厂。
——————————————————————————————————————————————————————————————————
那我们有什么方式可以对抽象工厂进行优化呢,大家随我一起来看一下。
用简单工厂+抽象工厂的方式进行实现:
首先,对于产品的子类,我们不做修改
先是IUser以及其子类
public abstract class IUser {
public abstract void insert(User user);
public abstract User getUser(String id);
}
public class SqlServerUser extends IUser { @Override public void insert(User user) { // ...... System.out.println("在SqlServer中插入User成功"); } @Override public User getUser(String id) { User u = null; // ...... System.out.println("在SqlServer中查询到了User"); return u; } }
public class AccessUser extends IUser { @Override public void insert(User user) { // ...... System.out.println("在Access中插入User成功"); } @Override public User getUser(String id) { User u = null; // ...... System.out.println("在Access中查询到了User"); return u; } }然后是IDepartment以及其子类
public abstract class IDepartment {
public abstract void insert(Department deprecated);
public abstract Department getDepartment(String id);
}
public class SqlServerDepartment extends IDepartment { @Override public void insert(Department department) { // ...... System.out.println("在SqlServer中插入Department成功"); } @Override public Department getDepartment(String id) { Department d = null; // ...... System.out.println("在SqlServer中查询到了Department"); return d; } }
public class AccessDepartment extends IDepartment { @Override public void insert(Department deprecated) { // ...... System.out.println("在Access中插入Department成功"); } @Override public Department getDepartment(String id) { Department d = null; // ...... System.out.println("在Access中查询到了Department"); return d; } }
我们要修改的是工厂类,去掉IFactory以及他的子类SqlServerFactory和AccessFactory。用一个简单工厂类DataFactory代替。在写DataFactory的代码之前,我们先来简单了解一下反射,根据我们代码的需要,这里只简单讲解一下通过反射来实例化对象。
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。我们知道,类的实例化过程,就是通过new的方式调用它的构造函数,从而获得这个类的对象,那么这个构造函数就是这个类的一个特殊方法,我们就可以通过反射来调用,从而获取对象:
1、假如我们有一个学生类
public class Student {
String name = "新生";
public Student() {
super();
}
public Student(String name) {
super();
this.name = name;
}
}
2、然后我们通过反射来实例化这个学生类,从而获得学生对象
Student stu = null;
try {
stu = (Student) Class.forName("my.demo.milltly.jd_cx.Student").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(stu.name);
输出结果为:新生。我们通过Class.forName()来获得这个Student类,其中作为参数的字符串my.demo.milltly.jd_cx.Student是这个类的全路径名,newInstance就是调用这个类的无参构造函数。
那么我们要调用它的有参构造函数,使之实例化对象的同时,赋予它名字,要怎么做呢。
Student stu = null;
Class[] cla = { String.class };
Object[] ob = { "小明" };
try {
stu = (Student) Class.forName("my.demo.milltly.jd_cx.Student").getDeclaredConstructor(cla).newInstance(ob);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(stu.name);
首先,我们要声明两个数组,Class数组是用来放置传入参数的类型,Object数组是对应参数的实际值。在调用的时候,我们需要先调用getDeclaredConstructor,将Class数组传入,以获取类中对应参数的构造器,再通过newInstance将需要赋值的参数数组传给构造器,从而获取对象。
好了,回来看一下DataFactory要怎么写吧
public class DataFactory {
private static String className_User = "my.demo.milltly.jd_cx.SqlServerUser";
private static String className_Department = "my.demo.milltly.jd_cx.SqlServerDepartment";
// private static String className_User = "my.demo.milltly.jd_cx.AccessUser";
// private static String className_Department = "my.demo.milltly.jd_cx.AccessDepartment";
public static IUser createUser() {
IUser u = null;
try {
u = (IUser) Class.forName(className_User).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return u;
}
public static IDepartment createDepartment() {
IDepartment d = null;
try {
d = (IDepartment) Class.forName(className_Department).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return d;
}
}
好了,这样我们就可以获得所需要的对象了,我们在客户端调用一下:
4000
IUser u = DataFactory.createUser();
u.insert(new User());
User user = u.getUser("1");
IDepartment d = DataFactory.createDepartment();
d.insert(new Department());
Department department = d.getDepartment("1");
对于一个应用,我们要用的数据库,一般都是一个,应该不会出现一个程序即用Sql Server又用Access的,所以,我将所需要用的数据库标识写到了DataFactory中,免去了客户端调用Create方法的时候还要传递参数的过程,这样,需要切换数据库的时候,我只需要将上面声明的标识字符串替换就好了,无需去改客户端代码。
通过反射技术,如果需要增加新的数据库Oracle的时候,我只需要增加新的功能子类,然后将字符串替换成Oracle的就好了,避免了之前第一篇中简单工厂类修改switch...case的尴尬。
如果需求来自功能的增加,比如添加Project表,我们也无需像之前抽象工厂那样,修改IFactory及其子类。而只需在DataFactory中增加新的create方法,无需对已有方法进行修改,即可完成功能增加。是不是比之前设计方便了很多,这也是反射的魅力所在。
有些朋友会说,切换数据库的时候,我还是要修改代码,修改就要重新编译,能不能不修改代码呢。可以,因为我们是通过字符串变量来实例化对象,变量意味着,我可以在运行时来确定它的值,而无需在编译期就要指定它的值,这就给了我们灵活应用的空间。
用配置文件来优化
首先,书写配置文件a.properties
userclass = my.demo.milltly.jd_cx.SqlServerUser
departmentclass = my.demo.milltly.jd_cx.SqlServerDepartment
然后通过读取配置文件信息,来实例化对象
public class DataFactory {
private static String getUserClassName() {
Properties prop = new Properties();
try {
prop.load(DataFactory.class.getClassLoader().getResourceAsStream("a.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String userClass = prop.getProperty("userclass");
return userClass;
}
private static String getDepartmentClassName() {
Properties prop = new Properties();
try {
prop.load(DataFactory.class.getClassLoader().getResourceAsStream("a.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String departmentClass = prop.getProperty("departmentclass");
return departmentClass;
}
public static IUser createUser() {
IUser u = null;
try {
u = (IUser) Class.forName(getUserClassName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return u;
}
public static IDepartment createDepartment() {
IDepartment d = null;
try {
d = (IDepartment) Class.forName(getDepartmentClassName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return d;
}
}这样,当发生数据库切换的时候,我只需修改配置文件为所需数据库功能类,就可完成切换,无需修改代码,是不是更方便了。好了,这次就先说到这里吧。
相关文章推荐
- 抽象工厂模式优化
- 如何做到优化引擎搜索SEO之第四篇:谷歌分析(Google Analytics)
- 全功能智能车之最后优化DMA摄像头,无FIFO摄像头的终结版程序(第四篇)
- 性能优化系列第四篇--移动端网络优化
- Entity Framework 第四篇 优化SQL查询
- android面向接口编程(抽象工厂模式,扩展性超强,Demo优化)
- MySQL 数据库性能优化之SQL优化(这是 MySQL数据库性能优化专题 系列的第四篇文章)
- 设计模式实践(抽象工厂模式应用)—通信录的优化过程
- java(优化10) 15天玩转redis之第四篇
- 性能优化第四篇--移动网络优化
- MySQL 数据库性能优化第四篇文章之SQL优化
- Apache 2.0性能优化-MPM的选择与配置--From uudangdang.com
- Tomcat优化调整
- SQL优化(1)
- SEO 优化方案
- mysql limit查询优化
- SQL查询安全性及性能优化
- MySQL查询优化技术系列讲座之使用索引
- 优化 PHP 代码技巧
- myeclipse优化 Maven