【设计模式】一石三鸟——三个工厂

      最近一直在总结设计模式,但总是总结了又改,改了再改,有一种雾里看花,抓不到重点的感觉。我感觉自己陷入了怪圈,不能跳出来宏观看问题!所以,最近几天一直不能发表文章,真真是山穷水尽!不管了,先从它们三个开始,希望柳暗花明。。


      例子

      在设计模式中有这样一个问题,一个软件公司要给两家企业做分别做一个薪资管理系统,这两家企业的需求都是很相似的。不同的是,一家用SQL Server作为数据库,另一家用Access作为数据库。如果你是开发员,怎样才能更高效地完成任务?!

     不用着急,怎么一步步探索!


     一、工厂方法——解决更换数据库问题

     从问题中,我们可以分析出,它的关键点是在换数据库。现在以“新增用户”和“得到用户”为例,首先画一个工厂方法代码结构图,从宏观上进行分析:

【设计模式】一石三鸟——三个工厂_第1张图片

     按照图,实现代码

   
    class program
    {
        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; }
            }
        //IUser接口,用于客户端访问,解除与具体数据库访问的耦合
        interface IUser
        {
            void Insert(User user);
            User GetUser(int id);
        }
        //SqlUser类,用于访问SQL的User
        class SqlUser : IUser
        {
            public void Insert(User user)
            {
                Console.WriteLine("在SQL中给User表增加一条记录");
            }
            public User GetUser(int id)
            {
                Console.WriteLine("在SQL中根据ID得到User表的一条记录");
                return null;
            }
        }
        //AccessUser类,用于访问Access的User
        class AccessUser : IUser
        {
            public void Insert(User user)
            {
                Console.WriteLine("在Access中给User表增加一条记录");
            }
            public User GetUser(int id)
            {
                Console.WriteLine("在Access中根据ID得到User表的一条记录");
                return null;
            }
        }
        //IFactory接口,定义一个创建访问User表对象的抽象的工厂接口
        interface IFactory
        {
            IUser CreateUser();
        }
        //SqlFactory类,实现IFactory接口,实例化SqlUser
        class SqlFactory : IFactory
        {
            public IUser CreateUser()
            {
                return new SqlUser();
            }
        }
        //Access类,实现IFactory接口,实例化AccessUser
        class AccessFactory : IFactory
        {
            public IUser CreateUser()
            {
                return new AccessUser();
            }
        }
    
        //客户端
        static void Main(string[] args)
        {
            User user = new User();
            IFactory factory = new SqlFactory(); //若要更改成Access数据库,只需将SqlFactory()换成AccessFactory()
            IUser iu = factory.CreateUser();
            iu.Insert(user);
            iu.GetUser(1);
            Console.Read();
        }
    }
}

     通过工厂方法,现在如果要换数据库,只需要把IFactory factory = new SqlFactory()改成IFactory factory =new AccessFactory()。此时,由于多态的关系,使得声明IUser接口的对象iu事先根本不知道是在访问哪个数据库,却可以在运行时很好地完成工作,这就是所谓的业务逻辑与数据访问的解耦。


     二、抽象工厂——解决创建多个对象问题

     由于是开发薪资管理系统,在数据库中不可能只有一个User表,可能会有很多表,比如增加一个部门表(Dpt),那我们应该怎么办?这时候就用到抽象工厂了

     代码结构图

