模擬訪問數據庫「新增用戶」和「獲得用戶」,用戶類假設只有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,解除分支判斷帶來的耦合。
能夠發現到目前爲止,就「工廠」而言,已經包含了三種設計模式:
對上述模式的不一樣點將在後續推出,本文篇幅已通過長,此處先不敘述。