1. 程式人生 > 實用技巧 >設計模式 - 12)抽象工廠

設計模式 - 12)抽象工廠

class User
{
    int _id;
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }

    string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

interface IUser
{
    void InsertUser(User user);
    User GetUser(int id);
}    

class SqlserverUser : IUser
{
    public void InsertUser(User user)
    {
        Console.WriteLine("Insert into user by Sqlserver");
    }

    public User GetUser(int id)
    {
        Console.WriteLine("Get user by Sqlserver");
        return null;
    }
}

class AccessserverUser : IUser
{
    public void InsertUser(User user)
    {
        Console.WriteLine("Insert into user by Accessserver");
    }

    public User GetUser(int id)
    {
        Console.WriteLine("Get user by Accessserver");
        return null;
    }
}

class Department
{
    int _id;
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }

    string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

interface IDepartment
{
    void InsertDepartment(Department department);
    Department GetDepartment(int id);
}

class SqlserverDepartment : IDepartment
{
    public void InsertDepartment(Department department)
    {
        Console.WriteLine("Insert into Department by Sqlserver");
    }

    public Department GetDepartment(int id)
    {
        Console.WriteLine("Get Department by Sqlserver");
        return null;
    }
}

class AccessserverDepartment : IDepartment
{
    public void InsertDepartment(Department department)
    {
        Console.WriteLine("Insert into Department by Accessserver");
    }

    public Department GetDepartment(int id)
    {
        Console.WriteLine("Get Department by Accessserver");
        return null;
    }
}

interface IFactoryDB
{
    IUser CreateUser();
    IDepartment CreateDepartment();
}

class SqlserverFactory : IFactoryDB
{
    public IUser CreateUser()
    {
        return new SqlserverUser();
    }

    public IDepartment CreateDepartment()
    {
        return new SqlserverDepartment();
    }
}

class AccessFactory : IFactoryDB
{
    public IUser CreateUser()
    {
        return new AccessserverUser();
    }

    public IDepartment CreateDepartment()
    {
        return new AccessserverDepartment();
    }
}   

User user = new User() { Name = "Boy", Id = 1 };
Department dp = new Department() { Name = "department", Id = 2 };
IFactoryDB dbFactory = new AccessFactory();

IUser iu = dbFactory.CreateUser();
iu.InsertUser(user);
iu.GetUser(user.Id);

IDepartment idp = dbFactory.CreateDepartment();
idp.InsertDepartment(dp);
idp.GetDepartment(dp.Id);

缺點:每增加一個表,就需要新增 3 個類的同時,
修改 IFactory、SqlserverFactory、AccessFactory 類,
這違法了開放-封閉原則。下面使用 DataAccess。

class DataAccess
{
    private static readonly string db = "Sqlserver";
    //private static readonly string db = "Access";
    //private static readonly string db = "Oracle";

    public static IUser CreateUser()
    {
        IUser result = null;
        switch (db)
        {
            case "Sqlserver":
                result = new SqlserverUser();
                break;
            case "Access":
                result = new AccessserverUser();
                break;                
        }
        return result;
    }

    public static IDepartment CreateDepartment()
    {
        IDepartment result = null;
        switch (db)
        {
            case "Sqlserver":
                result = new SqlserverDepartment();
                break;
            case "Access":
                result = new AccessserverDepartment();
                break;
        }
        return result;
    }
}

//業務程式碼:
User user = new User() { Name = "Boy", Id = 1 };
Department dp = new Department() { Name = "department", Id = 2 };

IUser iu = DataAccess.CreateUser();
iu.InsertUser(user);
iu.GetUser(user.Id);

IDepartment idp = DataAccess.CreateDepartment();
idp.InsertDepartment(dp);
idp.GetDepartment(dp.Id);

缺點:如果這時候再加一個 Oracle 資料庫,如果是原來的抽象工廠,只需要加一個 OracleFactory 就可以,現在每個方法的 switch 裡面都得再加一個 case。因此採用反射技術:

namespace WpfApp1.AbstractFactory {
class DataAccess
{
    private static readonly string AssemblyName = "WpfApp1";
    private static readonly string db = ConfigurationManager.AppSettings["DB"];

    public static IUser CreateUser()
    {
        string className = AssemblyName + ".AbstractFactory." + db + "User";
        return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
    }

    public static IDepartment CreateDepartment()
    {
        string className = AssemblyName + ".AbstractFactory." + db + "Department";
        return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
    }
}

// 業務程式碼:
User user = new User() { Name = "Boy", Id = 1 };
Department dp = new Department() { Name = "department", Id = 2 };

IUser iu = DataAccess.CreateUser();
iu.InsertUser(user);
iu.GetUser(user.Id);

IDepartment idp = DataAccess.CreateDepartment();
idp.InsertDepartment(dp);
idp.GetDepartment(dp.Id);