您的位置:首页 > 其它

被说了很多遍的设计模式---抽象工厂模式

2016-11-03 21:16 435 查看
[把你的理性思维慢慢变成条件反射]

本文,我们讲介绍抽象工厂模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:

操作系统:win7
x64

其他软件:eclipse mars,jdk7

-------------------------------------------------------------------------------------------------------------------------------------


经典问题:

在产品簇的范围内选择合适对象的问题,如数据库驱动。(产品簇:相同功能的不同实例)


思路分析:

要点一:所有产品具有相似的功能或属性。

要点二:所有产品在实现上存在差异。


示例工程:




错误写法:

创建SqlDB.java文件,具体内容如下:(在此为简化实现过程,实际代码仅作为示例,简单的SQL连接代码,见注释部分)

package com.csdn.ingo.gof_AbstractFactory;

public class SqlDB {
public void insert(User user){
System.out.println("insert:"+user.getName());
}
public User select(String string){
System.out.println("select:"+string);
return null;
}
}

//public class DBConnection {
//    public static final String url = "jdbc:mysql://127.0.0.1/student";
//    public static final String name = "com.mysql.jdbc.Driver";
//    public static final String user = "root";
//    public static final String password = "root";
//
//    public Connection conn = null;
//    public PreparedStatement pst = null;
//
//    public DBConnection(String sql) {
//        try {
//            Class.forName(name);//指定连接类型
//            conn = DriverManager.getConnection(url, user, password);//获取连接
//            pst = conn.prepareStatement(sql);//准备执行语句
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
//
//    public void close() {
//        try {
//            this.conn.close();
//            this.pst.close();
//        } catch (SQLException e) {
//            e.printStackTrace();
//        }
//    }
//}
创建User.java文件,具体内容如下:
package com.csdn.ingo.gof_AbstractFactory;

