• C#设计模式-2工厂方法模式(Factory Method)


    什么是工厂模式?
    类比生活中的概念,当我们需要打电话的时候,我们需要一部手机,我们通常会选择直接去卖手机的实体店买。
    但在程序设计中,当我们需要调用一个类(PhoneA或PhoneB)的方法的时候,我们往往还要关心他是怎么new出来的(见代码段1)。
    这就好比我们自己还要去工厂里面告诉他们你要怎么生产出一部我需要的手机。而我们只关心他能不能打电话,你怎么做的关我屁事。
    所以这就有了工厂模式。工厂模式其实就是抽象出一个工厂,我需要什么手机,去哪个工厂买就行了。我不必关心他的制作过程,
    你只要能生产我要的功能的手机就行。
    所以我们需要引入简单工厂,让我们不用再关心手机怎么生产的,具体代码的实现见代码段2

    一、引言
      在简单工厂模式中讲到简单工厂模式的缺点,有一点是——简单工厂模式系统难以扩展,一旦添加新产品就不得不修改简单工厂方法,
    这样就会造成简单工厂的实现逻辑过于复杂,然而本专题介绍的工厂方法模式可以解决简单工厂模式中存在的这个问题,
    下面就具体看看工厂模式是如何解决该问题的。

    抽象工厂角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
    具体工厂角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象
    抽象产品角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。
    具体产品角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

    二、工厂方法模式的实现
      工厂方法模式之所以可以解决简单工厂的模式,是因为它的实现把具体产品的创建推迟到子类中,
    此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改
    工厂类逻辑的情况下来添加新产品,这样也就克服了简单工厂模式中缺点。下面看下工厂模式的具体实现代码(这里还是以简单
    工厂模式中点菜的例子来实现):

     /// <summary>
        /// 抽象工厂类
        /// </summary>
        public abstract class Creator
        {
            // 工厂方法
            public abstract Food CreateFoddFactory();
        }
    /// <summary>
        /// 菜抽象类
        /// </summary>
        public abstract class Food
        {
            // 输出点了什么菜
            public abstract void Print();
        }
    /// <summary>
        /// 土豆肉丝这道菜
        /// </summary>
        public class ShreddedPorkWithPotatoes : Food
        {
            public override void Print()
            {
                Console.WriteLine("土豆肉丝好了");
            }
        }
      /// <summary>
        /// 土豆肉丝工厂类
        /// </summary>
        public class ShreddedPorkWithPotatoesFactory : Creator
        {
            /// <summary>
            /// 负责创建土豆肉丝这道菜
            /// </summary>
            /// <returns></returns>
            public override Food CreateFoddFactory()
            {
                return new ShreddedPorkWithPotatoes();
            }
        }
    /// <summary>
    /// 西红柿炒鸡蛋这道菜
    /// </summary>
    public class TomatoScrambledEggs : Food
    {
    public override void Print()
    {
    Console.WriteLine("西红柿炒蛋好了!");
    }
    }
      /// <summary>
        /// 西红柿炒蛋工厂类
        /// </summary>
        public class TomatoScrambledEggsFactory : Creator
        {
            /// <summary>
            /// 负责创建西红柿炒蛋这道菜
            /// </summary>
            /// <returns></returns>
            public override Food CreateFoddFactory()
            {
                return new TomatoScrambledEggs();
            }
        }

    class Program
    {
    static void Main(string[] args)
    {
    // 初始化做菜的两个工厂()
    Creator shreddedPorkWithPotatoesFactory = new ShreddedPorkWithPotatoesFactory();
    Creator tomatoScrambledEggsFactory = new TomatoScrambledEggsFactory();

    // 开始做西红柿炒蛋
    Food tomatoScrambleEggs = tomatoScrambledEggsFactory.CreateFoddFactory();
    tomatoScrambleEggs.Print();

    //开始做土豆肉丝
    Food shreddedPorkWithPotatoes = shreddedPorkWithPotatoesFactory.CreateFoddFactory();
    shreddedPorkWithPotatoes.Print();

    Console.Read();
    }
    }

     个人觉得 print ()打印fode 生产完的信息, 应该作为返回值传回,在main中执行print 也是动用了工厂的功能 main内作为一个消费者 客户端的角度上, 应该是说小二上一盘菜, 然后交给工厂处理, 带返回值变好,这个print在main内实现 个人认为不妥

    程序或者改成

    如工厂类中

     /// <summary>
        /// 西红柿炒蛋工厂类
        /// </summary>
        public class TomatoScrambledEggsFactory : Creator
        {
            /// <summary>
            /// 负责创建西红柿炒蛋这道菜
            /// </summary>
            /// <returns></returns>
            public override Food CreateFoddFactory()
            {
                return new TomatoScrambledEggs().Print();   //打印做好这块 应该放到此处
            }
        }

    于是main里边 可以把print那段去掉了.

    我想尝试把一个模式简单工厂模式的加减乘数用 工厂模式实现,来更形象的对比两者的差距 发现工厂模式之后显得多此一举,所以不能说哪个设计模式多么优越要跟实际项目做相应的选择吧.

     下边连个uml图可以很方便的看出区别

    工厂方法

            工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。在同一等级结构中,支持增加任意产品

            优点:允许系统在不修改具体工厂角色的情况下引进新产品

            缺点:由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量

    以下引入一个简单工厂模式和工厂模式相对比的例子

    简单工厂

    角色:

    工厂(Creator)角色
    简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
    抽象产品(Product)角色
    简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
    具体产品(Concrete Product)角色
    是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

    引入实际情况:

    如果有一个住户管理系统,里面的住户类型是可变的,每一种租户类型的租金计算公式都存在差异

    例如:A类型的住户租金额=天数*单价+绩效*0.005

             B类型的住户租金额=月份*(每月价格+performance*0.001)

    分析:

    1. 商店存在共有的计算方法,这是实体商店的行为,然而他们的行为的方式不一样,所有我们抽象商店类,代码

    namespace SimpleFactory.App.IFactroy  
    {  
          
        public interface Ishop  
        {  
            double Getrent(int days, double dayprice, double performance);  
        }  

    2.在抽象了商店之后,我们要对创建具体产品类,这里就是具体的类型商店,里面实现该商店的行为方法。

    创建A类型的商店

     //A类型的商店的创建  
        public class Ashop:Ishop  
        {  
            /// <summary>  
            /// /// A类型商店租金额,天数*单价+绩效*0.005  
            /// </summary>  
            /// <param name="days">天数</param>  
            /// <param name="dayprice">每天单价</param>  
            /// <param name="performance">日平均绩效</param>  
            /// <returns></returns>  
            public double Getrent(int days, double dayprice, double performance)  
            {  
                Console.WriteLine("A商店的租金算法");  
                return days * dayprice + performance * 0.01;  
            }  
        }  

    创建B类型的商店

    /// <summary>  
        /// B类型的商店的创建  
        /// </summary>  
        public class Bshop:Ishop  
        {  
              
            /// <summary>  
            /// B类型商店的租金=月份*(每月价格+performance*0.001)  
            /// </summary>  
            /// <param name="month">月数</param>  
            /// <param name="monthprice">月单价</param>  
            /// <param name="performance">月平均绩效</param>  
            /// <returns></returns>  
            public double Getrent(int month, double monthprice, double performance)  
            {  
                Console.WriteLine("B商店的租金算法");  
                return month * (monthprice + performance * 0.001);  
            }  
        }  

    3. 在创建号类型商店并实现方法后,思考在什么情况下如何创建那种对象,于是简单工厂模式中最核心的部分:工厂类出来了

      public class factorymethod  
        {  
            public Ishop CreateShow(string show)  
            {  
                switch (show.Trim().ToLower())  
                {  
                    case"ashop":  
                        return new Ashop();  
                    case "bshop":  
                        return new Ashop();  
                    default:  
                        throw new Exception("该商店不存在");  
                }  
            }  
        }  

    4.     然后就根据当前的商店类型进行判断,该类型的商店应该进行哪一种算法:

     class Program  
        {  
            static void Main(string[] args)  
            {  
                Ishop As;  
                factorymethod afm = new factorymethod();  
                As = afm.CreateShow("ashop"); //a 类型的某商店  
                double total = As.Getrent(30, 300, 2000);     //30 天/100元  日平均绩效为2000  
                Console.WriteLine("该A类型商店的租金为:" + total);  
      
                Console.WriteLine("=============");  
      
                Ishop Bs;  
                factorymethod bfm = new factorymethod();  
                Bs = bfm.CreateShow("bshop"); //b 类型的某商店  
                total = Bs.Getrent(3, 3000, 60000);          //3 月/4000元  月平均绩效为60000  
                Console.WriteLine("该B类型商店的租金为:" + total);  
                Console.ReadKey();  
            }  
        }  

    到这里我们实现了客户要求的两种类型商店的算法的需求,但是作为一种好的设计架构,还应该考虑到后面的需求变革,如果客户现在又

    增加了C类型商店和D类型商店,它们的算法要求又不一样,这个时候我们就需要进行C,D类型商店的创建,并继承Ishop接口,实现里面的方法,

    同时还得继续修改工厂类在switc中增加case进行捕捉创建相应的商店对象,一旦出现这样的情况,是不利于程序的扩展性和项目后期的维护性的。

    优点:

    • 简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过它,外界可以从直接创建具体产品对  象的尴尬局面中摆脱出来。
    • 外界与具体类隔离开来,偶合性低。
    • 明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

    缺点:

    • 工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则 
    • 虽然简单工厂模式能够适应一定的变化,但是它所能解决的问题是远远有限的。它所能创建的类只能是事先教考虑到的,如果需要添加新的类,则就需要改变工厂类了。

    出现的上诉情况,应该如何解决,值得思考,将在下一个工厂方法模式中得到很好的解决.

    工厂模式实现

    引用 https://blog.csdn.net/heyangyi_19940703/article/details/51188966

    工厂方法模式简介:

    工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

    抽象工厂角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
    具体工厂角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象
    抽象产品角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。
    具体产品角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

    【C#设计模式-简单工厂模式】中,我们使用简单工厂模式实现了:

    如果有一个住户管理系统,里面的住户类型是可变的,每一种租户类型的租金计算公式都存在差异
    例如:A类型的住户租金额=天数*单价+绩效*0.005;B类型的住户租金额=月份*(每月价格+performance*0.001)
    这里我们虽然实现了客户的需求,但是如果客户后期需要增加了C类型商店和D类型商店,而它们的算法要求又不一样,
    这个时候我们就需要进行C,D类型商店的创建,并继承Ishop接口,实现里面的方法,
    同时还得继续修改工厂类在switc中增加case进行捕捉创建相应的商店对象,一旦出现这样的情况,是不利于程序的扩展性和项目后期的维护性的。
    现在使用工厂方法模式可以很好的解决这一问题。不多说上代码。

    1.分析:商店有共同的行为特征,都要进行店铺租金计算行为,我们抽象了Ishop ,里面有待实现的计算商店租金方法行为。

      public interface Ishop  
        {  
            double Getrent(int days, double dayprice, double performance);  
        }  

    2.我们实现Isho接口里面的方法,创建A,B类型店铺。

     /// <summary>  
        /// 继承商店接口,实现里面的行为方法,即算法  
        /// </summary>  
        public class Ashop:Ishop  
        {  
            /// <summary>  
            /// /// A类型商店租金额,天数*单价+绩效*0.005  
            /// </summary>  
            /// <param name="days">天数</param>  
            /// <param name="dayprice">每天单价</param>  
            /// <param name="performance">日平均绩效</param>  
            /// <returns></returns>  
            public double Getrent(int days, double dayprice, double performance)  
            {  
                Console.WriteLine("A商店的租金算法");  
                return days * dayprice + performance * 0.01;  
            }  
        }  
     /// <summary>  
        /// 继承商店接口,实现里面的行为方法,即算法  
        /// </summary>  
        public class Bshop:Ishop  
        {  
            /// <summary>  
            /// B类型商店的租金=月份*(每月价格+performance*0.001)  
            /// </summary>  
            /// <param name="month">月数</param>  
            /// <param name="monthprice">月单价</param>  
            /// <param name="performance">月平均绩效</param>  
            /// <returns></returns>  
            public double Getrent(int month, double monthprice, double performance)  
            {  
                Console.WriteLine("B商店的租金算法");  
                return month * (monthprice + performance * 0.001);  
            }  
        }  

    3.现在考虑在什么情况下创建商店的实体,对不同的商店进行租金计算,并且方便以后的增加和修改。 

    于是我们创建IFactroy接口,里面有待实现的创建商店对象的方法。

      /// <summary>  
        /// 工厂类,创建商店类型实体  
        /// </summary>  
        public interface IFactory  
        {  
            Ishop CreateShop();  
        }  

    4.现在我们就可以继承自IFactory,实现里面创建对应的商店对象了。

      /// <summary>  
        /// 继承工厂类,创建A类型商店实体  
        /// </summary>  
        public class CreateBshop : IFactory  
        {  
            public Ishop CreateShop()  
            {  
                return new Ashop();  
            }  
        }  
    /// <summary>  
        /// 继承工厂类,创建B类型商店实体  
        /// </summary>  
        public class CreateAshop : IFactory  
        {  
            public Ishop CreateShop()  
            {  
                return new Bshop();  
            }  
        }  

    5.之后根据当前的商店类型进行判断,该类型的商店应该进行哪一种算法:

     class Program  
        {  
            static void Main(string[] args)  
            {  
                string shopname = ConfigurationManager.AppSettings["CreateShopClassName"];  
                //shopname为创建商店类名称,此处=CreateAshop  
                IFactory af = (IFactory)Assembly.Load("ProductEnity").CreateInstance("ProductEnity." + shopname);  
                //第一个ProductEnity是dll的名称,第二个ProductEnity是项目的命名空间。  
                Ishop As = af.CreateShop(); double total = As.Getrent(30, 300, 2000); //30 天/100元 日平均绩效为2000   
                Console.WriteLine("该A类型商店的租金为:" + total);   
                  
                Console.WriteLine("=============");  
                IFactory bf = (IFactory)Assembly.Load("ProductEnity").CreateInstance("ProductEnity." + "CreateBshop");  
                //CreateBshop可以保存为配置或者存在数据库中,  
                //注意该保存字符串应该与项目中创建的类名一样,  
                //否则反射的方式会找不到该项目下的类。  
                Ishop Bs = bf.CreateShop(); total = Bs.Getrent(30, 300, 2000); //30 天/100元 日平均绩效为2000  
                Console.WriteLine("该A类型商店的租金为:" + total);  
            }  
        }  

    这里我们使用反射的方式创建对象,替换了之前的工厂类通过switch创建对象的方式,有利于后面的新类型商店增加和算法修改增加和维护

    以后在项目需求在有变革,我们只需要重新在项目中增加C,D类型商店,继承Ishop实现里面的方法,同时,添加继承IFactroy接口,创建对应的商店对象编译该项目的

    ProductEnity.dll,以后再进行计算该C,D类型的商店算法就可以通过反射的方式进行计算,不需要修改原来的工程类代码。

    以上是搜集互联网资料,总结一下,例子更多一点,更能立体的深刻的理解简单工厂模式和工厂模式的区别

    感觉这些东西的优劣也不绝对,仁者见仁智者见智吧, 就像这个例子, 把switch  选择去掉了,也少了这个功能, 并列的在main中实现了 . switch 本来是在工厂里的,现在移动到了main里边,本人还是觉得具体问题具体分析吧,根据项目需求选择简单工厂和工厂模式.

    设计模式更像是一种设计思维, 在日常设计中拓展自己的思路很有帮助.所以要多整理资料,多融会贯通

  • 相关阅读:
    OSX中zsh新增环境变量
    新的开始 春光明媚
    tmux
    继承
    6
    Object类
    网页收藏
    画王八
    ES6 语法之import export
    ES6 语法 之 destructuring
  • 原文地址:https://www.cnblogs.com/zuochanzi/p/8872949.html
Copyright © 2020-2023  润新知