简单工厂模式

该帖子是逐步深入介绍工厂模式的内容

从一个简单的计算器控制台程序开始


1初学者代码毛病

2代码规范

3

 class Program
    {
        static void Main(string[] args)
        {

            try
            {

            
            Console.WriteLine("请输入数字A");
            string strNumberA = Console.ReadLine();
            Console.WriteLine("请选择运算符号 (+,-,*,/)");
            string strOperate = Console.ReadLine();
            Console.WriteLine("请输入数字B");
            string strNumberB = Console.ReadLine();
            string strResult="";
            switch (strOperate )
            {
                case "+":
                    strResult = Convert.ToString(Convert.ToDouble(strNumberA) + Convert.ToDouble(strNumberB));
                        break;
                case "-":
                    strResult = Convert.ToString(Convert.ToDouble(strNumberA) -Convert.ToDouble(strNumberB));
                    break;
                case "/":
                    if (strNumberB != "0")
                    {
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB));
                       
                    }
                    else
                    {
                        strResult = "除数不能为0";
                    }
                    break;


                case "*":
                    strResult = Convert.ToString(Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB));
                    break;







            }
            Console.WriteLine("The result of the operation is" + strResult);
            Console.ReadLine();
            }
            catch(Exception ex)
            {
                Console.WriteLine("You made a mistake:" + ex.Message);
            }

        }
    }

3面向对象———业务封装 

就是让业务逻辑与界面逻辑分开,让他们的耦合度下降,只有分开才咳哟达到容易维护或者扩展

OPeration类

 class Operation
    {
        public static double GetResult(double  numberA,double numberB,string operation)
        {
            double result = 0d;
            switch (operation)
            {
                case "+":
                    result = numberA + numberB;

                   break;

                case "-":
                    result = numberA - numberB;

                    break;
                case "*":
                    result = numberA * numberB;

                    break;
                case "/":
                    result = numberA / numberB;

                    break;
            }
            return result;
        }
    }

客户端代码

  class Program
    {
        static void Main(string[] args)
        {

            try
            {

            
            Console.WriteLine("请输入数字A");
            string strNumberA = Console.ReadLine();
            Console.WriteLine("请选择运算符号 (+,-,*,/)");
            string strOperate = Console.ReadLine();
            Console.WriteLine("请输入数字B");
            string strNumberB = Console.ReadLine();
            string strResult="";
                strResult = Convert.ToString(Operation.GetResult(Convert.ToDouble(strNumberA),
                    Convert.ToDouble(strNumberB),strOperate ));

                #region 

                /*    

                    switch (strOperate )
                {
                    case "+":
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) + Convert.ToDouble(strNumberB));
                            break;
                    case "-":
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) -Convert.ToDouble(strNumberB));
                        break;
                    case "/":
                        if (strNumberB != "0")
                        {
                            strResult = Convert.ToString(Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB));

                        }
                        else
                        {
                            strResult = "除数不能为0";
                        }
                        break;


                    case "*":
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB));
                        break;







                }
                */
                #endregion
                Console.WriteLine("The result of the operation is" + strResult);
            Console.ReadLine();
            }
            catch(Exception ex)
            {
                Console.WriteLine("You made a mistake:" + ex.Message);
            }

        }
    }

4面向对象———继承与多态

我按照你说的方法写出来了一部分,首先是一个运算类,它有两个 Number 属性,主要用于计算器的前后数,然后有一个虚方法 GetResult(), 用于得到结果,然后我把加减乘除都写成了运算类的子类,继承它后,重写了 GetResut()方法,这样如果要修政任何一个算法,就不需要提供其他算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢?”
(作者注:以上代码读者如果感觉阅读吃力,说明您对继承、多态、虚方法、方法重写等概念的理解尚不够,建议先阅读本书的附录一,理解了这些基本概念后再继续往下阅读。)


虚方法

文章转自:https://www.cnblogs.com/wayfarer/archive/2009/11/03/1595555.html
C#的语法脱胎于C++,因而保留了virtual关键字,可以定义一个虚方法(或虚属性)。一个类的成员被定义为virtual,就意味着它在告诉自己的子类:我准备了一笔遗产,你可以全盘接受,也可以完全拒绝或者修改我的遗嘱。显然,虚方法授予子类的权利甚至大于抽象方法。子类面对抽象方法只有重写(override)的权利,而对于虚方法,它还可以选择完全继承。

毫无疑问,虚方法破坏了对象的封装性。如果不加约束的使用,会对调用方造成破坏,至少它有可能破坏子类与父类之间在外在行为上的一致性。因此,当我们在重写虚方法时,务必要遵循Liskov替换原则。我们要保证对于调用方而言,子类对于父类是完全可以替换的。这里所谓的“替换”,是指子类不能破坏调用方对父类行为的期待。准确地说,子类在重写父类的虚方法时,必须遵循调用该方法的前置条件与后置条件。这也是“契约式设计”的思想。最理想的状态是让使用对象甚至无法知道是否存在派生类[1]。即类的继承体系对于调用者而言,必须体现外部接口的一致性,这样才能做到调用者对派生类无知。

