[置顶] 设计模式——抽象工厂之反射“+”

前言

  在学习设计模式时,我接触到了简单工厂模式、工厂模式和抽象工厂模式,敲完三个模式的小例子,还是感觉抽象工厂模式比较好。代码与代码之间,类与类,接口与接口之间耦合显然降到了至今的最低。大大提高了复用性和后期软件的维护,方便了用户需求的更改。

内容

  抽象工厂设计模式中,最为典型的是反射“+”思想。下面举例子说明:

背景

  给一家企业做的电子商务网站,使用SQLServer作为数据库的,应该说上线后除了开始有些小问题,基本都还可以,而后,公司接到了另外一家公司类似的需求的项目,但是这个公司采用比较省钱的方式,租用了一个空间,只能用Access,不能用SQL Server。目标是将原来用SQL Server作为数据库的代码改为用Access作为数据库的代码。

实践

  这样的要求就要看原来的代码了,如果原代码之间耦合度够低,那么转换起来不至于那么费劲。且看用抽象工厂模式写的代码:

用户接口部分:

//作者:周丽同
    //用户接口
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }
    //sqlserverUser类
    class SqlserverUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在SQL中User表中增加了一条记录");
        }
        public User GetUser(int id)
        {
            Console.WriteLine("在SQL Server中根据ID得到User表一条记录");
            return null;
        }
    }

    //AccessUser类
    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);
    }
    //sqlserverDepartment
    class SqlserverDepartment:IDepartment
    {
        public void Insert (Department department)
        {
            Console.WriteLine("在sql server中给department表添加了一条记录");
        }
        public Department GetDepartment(int id)
        {
            Console.WriteLine("在sql server中根据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 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 _deptname;
        public string DeptName
        {
            get { return _deptname; }
            set { _deptname = value; }
        }
    }

数据库部分:

 //作者:周丽同
    class DataAcess
    {
        private static readonly string db = "Sqlserver";//数据库名称,可以替换成Access;
        //private static readonly string db="Access";  

        public static IUser CreateUser()
        {
            IUser result = null;
            switch(db )//由于db的事先设置,所以此处可以根据选择实例化出相应的对象;
            {
                case "Sqlserver":
                    result = new SqlserverUser();
                    break;
                case "Access":
                    result =new AccessUser ();
                    break ;
            }
            return result ;
        }

        public static IDepartment  CreateDepartment()
        {
            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)
        {
            User user = new User();
            Department dept = new Department();

            IUser iu = DataAcess.CreateUser();//直接得到实际的数据库访问实例,而不存在任何依赖;

            iu.Insert(user);
            iu.GetUser(1);

            IDepartment id = DataAcess.CreateDepartment();
            id.Insert(dept);
            id.GetDepartment(1);
            Console.Read();
        }

    }

   上面的代码,很好的提高了复用性和后期维护性,贯彻了开——闭原则。但是如果下一个公司做网站要用Oracle数据库访问,那该怎么办?按着上面的代码思路只能改DataAccess类,在每个方法的switch中加case了。

反射+抽象工厂的数据访问程序

   这里如果用到一个简单的.net技术就完美解决了。

格式是:

//Assembly.Load ("程序集名称".CreateInstance ("命名空间.类名称"))//只有在程序顶端协商using System.Reflection;来引用Reflection。
有了反射,我们获得实例可以用下面两种写法:

//常规写法
 //   IUser result=new SqlserverUser();

//反射的写法
//using System.Reflection;

//    IUser result =(IUser)Assembly.Load("抽象工厂模式(当前程序的名称)").CreateInstance("抽象工厂模式(当前命名空间名称).SqlserverUser(要实例化的类名)");

修改代码部分:

//作者:周丽同
    class DataAccess
    {
        private static readonly string AssemblyName = "抽象工厂模式";
        private static readonly string db = "Sqlserver";//数据库名称,可替换成Access;

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

  比以前,代码是漂亮多了,但是在更换数据库访问时,还是需要去改程序(改db这个字符串的值)重编译,不完全符合真正的开放——封闭原则。反射+配置文件实现数据库访问程序可以解决更改DataAccess问题。

反射+配置文件实现数据访问程序

  我们可以通过读文件来给DB字符串赋值,在配置文件中写明是Sql Server还是Access,这样DataAccess类也不用更改了。

添加一个App.config文件:

//作者:周丽同
    <?xml version ="1.0" endcoding ="utf-8"?>
    <configuration>
        <appSetttings>
            <add key="DB" value="Sqlserver"/>//配置文件可以换成Access或者Oracle;
        </appSettings>
    <configuration>
  再添加引用 System.configuraiton,并在程序开头增加 using System.Configuration;,然后更改DataAccess类字段DB的赋值代码。

#region
    private static readonly string db=ConfigurationManager.AppSettings["DB"];
#endregion

小结

  这样看来,其实在一些用到“switch~case或者If”的语句,都可以考虑用反射技术来解除分支判断带来的耦合。三大工厂模式,自认为,抽象工厂最为重要,而且里面的反射思想用的很棒!

感谢您的宝贵时间~~~




你可能感兴趣的:([置顶] 设计模式——抽象工厂之反射“+”)