• 简单工厂模式,工厂方法模式,抽象工厂模式,spring的狂想


          菜鸟D在项目中遇见一个比较纠结的高耦合,所以就想办法来解耦。情况是这样的:系统通过用户选择treeview控件的节点判断调用不同的处理,这些处理中某些东西又是类似的。同事的建议是采用简单工厂,耦合就耦合吧,反正treeview节点基本是不会变化的。(能偷懒就偷懒吧)

          菜鸟D有些偏执,想找些方法来解耦。于是就学习了这个几种方法,虽然不一定用的上,多学一点总是好的。

          首先说简单工厂,例子是一个已经二到死的计算器。

          简单工厂由三种角色组成:工厂类角色(creator),抽象产品类角色(product,图中为Iproduct接口),具体产品类角色(concreteproduct,图中为Product_A…)。

        代码如下:

    public class Calculate
        {
            public double DValue1 { get; set; }
            public double DValue2 { get; set; }
    
            public virtual double GetResult()
            {
                return 0;
            }
        }
    
        public class Add : Calculate
        {
            public override double GetResult()
            {
                return DValue1 + DValue2;
            }
        }
    
        public class Sub : Calculate
        {
            public override double GetResult()
            {
                return DValue1 - DValue2;
            }
        }
    
        public class Mul : Calculate
        {
            public override double GetResult()
            {
                return DValue1 * DValue2;
            }
        }
    
        public class Div : Calculate
        {
            public override double GetResult()
            {
                if (DValue2.Equals(0))
                {
                    if (DValue1.Equals(0))
                    {
                        return 1;
                    }
                    return 0;
                }
                return DValue1 / DValue2;
            }
        }
    基本的代码
                #region 客户端
                Console.WriteLine("请输入第一个数");
                string v1 = Console.ReadLine();
                Console.WriteLine("请输入运算符");
                string oper = Console.ReadLine();
                Console.WriteLine("请输入第二个数");
                string v2 = Console.ReadLine();
                Calculate c = GetCalculateByOper(oper);
                c.DValue1 = Convert.ToDouble(v1);
                c.DValue2 = Convert.ToDouble(v2);
                Console.WriteLine("结果:" + c.GetResult()); 
                #endregion
    
            #region 工厂方法
            private static Calculate GetCalculateByOper(string oper)
            {
                Calculate c = new Calculate();
                switch (oper)
                {
                    case "+": c = new Add();
                        break;
                    case "-": c = new Sub();
                        break;
                    case "*": c = new Mul();
                        break;
                    case "/": c = new Div();
                        break;
                }
                return c;
            } 
            #endregion    

          在这个例子中没有使用抽象类,接口,而是用了虚方法(纯粹是不想在接口里定义属性,自己喜欢用什么就用什么,毕竟这不是简单工厂的核心)。简单工厂的核心是充当工厂类角色的工厂方法(起码在此例中是这样),逻辑判断中使具体产品类和工厂直接耦合了(好像类图的关系已经决定了这种耦合,但是将具体产品和客户端解耦)。如果要添加一个开方运算,首先添加开方的运算类,然后打开工厂方法进行修改。(呵呵,违反了开闭原则)

         接下来再说一下工厂方法模式,类图如下:

          先分析一下类图,工厂模式有了四个角色:工厂接口,工厂实现,产品接口,产品实现(其实说角色不贴切,还不如说是种类)。工厂实现和产品实现有了依赖关系,通过调用工厂实现来获取产品实现。也就是一种产品实现对应一种工厂实现。

         还是那个二的要死的计算器的例子,代码如下:

        public interface IFactory
        {
            ICalculatable GetCalculatable();
        }
    
        public interface ICalculatable
        {
            double GetReslut(double d1, double d2);
        }
    
        public class Add : ICalculatable
        {
            public double GetReslut(double d1, double d2)
            {
                return d1 + d2;
            }
        }
    
        public class AddFactory : IFactory
        {
            public ICalculatable GetCalculatable()
            {
                return new Add();
            }
        }
    
        public class Sub : ICalculatable
        {
            public double GetReslut(double d1, double d2)
            {
                return d1 - d2;
            }
        }
    
        public class SubFactory : IFactory
        {
            public ICalculatable GetCalculatable()
            {
                return new Sub();
            }
        }
    
        public class Mul : ICalculatable
        {
            public double GetReslut(double d1, double d2)
            {
                return d1 * d2;
            }
        }
    
        public class MulFactory : IFactory
        {
            public ICalculatable GetCalculatable()
            {
                return new Mul();
            }
        }
    
        public class Div : ICalculatable
        {
            public double GetReslut(double d1, double d2)
            {
                if (d2.Equals(0))
                {
                    if (d1.Equals(0))
                    {
                        return 1;
                    }
                    return 0;
                }
                return d1 / d2;
            }
        }
    
        public class DivFactory : IFactory
        {
            public ICalculatable GetCalculatable()
            {
                return new Div();
            }
        }
    基本代码
    #region 逻辑判断
        public class Factory
        {
            public static IFactory GetCalculatableByOper(string oper)
            {
                IFactory factory = null;
                switch (oper)
                {
                    case "+": factory = new AddFactory();
                        break;
                    case "-": factory = new SubFactory();
                        break;
                    case "*": factory = new MulFactory();
                        break;
                    case "/": factory = new DivFactory();
                        break;
                }
                return factory == null ? null : factory;
            }
        } 
    #endregion
    
                #region 客户端
                Console.WriteLine("请输入第一个数");
                string v1 = Console.ReadLine();
                Console.WriteLine("请输入运算符");
                string oper = Console.ReadLine();
                Console.WriteLine("请输入第二个数");
                string v2 = Console.ReadLine();
                ICalculatable calculatable = Factory.GetCalculatableByOper(oper).GetCalculatable();
                Console.WriteLine("结果:" + calculatable.GetReslut(Convert.ToDouble(v1), Convert.ToDouble(v2))); 
                #endregion

          注意:此处的判断逻辑已经不属于工厂模式了(类图中很明确),也可以脱离客户端。虽说简单工厂也可以这样将判断逻辑脱离客户端,但是简单工厂的逻辑判断是在工厂类中的,也就依然是简单工厂模式的一部分(此处逻辑有些乱,不懂的就认真研究类图)。通常在工厂模式的客户端需要用到哪种工厂再去创建哪种工厂,而不是如例子中的使用一个判断逻辑来选择,也就是工厂模式创建工厂是通过“外部”的逻辑来确定,而模式本身是不会做判断的。(所以这个例子并不是一个合适的例子)

          此时,计算器要再添加一个开方的方法,只需添加方法实现,添加工厂实现,不会对原有的代码做修改,符合了开闭原则(你当我瞎啊,你不修改判断逻辑吗?说了多少次了,判断逻辑部分不属于工厂模式。不过比简单工厂确实多写不少东西)。

          工厂方法模式通常在以下两种情况中使用 :

          第一,需要使用某种产品,客户端很清楚应该使用哪个具体工厂,此种情况不存在上述的判断逻辑,只需要实例化具体工厂,然后生产具体产品。

          第二,需要使用某种产品,但是不想也不需要知道是哪个工厂创建,此种情况存在一定的判断逻辑,但是客户端不需要知道这个逻辑,生产方(工厂模式)是通过外部的逻辑来生产产品,这时由外部来实例化具体工厂,生产具体产品交付给客户端。

          接着再来说说抽象工厂,在抽象工厂中最明显的一个特点是产品不止一类了,所以前文提到的计算机的例子就不适合使用抽象工厂模式(窃以为如此,有反对意见的欢迎提出)。

          在抽象工厂在提到一个“产品族”的概念,其实在抽象工厂中最为直观的体现就是产品A接口,产品B接口甚至是产品C、D接口。类图如下,从类图中不难发现,抽象工厂模式的具体工厂角色可以生产多种产品(工厂方法模式的具体工厂只能生产一种产品,不信去看类图)。就好像东方红拖拉机厂在工厂方法模式下只能生产拖拉机,但是在抽象工厂模式下不仅能生产拖拉机,还能生产卡车(如果加一个坦克的生产线,还能生产坦克)。

          此处就用比较熟悉的农场的例子,农场生产水果和蔬菜,水果分为北方水果和热带水果,蔬菜也一样,所以农场也分为北方农场和热带农场。

          代码如下:

        public interface IFruit
        {
            string Show();
        }
    
        public interface IVeggie
        {
            string Show();
        }
    
        public class NFruit : IFruit
        {
            private string name;
            public string Name
            {
                get { return "北方" + name; }
                set { name = value; }
            }
    
            public string Show()
            {
                return Name;
            }
        }
    
        public class TFruit : IFruit
        {
            string name;
    
            public string Name
            {
                get { return "热带" + name; }
                set { name = value; }
            }
    
            public string Show()
            {
                return Name;
            }
        }
    
        public class NVeggie : IVeggie
        {
            string name;
    
            public string Name
            {
                get { return "北方" + name; }
                set { name = value; }
            }
    
    
            public string Show()
            {
                return Name;
            }
        }
    
        public class TVeggie : IVeggie
        {
            string name;
    
            public string Name
            {
                get { return "热带" + name; }
                set { name = value; }
            }
    
            public string Show()
            {
                return Name;
            }
        }
    
        public interface Factory
        {
            IFruit CreateFruit(string nm);
            IVeggie CreateVeggie(string nm);
        }
    
        public class NFactory : Factory
        {
            public IFruit CreateFruit(string nm)
            {
                return new NFruit() { Name = nm };
            }
    
            public IVeggie CreateVeggie(string nm)
            {
                return new NVeggie() { Name = nm };
            }
        }
    
        public class TFactory : Factory
        {
            public IFruit CreateFruit(string nm)
            {
                return new TFruit() { Name = nm };
            }
    
            public IVeggie CreateVeggie(string nm)
            {
                return new TVeggie() { Name = nm };
            }
        }
    基本代码

          此例的逻辑判断部分没写。从类图上看,它是不属于抽象工厂模式的,从简化客户端的角度,这个逻辑判断是可以从客户端剥离的,很显然和工厂方法模式的逻辑判断属于同一地位(有种两头受气的感觉)。

         然后再简单说spring的依赖注入。在spring中需要获取一个产品(实例)如何获取?容器来提供。逻辑判断呢?可以放在客户端,也可以从客户端剥离,爱放哪放哪。容器只依赖于配置,逻辑判断不会影响容器。所以容器很好的将生产过程与客户端隔离,这就不存在耦合了。

          接下来再说一下,一直不停的重复的判断逻辑。在菜鸟D 的看法里,上述几个模式的耦合都是存在于判断逻辑中的。在简单工厂中,工厂和客户端之间耦合较低,但是工厂和具体的产品类是直接耦合的。在工厂方法模式、抽象工厂模式、spring中,逻辑判断只是一个辅助,逻辑判断将生产过程和客户端隔离,大大地降低了耦合程度。所以在衡量设计模式的耦合时,就需要衡量判断逻辑在模式中作用和角色。有些耦合是很难避免的,为了避免这些耦合甚至可能会造成更多的耦合。至于开闭原则,编码过程尽量去注意,否则为以后的开发带来麻烦就不是我们想要的了。

         一家之言,不值一哂,如有谬误,欢迎指正。

         菜鸟D希望这篇文章对您有所帮助。

    扩展参考:

    大话设计模式P72:

          工厂方法实现时,客户端需要决定实例化哪一个工厂来实现运算类(具体产品),选择判断的问题还是存在的,也就是说,工厂方法吧简单工厂的内部逻辑判断移到了客户端代码来进行.想要添加功能,本来是要改工厂类的,而现在是修改客户端.

    三种工厂模式区别总结

    http://blog.csdn.net/lingfengtengfei/article/details/12374469

    spring.net的依赖注入

    http://www.cnblogs.com/GoodHelper/archive/2009/10/26/SpringNET_DI.html

    工厂方法模式

    http://blog.csdn.net/zhengzhb/article/details/7348707

    三种工厂模式

    http://www.cnblogs.com/poissonnotes/archive/2010/12/01/1893871.html

  • 相关阅读:
    Spring:ContextLoaderListener作用
    正确理解UNICODE UTF8等编码方式
    context:propertyplaceholder/元素
    org.springframework.web.context.ContextLoaderListener作用
    javascript下ie7,ie8的Date Bug的解决
    margin负值的几种妙用
    小米note3,华为手机,软键盘弹出之后,页面上定位的元素布局会乱掉
    当padding,margin,top为百分比值,具体数值如何计算
    PHP处理二维数组合并 时间复杂度O(n)
    redis常用操作整理
  • 原文地址:https://www.cnblogs.com/cnDqf/p/4122771.html
Copyright © 2020-2023  润新知