模拟访问数据库“新增用户”和“获得用户”,用户类假设只有ID和Name两个字段。sql
/** * 用户类 * Created by callmeDevil on 2019/7/28. */ public class User { private int id; private String name; // 省略 get set 方法 }
/** * 假设sqlServer 链接,用于操做User表 * Created by callmeDevil on 2019/7/28. */ public class SqlServerUser { public void insert(User user){ System.out.println("在SQL Server中给User表增长一条记录"); } public User getUser(int id){ System.out.println("在SQL Server中根据ID获得User表一条记录"); return null; } }
public class Test { public static void main(String[] args) { User user = new User(); SqlServerUser su = new SqlServerUser(); su.insert(user); su.getUser(user.getId()); } }
在SQL Server中给User表增长一条记录 在SQL Server中根据ID获得User表一条记录
若是须要链接别的数据库,那么这个写法没法扩展,下面使用工厂方法模式实现数据库
/** * 用于客户端访问,解除与具体数据库访问的耦合 * Created by callmeDevil on 2019/7/28. */ public interface IUser { void insert(User user); User getUser(int id); }
/** * 用于访问SQL Server 的User * Created by callmeDevil on 2019/7/28. */ public class SqlServerUser implements IUser { @Override public void insert(User user) { System.out.println("在SQL Server中给User表增长一条记录"); } @Override public User getUser(int id) { System.out.println("在SQL Server中根据ID获得User表一条记录"); return null; } }
/** * 用于访问Access 的User * Created by callmeDevil on 2019/7/28. */ public class AccessUser implements IUser { @Override public void insert(User user) { System.out.println("在Access 中给User表增长一条记录"); } @Override public User getUser(int id) { System.out.println("在在Access中根据ID获得User表一条记录"); return null; } }
/** * 定义一个建立访问User 表对象的抽象工厂接口 * Created by callmeDevil on 2019/7/28. */ public interface IFactory { IUser createUser(); }
/** * 实现IFactory 接口,实例化SQLServerUser * Created by callmeDevil on 2019/7/28. */ public class SqlServerFactory implements IFactory { @Override public IUser createUser() { return new SqlServerUser(); } }
/** * 实现IFactory 接口,实例化AccessUser * Created by callmeDevil on 2019/7/28. */ public class AccessFactory implements IFactory { @Override public IUser createUser() { return new AccessUser(); } }
public class Test { public static void main(String[] args) { User user = new User(); // 若要更改为 Access 数据库,只须要将此处改为 // IFactory factory = new AccessFactory(); IFactory factory = new SqlServerFactory(); IUser iUser = factory.createUser(); iUser.insert(user); iUser.getUser(1); } }
测试结果同上。编程
若是要增长一个部门表(Department),须要怎么改?设计模式
/** * 部门表 * Created by callmeDevil on 2019/7/28. */ public class Department { private int id; private String name; // 省略 get set 方法 }
/** * 用于客户端访问,解除与具体数据库访问的耦合 * Created by callmeDevil on 2019/7/28. */ public interface IDepartment { void insert(Department department); Department getDepartment(int id); }
/** * 用于访问SqlServer 的Department * Created by callmeDevil on 2019/7/28. */ public class SqlServerDepartment implements IDepartment { @Override public void insert(Department department) { System.out.println("在 SqlServer 中给Department 表增长一条记录"); } @Override public Department getDepartment(int id) { System.out.println("在SQL Server中根据ID获得Department表一条记录"); return null; } }
/** * 用于访问Access 的Department * Created by callmeDevil on 2019/7/28. */ public class AccessDepartment implements IDepartment { @Override public void insert(Department department) { System.out.println("在Access 中给Department 表增长一条记录"); } @Override public Department getDepartment(int id) { System.out.println("在Access 中根据ID获得Department表一条记录"); return null; } }
/** * 定义一个建立访问User 表对象的抽象工厂接口 * Created by callmeDevil on 2019/7/28. */ public interface IFactory { IUser createUser(); IDepartment createDepartment(); //增长的接口方法 }
/** * 实现IFactory 接口,实例化SQLServerUser * Created by callmeDevil on 2019/7/28. */ public class SqlServerFactory implements IFactory { @Override public IUser createUser() { return new SqlServerUser(); } @Override public IDepartment createDepartment() { return new SqlServerDepartment(); //增长了SqlServerDepartment 工厂 } }
/** * 实现IFactory 接口,实例化AccessUser * Created by callmeDevil on 2019/7/28. */ public class AccessFactory implements IFactory { @Override public IUser createUser() { return new AccessUser(); } @Override public IDepartment createDepartment() { return new AccessDepartment(); //增长了AccessDepartment 工厂 } }
public class Test { public static void main(String[] args) { User user = new User(); Department dept = new Department(); // 只需肯定实例化哪个数据库访问对象给 factory IFactory factory = new AccessFactory(); // 则此时已于具体的数据库访问解除了依赖 IUser iUser = factory.createUser(); iUser.insert(user); iUser.getUser(1); IDepartment iDept = factory.createDepartment(); iDept.insert(dept); iDept.getDepartment(1); } }
在Access 中给User表增长一条记录 在Access 中根据ID获得User表一条记录 在Access 中给Department 表增长一条记录 在Access 中根据ID获得Department表一条记录
提供一个建立一系列相关或相互依赖对象的接口,而无需指定它们具体的类。ide
实际上上面的修改实现已经知足抽象工厂模式的实现方式,此处再也不举例。sqlserver
若是还要添加对项目表(Project)的访问,那么须要增长三个类,IProject、SQLServerProject、AccessProject,还须要更改 IFactory、ISQLServerFactory、AccessFactory 才能够彻底实现,这太糟糕了。编程是门艺术,这样大批量的改动,显然是很是丑陋的作法。测试
去除IFactory、SQLServerFactory、AccessFactory,改成一个 DataAccess,用一个简单工厂模式来实现。设计
/** * 统一管理数据库访问 * Created by callmeDevil on 2019/7/28. */ public class DataAccess { // 数据库名称,可替换成 Access private static final String DB = "SqlServer"; // private static final String DB = "Access"; public static IUser createUser() { IUser user = null; switch (DB) { case "SqlServer": user = new SqlServerUser(); break; case "Access": user = new AccessUser(); break; default: break; } return user; } public static IDepartment createDepartment() { IDepartment department = null; switch (DB) { case "SqlServer": department = new SqlServerDepartment(); break; case "Access": department = new AccessDepartment(); break; default: break; } return department; } }
public class Test { public static void main(String[] args) { User user = new User(); Department dept = new Department(); // 直接获得实际的数据库访问实例,而不存在任何的依赖 IUser iUser = DataAccess.createUser(); iUser.insert(user); iUser.getUser(1); IDepartment iDept = DataAccess.createDepartment(); iDept.insert(dept); iDept.getDepartment(1); } }
在SQL Server中给User表增长一条记录 在SQL Server中根据ID获得User表一条记录 在SQL Server中给Department 表增长一条记录 在SQL Server中根据ID获得Department表一条记录
虽然解决了抽象工厂模式中须要修改太多地方的问题,但又回到了简单工厂模式一开始的问题了,就是若是要链接 Oracle 数据库,那么须要修改的地方则是 DataAccess 类中全部方法的 swicth 中加 case 分支了。code
# 数据库名称,可更改为 Access db=SqlServer
/** * 统一管理数据库访问 * Created by callmeDevil on 2019/7/28. */ public class DataAccess { // 数据库名称,从配置文件中获取 private static String DB; public static IUser createUser() throws Exception { if (DB == null || DB.trim() == "") { return null; } // 拼接具体数据库访问类的权限定名 String className = "com.xxx." + DB + "User"; return (IUser) Class.forName(className).newInstance(); } public static IDepartment createDeptment() throws Exception { if (DB == null || DB.trim() == "") { return null; } // 拼接具体数据库访问类的权限定名 String className = "com.xxx." + DB + "Department"; return (IDepartment) Class.forName(className).newInstance(); } public static String getDB() { return DB; } public static void setDB(String DB) { DataAccess.DB = DB; } }
public class Test { public static void main(String[] args) throws Exception { // 加载配置文件 Properties properties = new Properties(); InputStream is = new FileInputStream(new File("xxx\\db.properties")); // 配置文件所在路径,当前方式采用绝对路径获取 properties.load(is); is.close(); String db = properties.getProperty("db"); // 使用具体的数据库告诉管理类 DataAccess dataAccess = new DataAccess(); dataAccess.setDB(db); User user = new User(); IUser iUser = dataAccess.createUser(); iUser.insert(user); iUser.getUser(1); Department dept = new Department(); IDepartment iDept = dataAccess.createDeptment(); iDept.insert(dept); iDept.getDepartment(1); } }
在SQL Server中给User表增长一条记录 在SQL Server中根据ID获得User表一条记录 在SQL Server中给Department 表增长一条记录 在SQL Server中根据ID获得Department表一条记录
如今若是咱们增长了 Oracle 数据库访问,相关类的增长是不可避免的,这点不管用任何办法都解决不了,不过这叫扩展,开放-封闭原则告诉咱们,对于扩展,咱们开放,但对于修改,咱们应该尽可能关闭,就目前实现方式而言,只须要将配置文件中改成 Oracle (若是新增的具体访问类名称为 OracleUser 和 OracleDepartment 的话)便可达到目的,客户端也不须要任何修改。server
全部在用简单工厂的地方,均可以考虑用反射技术来去除 switch 或 if,解除分支判断带来的耦合。
能够发现到目前为止,就“工厂”而言,已经包含了三种设计模式:
对上述模式的不一样点将在后续推出,本文篇幅已通过长,此处先不叙述。