public class User {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory;

public class Window {
public static void main(String[] args) {
User u = new User();
SqlDB s = new SqlDB();
u.setId("aaaa");
u.setName("bbbb");
s.insert(u);
s.select("aaaa");
}
}

错误原因:

数据库的选择直接写入代码,并且与Sql语句的耦合度非常高。一旦后期进行适配别的数据库,就会违反“开闭原则”,从而需要大量的工作才能修改到对应的数据库,并且这种修改的影响范围的深度与广度尚未可知。因此,这种代码书写方式是一种非常不好的书写方式。


工厂方法模式的实现:

在前文中,我们介绍了工厂方法模式,在此,我们先来看看使用工厂方法模式的实现过程。



创建IFactory.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.one;

public interface IFactory {
public IUser createUser();
}
创建AccessFactory.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.one;

public class AccessFactory implements IFactory {

public IUser createUser() {
return new AccessUser();
}
}
创建SqlserverFactory.java文件,具体内容如下:
package com.csdn.ingo.gof_AbstractFactory.one;

public class SqlserverFactory implements IFactory{

public IUser createUser() {
return new SqlserverUser();
}
}
创建IUser.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.one;

public interface IUser {
void insert(User u);
User select(String id);
}
创建AccessUser.java,SqlserverUser.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.one;

public class AccessUser implements IUser{
public void insert(User user){
System.out.println("insert:"+user.getName());
}
public User select(String string){
System.out.println("select:"+string);
return null;
}
}
创建User.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.one;

public class User {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.one;

public class Window {
public static void main(String[] args) {
User u = new User();
u.setName("aaaa");
IFactory f = new SqlserverFactory();
IUser iu = f.createUser();
iu.insert(u);
iu.select("aaa");
}
}
工厂方法实现的优点与不足:

优点:切换数据时,仅需修改IFactory f = new SqlserverFactory();及对应的SQL语句。

缺点:任何新增和修改,都可能会导致需要针对不同的数据库进行对应的实现。

推荐写法:(抽象工厂模式)



详细代码与上文类似,工程结构图见下文模式总结部分。


在抽象工厂实现的基础上,结合简单工厂模式在进行略微修改:



创建DateAccess.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.three;

public class DataAccess {
//	private static final String db = "Sqlserver";//可由配置文件实现
private static final String db = "Access";

public static IUser createUser() {
IUser re = null;
switch (db) {
case "Sqlserver":
re = new SqlserverUser();
break;
case "Access":
re = new AccessUser();
break;

default:
break;
}
return re;
}
public static IDepartment createDepartment() {
IDepartment re = null;
switch (db) {
case "Sqlserver":
re = new SqlserverDepartment();
break;
case "Access":
re = new AccessDepartment();
break;
default:
break;
}
return re;
}
}
创建IUser.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.three;

public interface IUser {
void insert(User u);
User select(String id);
}
创建IDepartment.java文件,具体内容如下;

package com.csdn.ingo.gof_AbstractFactory.three;

public interface IDepartment {
void insert(Department d);
Department select(String id);
}
创建SqlserverUser.java,AccessUser.java,SqlserverDepartment.java,AccessDepartment.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.three;

public class SqlserverUser implements IUser {

public void insert(User user){
System.out.println("SqlserverUser insert:"+user.getName());
}
public User select(String string){
System.out.println("SqlserverUser select:"+string);
return null;
}
}
创建User.java,Department.java文件,见上文。

创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.three;

import org.omg.PortableServer.IdAssignmentPolicy;

public class Window {
public static void main(String[] args) {
User u = new User();
u.setName("aaaa");
IUser iu = DataAccess.createUser();
iu.insert(u);
iu.select("aaa");

Department de = new Department();
de.setName("asdf");
IDepartment d = DataAccess.createDepartment();
d.insert(de);
d.select("asdf");
}
}

推荐原因:

具体的Factory实现类,直到运行时才确定,并且,这个具体的Factory负责创建具体的对象。客户端如需要不同的对象,就需要调用不同的具体工厂。由此,在切换时,可以跟方便的切换调用方式即可。因为,客户端是面向抽象的,具体的实现已经分离到具体的子类当中。中间通过抽象接口作为中介,对于客户端完全屏蔽。非常符合“开闭原则”“迪米特法则”等。最后,上面的功能其实已经足够说明抽象工厂模式,简单工厂模式,工厂方法模式的区别了。但在此,为了完整的展示三种工厂模式的强大功能,我们再将配置文件与反射的结合的代码给出,供各位看官学习。



创建Init.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.four;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Init {
public static Properties getPro() throws FileNotFoundException, IOException {
InputStream inputStream =Object.class.getResourceAsStream("/classpro.properties");
Properties pro = new Properties();
pro.load(inputStream);
return pro;
}
}
在resources文件夹下,创建classpro.properties文件,具体内容如下:
User=com.csdn.ingo.gof_AbstractFactory.four.AccessUser
Department=com.csdn.ingo.gof_AbstractFactory.four.SqlserverDepartment
创建UserFactory.java,DepartmentFactory.java文件,具体内容如下:

package com.csdn.ingo.gof_AbstractFactory.four;

public class UserFactory {
public static IUser getInstance(String className) {
IUser u = null;
try {
u = (IUser) Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return u;
}
}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_AbstractFactory.four;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import org.omg.PortableServer.IdAssignmentPolicy;

public class Window {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties pro = Init.getPro();
User u = new User();
u.setName("aaaa");
IUser iu = UserFactory.getInstance(pro.getProperty("User"));
iu.insert(u);
iu.select("aaa");

Department de = new Department();
de.setName("asdf");
IDepartment d = DepartmentFactory.getInstance(pro.getProperty("Department"));
d.insert(de);
d.select("asdf");
}
}
其他文件请参考上文工程。现在,对于任意功能的数据访问层切换都可以完全脱离修改源代码。极大的方便了后期的维护与扩展。

模式总结:

本例UML结构图:



标准抽象工厂模式UML结构图:



概念总结:

抽象工厂模式:提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。

组成部分:抽象工厂类,具体工厂类,抽象产品类,具体产品类四部分组成。


反思:

首先,我们介绍下两个概念:产品簇:具体例子:MySQL,SQLserver,Oracle等都提供相似的功能,拥有相似的结构。产品等级:举个例子:各个数据库的版本。

由上面的示例可以总结出,在抽象工厂模式中,扩展一个产品簇是相对容易的。但是,如果要扩展一个产品等级,意味着需要对所有的具体类都进行相关功能的实现。

因此,在应用该模式时,需要实现设计好各种产品等级,随着时间推进,如果需要新增一个产品等级,工作量非常的巨大。希望各位看官牢记。


应用场景:

需要将对象的应用与创建及细节进行屏蔽时。
需要使用一个产品簇,但是每次只会使用一个具体的产品时。
在这个产品簇内,拥有相似的功能和特征。
产品簇结构稳定,不会轻易修改。


优点:

客户端不关心具体的产品的创建及对象细节,其面向接口编程。
保证产品簇中只能示例话出一个实例。
一定程度上符合“开闭原则”


缺点:

不适合产品等级随时变化的场景。
如果发生切换,不仅工厂需要修改,客户端也需要切换到对应的声明上。

-------------------------------------------------------------------------------------------------------------------------------------

至此,被说了很多遍的设计模式---抽象工厂模式 结束

参考资料:

图书:《大话设计模式》

其他博文:http://blog.csdn.NET/lovelion/article/details/7563445
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: