Loading

设计模式——抽象工厂模式的实现[备忘录]

业务场景:实现系统数据库的切换,运用抽象工厂模式(Abstract Factory)。

首先呢,我们利用工厂方法模式来实现,温习一下工厂方法模式。

业务场景条件:数据表:user(id,name);

                               department(id,name);

业务场景UML图:

思路:

1、定义实体类User、Department。

2、定义接口IUser、IDepartment,并声明void Insert(User user)、GetUser(int id);void Insert(Department department)、GetDepartment(int id)方法;

定义接口IUser、IDepartment用于客户端的访问,解除与具体数据库访问的耦合。

3、分别编写AccessUser(继承IUser,并实现该接口),SQLserverUser(继承IUser,并实现该接口);AccessDepartment(继承IDepartment,并实现该接口),SQLserverDepartment(继承IDepartment接口,并实现该接口)。

4、定义工厂接口IFactory(createUser、createDepartment)。

5、分别定义具体工厂AccessFactory、SQLserverFactory分别继承IFactory接口并实现该接口。

 

实现代码:

1、

User
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    public 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; }
        }
    }
    
}
Department
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    public class Department
    {
        private int _id;

        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
        private string _name;

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

2、

IUser
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal interface IUser
    {
        void Insert(User user);
        User GetUser(int id);

    }
}
IDepartment
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal interface IDepartment
    {
        void Insert(Department deparment);
        Department GetDepartment(int id);
    }
}

3、

AccessUser
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal class AccessUser:IUser
    {

        #region IUser 成员

        public void Insert(User user)
        {
            Console.WriteLine("Access正在为您添加一个用户……");
            Console.WriteLine("Access已经成功添加一个用户。");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Access正在为您查找id为" + id + "的用户……");
            Console.WriteLine("Access返回" + id + "用户");
            return null;
        }

        #endregion
    }
}
AccessDepartment
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal class AccessDepartment:IDepartment
    {

        #region IDepartment 成员

        public void Insert(Department deparment)
        {
            Console.WriteLine("Access正在为您添加一个部门……");
            Console.WriteLine("Access已经成功添加一个部门。");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("Access正在为您查找id为" + id + "的部门……");
            Console.WriteLine("Access返回" + id + "部门");
            return null;
        }

        #endregion
    }
}
SQLserverUser
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal class SqlserverUser:IUser
    {
        #region IUser 成员

        public void Insert(User user)
        {
            Console.WriteLine("SQLserver正在为您添加一个用户……");
            Console.WriteLine("SQLserver已经成功添加一个用户。");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("SQLserver正在为您查找id为"+id+"的用户……");
            Console.WriteLine("SQLserver返回"+id+"用户");
            return null;
        }

        #endregion
    }
}
SQLserverDepartment
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal class SQLserverDepartment:IDepartment
    {

        #region IDepartment 成员

        public void Insert(Department deparment)
        {
            Console.WriteLine("SQLserver正在为您添加一个部门……");
            Console.WriteLine("SQLserver已经成功添加一个部门。");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("SQLserver正在为您查找id为" + id + "的部门……");
            Console.WriteLine("SQLserver返回" + id + "部门");
            return null;
        }

        #endregion
    }
}

4、

IFactory
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal interface IFactory
    {
        IUser CreateUser();
        IDepartment CreateDepartment();
    }
}

5、

AccessFactory
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal class AccessFactory:IFactory
    {
        #region IFactory 成员

        public IUser CreateUser()
        {
            return new AccessUser();

        }

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

        #endregion
    }
}
SQLserverFactory
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    internal class SQLserverFactory:IFactory
    {
        #region IFactory 成员

        public IUser CreateUser()
        {
            return new SqlserverUser();
        }

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

        #endregion
    }
}

客户端调用代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    class Program
    {
        static void Main(string[] args)
        {
            User user = new User();
            Department department = new Department();
            //IFactory factory = new SQLserverFactory();//根据实例化具体的数据库,赋给factory对象。
            IFactory factory = new AccessFactory();
            IUser iuser = factory.CreateUser();
            iuser.Insert(user);
            iuser.GetUser(1);

            IDepartment idepartment = factory.CreateDepartment();
            idepartment.Insert(department);
            idepartment.GetDepartment(1);

            Console.ReadLine();
        }
    }
}

 

