书中引例:项目用的数据库为 SQL Server 。后又需求换成 Access 。数据库都有读写查等共同的操作,
using UnityEngine;
using System.Collections;
public class AbstractFactoryStudy : MonoBehaviour {
// Use this for initialization
void Start () {
User user = new User();
// IFactory07 factory07 = new SqlSercerFactory(); 若要换数据库 这两句换一下就行了
IFactory07 factory07 = new AccessFactory();
IUser iu = factory07.CreatUser();
iu.Insert(user);
iu.GetUser(1);
}
}
//IUser 接口 ,用于客户端访问,解除与具体数据库访问的耦合。
public interface IUser
{
void Insert(User user);
User GetUser(int id);
}
//SqlsercerUser 用于访问SqlServer的User
public class SqlsercerUser : IUser
{
public void Insert(User user)
{
Debug.Log("在SQL中添加记录");
}
public User GetUser(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
//AccesssUser 用于访问Access的User
public class AccesssUser : IUser {
public void Insert(User user) {
Debug.Log("在AccesssUser中添加记录");
}
public User GetUser(int id) {
Debug.Log("在AccesssUser中获得记录");
return null;
}
}
//IFactory07 接口, 定义一个创建访问User表对象的抽象的工厂接口。
public interface IFactory07
{
IUser CreatUser();
}
//SqlSercerFactory 类,实现 IFactory07 接口,实例化 SqlsercerUser
public class SqlSercerFactory :IFactory07
{
public IUser CreatUser()
{
return new SqlsercerUser();
}
}
//AccessFactory 类,实现 IFactory07 接口,实例化 AccessUser
public class AccessFactory : IFactory07 {
public IUser CreatUser() {
return new AccesssUser();
}
}
//User 表结构
public class User
{
public int ID { get; set; }
public string name { get; set; }
}
以上想想要换数据库只需要改一句就行了,由于多肽的关系,使得声明的 IUSer 接口的对象 iu 事先根本不知道是在访问那个数据库,却可以在运行时很好的完成工作,实现了业务逻辑与数据访问的解耦。
需要加一个数据库表 Department:
using UnityEngine;
using System.Collections;
public class AbstractFactoryStudy : MonoBehaviour {
// Use this for initialization
void Start () {
User user = new User();
// IFactory07 factory07 = new SqlSercerFactory(); 若要换数据库 这两句换一下就行了
IFactory07 factory07 = new AccessFactory();
IUser iu = factory07.CreatUser();
iu.Insert(user);
iu.GetUser(1);
Department dept = new Department();
IDepartment id = factory07.CreatDepartment();
id.Insert(dept);
id.GetDepartment(1);
}
}
public interface IDepartment
{
void Insert(Department department);
Department GetDepartment(int id );
}
public interface IUser
{
void Insert(User user);
User GetUser(int id);
}
public class SqlsercerDepartment : IDepartment {
public void Insert(Department department)
{
Debug.Log("在SQL中添加记录");
}
public Department GetDepartment(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
public class AccesssDepartment : IDepartment {
public void Insert(Department department) {
Debug.Log("在SQL中添加记录");
}
public Department GetDepartment(int id) {
Debug.Log("在SQL中获得记录");
return null;
}
}
public class SqlsercerUser : IUser
{
public void Insert(User user)
{
Debug.Log("在SQL中添加记录");
}
public User GetUser(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
public class AccesssUser : IUser {
public void Insert(User user) {
Debug.Log("在AccesssUser中添加记录");
}
public User GetUser(int id) {
Debug.Log("在AccesssUser中获得记录");
return null;
}
}
public interface IFactory07
{
IUser CreatUser();
IDepartment CreatDepartment();// 增加接口的方法
}
public class SqlSercerFactory :IFactory07
{
public IUser CreatUser()
{
return new SqlsercerUser();
}
public IDepartment CreatDepartment()//增加了 SqlsercerDepartment 工厂
{
return new SqlsercerDepartment();
}
}
public class AccessFactory : IFactory07 {
public IUser CreatUser() {
return new AccesssUser();
}
public IDepartment CreatDepartment()//增加了 AccesssDepartment 工厂
{
return new AccesssDepartment();
}
}
public class User
{
public int ID { get; set; }
public string name { get; set; }
}
//Department 表结构
public class Department {
public int ID { get; set; }
public string name { get; set; }
}
抽象工厂模式:提供一个创建一系列相关或互相依赖对象的接口, 而无需指定他们具体的类。
AbstractProductA 和 AbstractProductB 是两个抽象产品,之所以为抽象,是因为它们都有可能有两种不同的实现,就刚才的例子来说就是User 和 Department ,而 ProductA1 ProductA2 和 ProductB1 ProductB2 就是对两个抽象产品的具体分类的实现,比如ProductA1可以理解为是 SqlServerUser ,而 ProductB1 是 AccessUser。
IFactory 是一个抽象工厂接口, 它里面应该包含所有的产品创建的抽象方法。而 ConcreteFactory1 和 ConcreteFactory2 就是具体的工厂了,就像 SqlserverFactory 和 AccessFactory 一样。 通常是子啊运行时刻在创建一个ConcreteFactory 类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。
优点:易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。 它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
缺点:如果现在又需要加入表 Project 的话,那么至少要增加三个类:IProject SqlserverProject AccessProject 还需要更改 IFactory SqlserverFactory 和 AccessFactory 才可以完全实现。
客户端类并不只有一个,也就是调用IUser 或 IDepartment ,的地方都需要声明 IFactory factory = new AccessFactory(),要换数据库的话 每个地方都要改,所以 可以用简单工厂来改进抽象工厂, 去除 IFactory SqlserverFactory 和 AccessFactory 三个工厂类。 取而代之的是 DataAccess 类, 用一个简单工厂模式来实现。
using UnityEngine;
public class AbstractFactoryStudy : MonoBehaviour {
// Use this for initialization
void Start () {
User user = new User();
Department dept = new Department();
//直接得到实际的数据库访问实例,而不存在任何依赖。
IUser iu = DataAccess.CreateUser();
iu.Insert(user);
iu.GetUser(1);
//直接得到实际数据库访问实例,而不存在任何依赖
IDepartment id = DataAccess.CreateDepartment();
id.Insert(dept);
id.GetDepartment(1);
}
}
public class DataAccess
{
private static readonly string db = "Sqlserver";
// private static readonly string db = "Access"; 数据库名称可替换
public static IUser CreateUser()
{
IUser result = null;
switch (db)
{
case "Sqlserver":
result = new SqlsercerUser();
break;
case "Access":
result = new AccesssUser();
break;
}
return result;
}
public static IDepartment CreateDepartment()
{
IDepartment result = null;
switch (db)
{
case "Sqlserver":
result = new SqlsercerDepartment();
break;
case "Access":
result = new AccesssDepartment();
break;
}
return result;
}
}
public interface IDepartment
{
void Insert(Department department);
Department GetDepartment(int id );
}
public interface IUser
{
void Insert(User user);
User GetUser(int id);
}
public class SqlsercerDepartment : IDepartment {
public void Insert(Department department)
{
Debug.Log("在SQL中添加记录");
}
public Department GetDepartment(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
public class AccesssDepartment : IDepartment {
public void Insert(Department department) {
Debug.Log("在SQL中添加记录");
}
public Department GetDepartment(int id) {
Debug.Log("在SQL中获得记录");
return null;
}
}
public class SqlsercerUser : IUser
{
public void Insert(User user)
{
Debug.Log("在SQL中添加记录");
}
public User GetUser(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
public class AccesssUser : IUser {
public void Insert(User user) {
Debug.Log("在AccesssUser中添加记录");
}
public User GetUser(int id) {
Debug.Log("在AccesssUser中获得记录");
return null;
}
}
public class User {
public int ID { get; set; }
public string name { get; set; }
}
//Department 表结构
public class Department {
public int ID { get; set; }
public string name { get; set; }
}
上述代码抛弃了 IFactory SqlserverFactory 和 AccessFactory 三个工厂类,取而代之的是 DataAccess类,由于事先设置了db的值(Sqlserver 或 Access),所以简单工厂的方法都不需要传入参数,客户端没有出现任何一个 SQLServer 或 Access 的字样,达到了解耦的目的。
如果再需要加入Oracle数据库的访问的话 需要在DataAccess 类中每个方法的switch 中加case 也比较繁琐,可以利用反射来改进:
Assembly.Load("程序集名称").CreatInstance("命名空间.类名称")
反射引用的命名空间: using System.Reflection;
备注: ReadOnly与Const区别
using UnityEngine;
using System.Reflection;
public class AbstractFactoryStudy : MonoBehaviour {
void Start () {
User user = new User();
Department dept = new Department();
IUser iu = DataAccess.CreateUser();
iu.Insert(user);
iu.GetUser(1);
IDepartment id = DataAccess.CreateDepartment();
id.Insert(dept);
id.GetDepartment(1);
}
}
public class DataAccess
{
private static readonly string AssemblyName = "DaHuaSheJi";
private static readonly string db = "Sqlserver";//可换成 Access。 变化时 之变化这一句就可以了
public static IUser CreateUser()
{
string className = AssemblyName + "." + db + "User";
return (IUser) Assembly.Load(AssemblyName).CreateInstance(className);/* Unity 中这样写 Type t = Type.GetType("SqlsercerUser"); return (IUser) Activator.CreateInstance(t);*/ }
public static IDepartment CreateDepartment()
{
IDepartment result = null;
switch (db)
{
case "Sqlserver":
result = new SqlsercerDepartment();
break;
case "Access":
result = new AccesssDepartment();
break;
}
return result;
}
}
public interface IDepartment
{
void Insert(Department department);
Department GetDepartment(int id );
}
public interface IUser
{
void Insert(User user);
User GetUser(int id);
}
public class SqlsercerDepartment : IDepartment {
public void Insert(Department department)
{
Debug.Log("在SQL中添加记录");
}
public Department GetDepartment(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
public class AccesssDepartment : IDepartment {
public void Insert(Department department) {
Debug.Log("在SQL中添加记录");
}
public Department GetDepartment(int id) {
Debug.Log("在SQL中获得记录");
return null;
}
}
public class SqlsercerUser : IUser
{
public void Insert(User user)
{
Debug.Log("在SQL中添加记录");
}
public User GetUser(int id)
{
Debug.Log("在SQL中获得记录");
return null;
}
}
public class AccesssUser : IUser {
public void Insert(User user) {
Debug.Log("在AccesssUser中添加记录");
}
public User GetUser(int id) {
Debug.Log("在AccesssUser中获得记录");
return null;
}
}
public class User {
public int ID { get; set; }
public string name { get; set; }
}
//Department 表结构
public class Department {
public int ID { get; set; }
public string name { get; set; }
}
以上 ,如果在需要增加 Project 产品时,只需要增加三个与 Project 相关的类,在修改 DataAccess ,在其中增加一个 public static IProject CreatProject() 方法就可以了。
在追求完美一下, 就是将要读的数据库名称字符串放到配置文件中,添加一个 App.config文件,内容下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<addSettings>
<add key="DB" value="Sqlserver"/>
</addSettings>
</configuration>
所有的简单工厂都可以考虑用反射技术来去除 switch 或 if 解除分支判断带来的耦合。