简单工厂模式——创建型模式

∥☆過路亽.° 提交于 2020-02-29 04:39:35

思路:

当我们需要实现一个计算器的简单程序的时候,我们会怎么写?

class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.Write("请输入数字A:");
                String strNumberA = Console.ReadLine();

                Console.Write("请选择运算符号(+、-、*、/):");
                String strOperate = Console.ReadLine();

                Console.Write("请输入数字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 "*":
                        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;
                }
                Console.WriteLine("结果是:" + strResult);
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("您的输入有错:" + ex.Message);
            }
        }
    }

这样写有什么缺点呢?

其一:面向过程的变成方法较之于面向对象的编程方法而言的缺陷在于,面向过程要求模仿计算机的逻辑解决问题,这样便将程序的主要操作放在了程序的主体部分。一旦代码量较大逻辑较为复杂的时候,对程序进行增删查改的工作量会相应增大。不易于维护

其二:当代码量较大的时候,需要添加新功能或者删减相应功能的情况下,需要对代码动刀子的地方就相应增多。不容易扩展。例如我们需要增加平方立方运算、开方运算、求绝对值等等运算,我们不仅需要修改 

    Console.Write("请选择运算符号(+、-、*、/):");

语句,还需要增加switch分支。如果相应的还有其他逻辑功能,也需要做一定的修改。这样功能扩展的工作量就会变得很大,几乎与代码重写难度相当。

其三:我们现在实现的只是控制台功能。如果我们需要实现控制面板,我们就简单地把代码复制过去就行了吗?想想看,当我们需要做一定修改的时候,重复的代码越多,修改的难度是不是相应的就更大呢。当重复代码多到一定程度的时候,维护就会成为一场灾难。也就是说这样写可复用性差

那么怎么样可以使代码可复用、易扩展、易维护灵活性好呢?

首先我们定义一个运算类,由Operation运算类实现对运算前后操作数的获取,用一个虚方法GetRusult()得到结果。

  class Operation{
    protected:
        double numberA;
        double numberB;
    public:
        double getNumberA(){ return numberA; }
        double getNumberB(){ return numberB; }

        void setNumberA(double number){ numberA = number; }
        void setNumberB(double number){ numberB = number; }

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

将具体操作的+、-、*、/类作为Operation运算类的子类。将加减乘除类写成运算类的子类,这样在修改其中某个运算的代码时,可以不需要提供其他运算的代码,保证了代码的封闭性,为程序的安全性提供了保障。至少不用怕别人篡改代码造成损失了。也能保证有人在改加法的代码时不小心把减法给改成了除法了。
class OperationAdd :public Operation{
    public:
        double GetResult()
        {
            double result = 0;
            result = numberA + numberB;
            return result;
        }
    };

    class OperationSub :public Operation{
    public:
        double GetResult()
        {
            double result = 0;
            result = numberA - numberB;
            return result;
        }
    };

    class OperationMul :public Operation{
    public:
        double GetResult()
        {
            double result = 0;
            result = numberA * numberB;
            return result;
        }
    };

    class OperationDiv :public Operation{
    public:
        double GetResult()
        {
            double result = 0;
            if (numberB != 0)
                result = numberA / numberB;
            return result;
        }
    };

如何让计算机知道我想要执行什么运算呢?我们定义一个OperationFactory运算工厂类,我们让这个类来选择实例化我们需要的对象。我们需要选择的运算是一个变化很大的地方,应当考虑用一个专门的类来负责这个过程,这样如果需要修改相应的过程只需要修改这个类就可以了。
   class OperationFactory{
    public:
        Operation* createOperation(char 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;
        }
    };

客户端代码:这样如果我们的客户端(主函数)就和业务层分离了开来,客户只需要调用我们提供的方法,而不需要去管他是怎么实现的。满足设计模式的开放封闭原则。
    #include<iostream>
    #include<string>
    #include"Operation.h"
    #include"ASMD.h"
    #include"OperationFactory.h"

    using namespace std;

    int main(){
        double strNumberA, strNumberB;
        char operate;

        cout << "请输入第一个操作数A:";
        cin >> strNumberA;
        cout << "请输入运算符:(+、-、*、/)";
        cin >> operate;
        cout << "请输入第二个操作数B:";
        cin >> strNumberB;

        Operation* oper = NULL;
        OperationFactory operFact;

        oper = operFact.createOperation(operate);
        (*oper).setNumberA(strNumberA);
        (*oper).setNumberB(strNumberB);

        cout <<"结果是:"<< (*oper).GetResult() << endl;

        if (oper != NULL)
        {
            delete oper;
            oper = NULL;
        }

        return 0;
    }

经过上面的设计过程,如果我们需要添加新的运算规则,只需要添加相应的运算类,再在运算工厂类中添加相应的switch分支即可,实现了代码的可扩展;如果要修改修改相应的算法,只需修改相应的类,实现了代码的可维护;如果要添加控制面板程序,只需实例化相应的类,实现了代码的可复用

吐槽:

最容易让程序员癫狂的情况就是新功能还没有完善客户就要改需求了。其实当我们心里骂了千万遍客户是傻逼的时候,有没有想过是自己的代码设计模式不合理呢?

经历过一些客户擅改需求的事情之后,才逐渐明白软件设计模式的重要性。其实讲道理的话,客户的要求也并不过分,他们认为这么复杂的功能你们都实现了,增加一两个小功能会有多么难吗?只是他们不明白没有善用设计模式,面对已经完成的程序代码,修改功能几乎需要从头来过,这实在是很让人抓狂。

其实说白了原因就在于之前写的程序可维护、可重用、可扩展性差。如果事先就采用面向对象的设计思想,充分利用面向对象语言的封装、继承、多态把程序的功能模块分离,耦合性降低,就可以使程序更加灵活、易修改、可重用

本菜鸟的疑难杂症:

1.class类型重定义:

头文件不用互相包含,如果互相包含就会出现重定义。

2.override关键字:

虚函数描述符override,如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译。

3.虚函数:

虚函数是神马?你猜啊~你猜我告不告诉你

4.protected和private的区别:

C++中的private protected public区别,我不知道哦,你信不信啊

5.c/c++中结构体引用中箭头->与点.的区别

啊我从来没有用对过呢,所以不要问我了我不知道不知道不知道不知道

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!