上述代码属于抽象工厂模式。但是觉得很是带着许多工厂方法模式的影子。

抽象工厂的定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

 

这样设计能实现Access与SQLserver灵活的切换,但是如果新增Oracle话,我们需要创建OracleFactory、OracleUser、OracleDepartment等等。

更糟糕的情况是:我们有新添一张Project表,我们不得不添加IProject、SQLserverProject、AccessProject,还需要改动IFactory、SQLserverFactory、AccessFactory。

 

这样我们为了解决这个问题,我们先利用反射+抽象工厂来实现数据访问程序。

 

反射可以有效的解决switch case分支。

反射听起来不好理解,用起来却很神奇。

基本格式:Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")。需引用System.Reflection命名空间。

 

接下来我们用反射来改造一下程序:

UML图:

 

这样,我们就DataAccess取代了IFactory、SqlserverFactory、AccessFactory三个工厂。

DataAccess类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;
//引入反射命名空间

namespace AbstractFactory
{
    public class DataAccess
    {
        private static readonly string AssemblyName = "AbstractFactory";
        private static readonly string database = "Sqlserver";

        public  static IUser CreateUser()
        {
            string classname = AssemblyName + "." + database + "User";
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(classname);
            //Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")。需引用System.Reflection命名空间。

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

    }
}

如果我们要实现从Sqlserver切换到Access,只需要将

private static readonly string database = "Sqlserver";

改写成:private static readonly string database = "Access";  即可。

客户端调用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;



namespace AbstractFactory
{
    class Program
    {
        static void Main(string[] args)
        {
            //User user = new User();
            //Department department = new Department();
            ////IFactory factory = new SQLserverFactory();//根据实例化具体的数据库,赋给factory对象。
            //IFactory factory = new AccessFactory();
            //IUser iuser = factory.CreateUser();
            //iuser.Insert(user);
            //iuser.GetUser(1);

            //IDepartment idepartment = factory.CreateDepartment();
            //idepartment.Insert(department);
            //idepartment.GetDepartment(1);
            
            //对比一下抽象工厂模式

            //反射

            User user = new User();
            Department department = new Department();

            IUser iuser = DataAccess.CreateUser();
            iuser.Insert(user);
            iuser.GetUser(2);

            IDepartment idepartment = DataAccess.CreateDepartment();
            idepartment.Insert(department);
            idepartment.GetDepartment(2);
            Console.ReadLine();
        }
    }
}

 

 在DataAccess类中我们需要改写代码,才能完成数据库的切换。如果我们使用配置文件动态的读取,以实现数据库的切换,这样我们的程序才算完美。

接下来我们继续改造我们的程序。

1、添加“应用程序配置文件”---App.config

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

2、修改DataAccess类。引入System.Configuration,实现动态的读取App.config文件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;
//引入反射命名空间
using System.Configuration;
//引入配置文件命名空间

namespace AbstractFactory
{
    public class DataAccess
    {
        private static readonly string AssemblyName = "AbstractFactory";
        //private static readonly string database = "Sqlserver";
        private static readonly string database=ConfigurationSettings.AppSettings["DB"];

        public  static IUser CreateUser()
        {
            string classname = AssemblyName + "." + database + "User";
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(classname);
            //Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")。需引用System.Reflection命名空间。

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

    }
}

 

 这样我们通过改写App.config来实现数据库的切换,不用在修改程序本身。

抽象工厂总结:

  • 抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 优点:1、易于交换产品系列。

                2、使得具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口来操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。

  • 缺点:就是我们上面所说的新增project表的情况:“我们有新添一张Project表,我们不得不添加IProject、SQLserverProject、AccessProject”,比较难以支持新的产品种类。
  • 应用情景:

    • 同一个产品族的产品在一起使用时,而且它们之间是相互依赖的,不可分离
    • 系统需要由相互关联的多个对象来构成
    • 你想提供一组对象而不显示它们的实现过程,只显示它们的接口
    • 系统不应当依赖某一些具体产品类。

    应用场景举例:

    • 游戏开发中的多风格系列场景
    • 系统更改皮肤
    • 支持多种观感标准的用户界面工具箱(Kit)。

 

本文版权归本人和博客园共同所用,转载请注明出处。

 

posted @ 2012-06-01 15:14  Cooper_Liu  阅读(356)  评论(0编辑  收藏  举报