抽象工廠模式 - 設計模式學習

  抽象工廠模式(Abstract Factory),提供一個建立一系列相關或相互依賴對象的接口,而無需指定他們具體的類。javascript

  如下給出抽象工廠方法模式的UML圖:html

    

  回到《大話設計模式》裏面的雙數據庫訪問的例子:java

複製代碼
namespace ConsoleApplication1
{
    class User
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

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

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

        private string _deptNmae
        {
            get { return _deptNmae; }
            set { _deptNmae = value; }
        }
    }

    //IUser接口,用於客戶端訪問,解除與具體數據庫訪問的耦合
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

    class SqlserverUser : IUser 
    {
        public void Insert(User user)
        {
            Console.WriteLine("Sql Server添加一條記錄");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Sql Server根據ID獲得User表的一條記錄");
            return null;
        }
    }

    class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中給User表增長一條記錄");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根據ID獲得User表的一條記錄");
            return null;
        }
    }

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

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中給Department表插入一條記錄");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根據ID獲得Department表的一條記錄");
            return null;
        }
    }

    class AccessDepartment : IDepartment 
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中給Department表插入一條記錄");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根據ID獲得Department表的一條記錄");
            return null;
        }
    }

    interface IFactory
    {
        IUser CreateUser();

        IDepartment CreateDepartment();
    }

    //SQLServer實例化工廠,負責實例化SqlserverUser和SqlserverDepartment
    class SqlServerFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }

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

    //Access實例化工廠,負責實例化AccessUser和AccessDepartment
    class AccessFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new AccessUser();
        }

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

    class Program
    {
        static void Main(string[] args)
        {
            IFactory factory = new SqlServerFactory();

            IUser iu = factory.CreateUser();    //得到數據庫User訪問類的對象
            iu.Insert(new User());
            iu.GetUser(1);

            IDepartment idepartment = factory.CreateDepartment();   //得到數據庫Department訪問類的對象
            idepartment.Insert(new Department());
            idepartment.GetDepartment(1);

            Console.ReadKey();
        }
    }  
}
複製代碼

  其實以上代碼就叫作抽象工廠模式了,當只有一個User表的時候,就叫工廠方法模式。當有了多個表了,就叫抽象工廠模式。可是以上代碼是很是很差的,爲何?例如當你還要增長一個Project表的時候,就須要增長三個類,IProject、SqlserverProject、AccessProject,同時還須要更改IFactory、SqlserverFactory、AccessFactory。增長一張表就要更改三個類,這個是比較悲劇的。git

  所以,如下給出一個用簡單工廠模式來優化抽象工廠模式的代碼:數據庫

複製代碼
namespace ConsoleApplication1
{
    class User
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

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

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

        private string _deptNmae
        {
            get { return _deptNmae; }
            set { _deptNmae = value; }
        }
    }

    //IUser接口,用於客戶端訪問,解除與具體數據庫訪問的耦合
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

    class SqlserverUser : IUser 
    {
        public void Insert(User user)
        {
            Console.WriteLine("Sql Server添加一條記錄");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Sql Server根據ID獲得User表的一條記錄");
            return null;
        }
    }

    class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中給User表增長一條記錄");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根據ID獲得User表的一條記錄");
            return null;
        }
    }

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

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中給Department表插入一條記錄");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根據ID獲得Department表的一條記錄");
            return null;
        }
    }

    class AccessDepartment : IDepartment 
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中給Department表插入一條記錄");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根據ID獲得Department表的一條記錄");
            return null;
        }
    }

    class DataAccess
    {
        private static readonly string db = "SqlServer";    //事先就設置好的數據庫
        //private static readonly string db = "Access";     //也就是說,更改數據庫改的是這裏了 

        public static IUser CreateUser()    //這是第一個User的簡單工廠
        {
            IUser result = null;
            switch (db)         //根據db的設置實例化不一樣的User數據庫訪問對象
            {
                case "SqlServer":
                    result = new SqlserverUser();
                    break;
                case "Access":
                    result = new AccessUser();
                    break;
            }
            return result;
        }

        public static IDepartment CreateDepartment()    //這是Department的簡單工廠
        {   
            IDepartment result = null;
            switch (db)
            {
                case "Sqlserver":
                    result = new SqlserverDepartment();
                    break;
                case "Access":
                    result = new AccessDepartment();
                    break;
            }
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUser iu = DataAccess.CreateUser();    //得到數據庫User訪問類的對象,要改數據庫就要改DataAccess裏面的設置
            iu.Insert(new User());
            iu.GetUser(1);

            IDepartment idepartment = DataAccess.CreateDepartment();   //得到數據庫Department訪問類的對象,這裏沒有依賴SqlserverFactory或AccessFactory了
            idepartment.Insert(new Department());
            idepartment.GetDepartment(1);

            Console.ReadKey();
        }
    }  
}
複製代碼

  通過簡單工廠模式優化以後,客戶端就再也不依賴於SqlserverFactory或AccessFactory了,帶到了解耦的目的。若是要添加一個Project,只須要添加響應類,但改就只須要修改DataAccess就能夠了(在DataAccess裏面在添加一個簡單工廠)。很明顯者不是最終方案,由於簡單工廠方法仍是要修改,switch case。設計模式

  如下還有經過反射實現的更加好的方案:app

複製代碼
namespace ConsoleApplication1
{
    class User
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

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

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

        private string _deptNmae
        {
            get { return _deptNmae; }
            set { _deptNmae = value; }
        }
    }

    //IUser接口,用於客戶端訪問,解除與具體數據庫訪問的耦合
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

    class SqlserverUser : IUser 
    {
        public void Insert(User user)
        {
            Console.WriteLine("Sql Server添加一條記錄");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Sql Server根據ID獲得User表的一條記錄");
            return null;
        }
    }

    class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中給User表增長一條記錄");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根據ID獲得User表的一條記錄");
            return null;
        }
    }

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

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中給Department表插入一條記錄");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根據ID獲得Department表的一條記錄");
            return null;
        }
    }

    class AccessDepartment : IDepartment 
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中給Department表插入一條記錄");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根據ID獲得Department表的一條記錄");
            return null;
        }
    }

    class DataAccess
    {
        private static readonly string AssemblyName = "ConsoleApplication1";    //字符串爲程序集的名字
        private static readonly string db = ConfigurationManager.AppSettings["DB"];

        public static IUser CreateUser()
        {
            string className = AssemblyName + "." + db +"User"; //拼接SqlserverUser類或AccessUser類所在的位置
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);    //反射建立實例
        }

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

    class Program
    {
        static void Main(string[] args)
        {
            IUser iu = DataAccess.CreateUser();    //得到數據庫User訪問類的對象,要改數據庫就要改DataAccess裏面的設置
            iu.Insert(new User());
            iu.GetUser(1);

            IDepartment idepartment = DataAccess.CreateDepartment();   //得到數據庫Department訪問類的對象
            idepartment.Insert(new Department());
            idepartment.GetDepartment(1);

            Console.ReadKey();
        }
    }  
}
複製代碼

  配置文件App.config代碼:ide

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DB" value="Access" />
  </appSettings>
</configuration>

  以上就是反射+抽象工廠的方法, 仍是很是強大的,強大到什麼程度呢?例如上面的代碼,要修改數據庫訪問對象例如將Sqlserver改成Access根本不須要改動程序從新編譯,只須要改動App.config就能夠了。其實這裏的反射只是改進了簡單工廠模式而已,與抽象工廠沒太大的關係。要記住,全部在用到簡單工廠的地方均可以考慮用反射技術來去除switch解除分支判斷所帶來的耦合。post

  在以上的代碼中,要增長一個Project表的話,只須要添加三個Project相關的類(擴展),再修改DataAccess,在其中增長一個CreateProject()方法就能夠了。學習

 
 
分類: 設計模式
 
0
0
 
(請您對文章作出評價)
 
« 上一篇: 觀察者模式 - 設計模式學習
» 下一篇: 狀態模式 - 設計模式學習
相關文章
相關標籤/搜索