如果确实需要重写父类的方法,最好的方式是扩展而不是修改。这实际上也是开放-封闭原则的体现。例如在Decorator模式中,我们重写父类方法的目的,是为了实现对该方法的装饰。Proxy模式的实现同样如此。Michael C. Feathers对此给出的忠告是[2]:
1)尽可能避免重写具体方法。
2)倘若真的重写了某个具体方法,那么看看能否在重写方法中调用被重写的那个方法。

Feathers的忠告是针对Java语言,因为在C#中我们无法重写具体方法,只能利用new关键字在子类中新建一个相同方法签名的具体方法,而这样的方法并不具备多态性。这里涉及到一个有趣的话题,是关于Java和C#的比较。在Java语言中,如果没有添加任何关键字,则方法默认就是虚方法,任何子类都可以重写它。C#则相反,它对虚方法给予了显式的定义。Java语言的缔造者显然是“性本善”论者,他认为所有子类的实现者均抱着善意的态度来对待父类的方法,因而他赋予了子类相当程度的自由,但却可能被别有用心者偷偷打开封装的后门。如果确有非常重要的隐私防止被篡改,则可以利用final关键字来强制保护。C#语言的发明者则持有“性本恶”的论调,他恶意地揣测子类总是会不怀好意,所以提供了一套默认的强权,来保护父类的隐私。如果需要对子类开放,则明确地声明为virtual,这就牢牢地把控制权攥紧在父类的手中。

C#保守的做法使得语言的特质更加安全(当然,Java会更加自由),我们可以使用virtual的自由性,搭配方法的访问限制,搭建一个安全合理的白盒框架。virtual关键字的含意本身就是面向子类的,所以,我们应该尽可能地将其放在protected方法中使用。如果该方法代表的行为确实需要公开给调用者,我们可以定义一个公开的具体方法,在其中调用一个受保护的虚方法。

在Template Method模式中,体现了C#这种划分具体方法和虚方法的好处。Template Method模式要求子类只能部分地替换父类的实现,整个骨架则必须保持固定不变。在父类中,我们将模板方法定义为具体方法,将基本方法定义为抽象方法。模板方法规定了基本方法的调用顺序,如果我们可以在子类中重写模板方法,就可能破坏基本方法的调用顺序,从而对整个策略造成影响。Strategy模式就不存在这个问题,因为它的策略是整体的。Template Method模式在模板方法中规定的骨架,实际上就是为调用者制订的前置条件和后置条件。

有一种说法是不要在虚方法中访问私有字段[3]。这存在一定的合理性。因为一旦我们在父类的虚方法中访问了私有字段,那么在子类重写该虚方法时,由于无法获得父类的私有字段值,就可能会导致该字段值的缺失。但这种说法并不完全准确。一方面,我们认为Liskov替换原则主要是为了约束Is-A关系在行为上的一致性[4],如果该字段对行为不会造成影响,则无大碍。另一方面,这也说明我们在重写虚方法时,最佳实践还是需要在重写的同时,调用父类的虚方法,如Decorator模式的实现方式。

[1] Alan Shalloway, James R. Trott Design Patterns Explained
[2] Michael C. Feathers Working Effectively with Legacy Code
[3] Dino Esposito, Andrea Saltarello Microsoft.NET Architecting Applications for the Enterprise
[4] Robert C. Martin Agile Software Development:Principles,Patterns and Practices


class Operation
    {
        private double _numbeA = 0;
        private double _numbeB = 0;


        public double NumberA
        {
            get { return _numbeA; }
            set { _numbeA = value; }
        }

        public double NumberB
        {
            get { return _numbeB; }
            set { _numbeB = value; }
        }

        public virtual double GetResult()
        {
            double result = 0;
            return result;
        }

    }



    class OperationAdd:Operation//加法类,继承运算类
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA + NumberB;
            return result;
        }
    }


    class OperationSub : Operation
    {
        public override double GetResult()
        {

        double result = 0;
        result = NumberA - NumberB;
        return result;
        }
        
    
    }


    class OperationMul : Operation
    {
        public override double GetResult()
        {

            double result = 0;
            result = NumberA * NumberB;
            return result;
        }


    }


    class OperationDiv : Operation
    {
        public override double GetResult()
        {

            double result = 0;
            result = NumberA / NumberB;
            return result;
        }


    }

5简单工厂模式

用一个单独的类做这个创造实例的过程,这就是工厂,来我看看这个类如何写

简单运算工厂类

public class OperationFoctory
    {
        public static Operation   createOperate(string operate)
        {
            Operation oper = null;
            switch (operate)
            {
                case "+":
                    oper = new OperationAdd();
                    break;


                case "-":
                    oper = new OperationSub();
                    break;


                case "*":
                    oper = new OperationMul();
                    break;


                case "/":
                    oper = new OperationDiv();
                    break;


            }
            return oper;
        }

    }

客户端代码

                Operation oper;
                oper = OperationFoctory.createOperate("+");
                oper.NumberA = 1;
                oper.NumberB = 2;
                double result = oper.GetResult();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值