【设计模式】一石三鸟——三个工厂_第2张图片

     看到此图,再与之前的工厂方法结构图作对比,我们可以发现,它们之间没有结构上的变化,只是增加了一个部门表接口以及它的两个实现类。由此,我们可以得出一个结论:抽象工厂是工厂方法的拓展。当涉及到多个产品系列的问题时,我们就可以使用抽象工厂。


     三、简单工厂——解耦

     如果要使用抽象工厂,那么相应的就会增加三个类:IDpt、SqlDpt、AccessDpt,还需要更新IFactory、SqlFactory、AccessFactory才可以完全实现。俗话说,编程是一门艺术!这样大批量的改动,显然是非常丑陋的做法。我们可以用简单工厂来对抽象工厂进行改进。

     代码结构图

     【设计模式】一石三鸟——三个工厂_第3张图片

   咱们先不写代码,单是从图上来看,大道至简的气息跃然纸上!话不多说,看代码:

       

 
        class DataAccess
        //class DataSql
        {
            private static readonly string db = "Sql";
            public static IUser CreateUser()
            {
                IUser result = null;
                switch (db)
                {
                    case "Sql":
                        result = new SqlUser();
                        break;
                    case "Access":
                        result = new AccessUser();
                        break;
                }
                return result;
            }
            public static IDpt CreateDpt()
            {
                IDpt result = null;
                switch (db)
                {
                    case "Sql":
                        result = new SqlDpt();
                        break;
                    case "Access":
                        result = new AccessDpt();
                        break;
                }
                return result;
            }
        }  

     从图和代码中我们可以看出,去除了IFactory、SqlFactory、AccessFactory三个工厂类,取而代之的DataAccess类。由于事先设置了db的值(Sql或Access),所以,简单工厂的方法都不需要输入参数,这样在客户端就只需要DataAccess.CreateUser()和DataAccess.CreateDpt()来生成具体的数据库访问类实例,而客户端没有出现任何一个SQL Server或Access的字样,达到了解耦的目的。。


     四、反射——去除switch

     通过上面的修改,我们的代码无论是在复杂程度上,还是在灵活性上,都得到了很大的改善。但是,,,,,在传统的23中设计模式中,并没有简单工厂模式!Why?????它使我们的代码更加清晰整洁,达到了解耦的目的,难道它还有不足吗?答案是肯定的。。

    设计模式的六大原则中,最为核心的一项原则是开放-封闭原则它的定义我不多说,它的重要性我也不多做解释。简单工厂违背了这项原则,因为它使用了switch

    解决的办法就是反射!话不多说,先看代码

       

        using System.Reflection;  //用来引用Reflection,帮助我们解决简单工厂的不足      
        class DataAccess
        //class DataSql
        {
            private static readonly string AssemblyName = "抽象工厂模式";  //程序集
            private static readonly string db = "Sql";  //数据库名称,可替换为Access
            public static IUser CreateUser()
            {
                string className=AssemblyName + "."+db+"User";
                return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
            }
             public static IDpt CreateUser()
            {
                string className=AssemblyName + "."+db+"Dpt";
                return (IDpt)Assembly.Load(AssemblyName).CreateInstance(className);
            }
        }

     通过反射,我们将程序由编写时转为运行时。由于CreateInstane(className)中的字符串是可以写成变量的,而变量的值到底是SQL Sever还是Access,完全可以由事先的那个db变量来决定。所以,就去除了switch判断的麻烦。


     五、配置文件——不用更改DataAccess类(Perfect)

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

     添加App.config文件

【设计模式】一石三鸟——三个工厂_第4张图片

  添加引用System.configuration

【设计模式】一石三鸟——三个工厂_第5张图片

   在开头增加using System.Configuration

   using System.Configuration;      
        class DataAccess
        //class DataSql
        {
            private static readonly string db = ConfigurationManager.AppSettings["DB"];  //表示配置文件

        }  


     这样一来,咱们的软件就真正的完美了。

 

     小结

     整个过程下来,我们使用工厂方法达到了代码的可复用性,提高了效率;使用抽象工厂达到了软件的可拓展性;使用简单工厂改进抽象工厂,达到了解耦的目的;使用反射技术,去除了简单工厂带来的switch或if,更进一步解除了分之判断带来的耦合,是软件更易维护和拓展;最后,使用配置文件达到最完美的效果。

     好了,总结完三工厂,果然又有了思路。。


     

你可能感兴趣的:(※〖设计模式〗,设计模式,工厂模式,面向对象,实例)