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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ABCD898989/article/details/53023998

[把你的理性思维慢慢变成条件反射]

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

操作系统: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等都提供相似的功能,拥有相似的结构。产品等级:举个例子:各个数据库的版本。

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

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

应用场景:

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

优点:

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

缺点:

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

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

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


参考资料:

图书:《大话设计模式》

其他博文:http://blog.csdn.NET/lovelion/article/details/7563445


猜你喜欢

转载自blog.csdn.net/ABCD898989/article/details/53023998