zoukankan      html  css  js  c++  java
  • 简单工厂模式

    模拟一个简单的需求

    设计一个简单的计算器,能够完成加减乘除的操作,并且能够设置运算的两个操作数(暂时不考虑计算器的界面)。

    分析

    思路1

    可以创建一个计算器类,里面包含加减乘除的运算,以及设置获取操作数等操作,客户端直接创建一个计算器的对象,调用对应接口去执行运算。如果后续要添加新的运算,每次需要更改计算器类。

    风险:由于新增操作每次要更改原来设计的类,可能会由于不小心改掉了原来的实现计算逻辑,从而导致原来正常运行的程序得不到正确的结果。

    思路2

    为每种运算单独设计一个类,客户端使用的时候可以根据不同的运算创建不同的对象,执行相应的操作。

    由于每种运算符都有类似的操作,只是最终对数据执行的运算不一样,为了避免每个运算类写重复的代码,可以抽象出来一个运算类,包含公共的方法(设置操作数,获取操作数,获取计算结果等),具体运算类应该继承这个基类,在每个子类分别执行自己的运算。

    此时,客户端需要使用运算器的时候,创建对象使用new的时候还是需要知道每个运算类的类名,若这些类分别在不同的头文件声明,使用这些类的时候还需要include一堆的头文件。如果要新增加运算符,上述的地方都要改动,很容易遗漏,非常的繁琐。

    解决方案:由于创建的对象都是类似的对象(他们的父类都是同一个),可以创建一个类,提供一个静态方法,把创建具体类对象的工作都放到这个类中来完成,当客户端需要使用运算器的时候,就只需跟这个创建类打交道,并且使用这些对象的时候直接通过运算类的基类引用或者指针来执行操作就行了。当然,客户端和创建类需要使用一些通用的约定方式来表示要创建哪种类型的运算实例。针对本需求,可以直接使用加减乘数的字符来进行约定(“+”,“-”,“*”,“/”)。通过这个修改之后,就能够降低客户与每种运算类的耦合度。如果后续需要增加新的运算类,也不需要更改原来已经实现完好的各个具体的运算类,也就不会导致原来正常运行的结果由于不小心修改了原有代码导致结果不正确。

    而上述所说的为了创建具体运算对象和增加的类,就是一个工厂类,解决方案就是简单工厂模式。

    总结

    简单工厂模式并不属于23种GOF设计模式之一。

    简单工厂模式违背了面向对象设计中的开放封闭原则(简而言之,就是对扩展开放,对修改封闭)。按照上述的需求,需要增加一种运算,不妨就假设增加指数运算。此时需要增加一个指数运算类(这种是扩展操作,不修改原有实现,只要新增一个类,就是对扩展开放),还需要在工厂类中增加一种对象的创建逻辑(这种是修改操作,面向对象不提倡修改,就是对修改封闭),这样的修改还是会导致风险。

    c++示例代码

    头文件calculator.h,声明了抽象类运算类,以及具体的运算类,还有运算器工厂类

     1 #ifndef __CALCULATOR_H__
     2 #define __CALCULATOR_H__
     3 
     4 //运算抽象类
     5 class MyOperator
     6 {
     7 public:
     8     MyOperator(char op, double A = 0.0, double B = 0.0) :m_op(op), m_numberA(A), m_numberB(B) {}
     9     virtual ~MyOperator() {}
    10 
    11     //获取运算结果
    12     virtual double getResult()=0;
    13     //获取操作数
    14     double getNumA()const { return m_numberA; }
    15     double getNumB()const { return m_numberB; }
    16     //获取运算符
    17     char getOp()const { return m_op; }
    18     //设置操作数
    19     void setNumA(double A) { m_numberA = A; }
    20     void setNumB(double B) { m_numberB = B; }
    21 
    22 private:
    23     //运算符
    24     char m_op;
    25     //运算的两个操作数
    26     double m_numberA;
    27     double m_numberB;
    28 };
    29 
    30 //加法运算类:继承运算抽象类
    31 class OperatorPlus:public MyOperator
    32 {
    33 public:
    34     OperatorPlus(char op,double A = 0.0,double B = 0.0):MyOperator(op,A,B) {}
    35     virtual double getResult();
    36 };
    37 
    38 //减法运算类:继承运算抽象类
    39 class OperatorMinus :public MyOperator
    40 {
    41 public:
    42     OperatorMinus(char op,double A = 0.0, double B = 0.0) :MyOperator(op,A, B) {}
    43     virtual double getResult();
    44 };
    45 
    46 //乘法运算类:继承运算抽象类
    47 class OperatorMultiply :public MyOperator
    48 {
    49 public:
    50     OperatorMultiply(char op,double A = 0.0, double B = 0.0) :MyOperator(op,A, B) {}
    51     virtual double getResult();
    52 };
    53 
    54 //除法运算类:继承运算抽象类
    55 class OperatorDivision :public MyOperator
    56 {
    57 public:
    58     OperatorDivision(char op,double A = 0.0, double B = 0.0) :MyOperator(op,A, B) {}
    59     virtual double getResult();
    60 };
    61 
    62 //运算工厂类,用于生产客户端需要的具体类
    63 class OperatorFactory
    64 {
    65 public:
    66     static MyOperator* getOperator(char op,double A = 0.0,double B = 0.0);
    67 };
    68 #endif

    实现文件calculator.cpp,生面声明的每个类的具体实现

     1 #include <iostream>
     2 #include "calculator.h"
     3 
     4 using namespace std;
     5 
     6 //加法运算
     7 double OperatorPlus::getResult()
     8 {
     9     return (getNumA() + getNumB());
    10 }
    11 
    12 //减法运算
    13 double OperatorMinus::getResult()
    14 {
    15     return (getNumA()-getNumB());
    16 }
    17 
    18 //乘法运算
    19 double OperatorMultiply::getResult()
    20 {
    21     return (getNumA() * getNumB());
    22 }
    23 
    24 //除法运算
    25 double OperatorDivision::getResult()
    26 {
    27     double result = 0.0;
    28     if (0 == getNumB())
    29     {
    30         cout << "B not allow to be 0" << endl;
    31     }
    32     else
    33     {
    34         result = getNumA() / getNumB();
    35     }
    36     return result;
    37 }
    38 
    39 //运算工厂类,用于生产客户端需要的具体运算类
    40 MyOperator* OperatorFactory::getOperator(char opStr,double A,double B)
    41 {
    42     switch (opStr)
    43     {
    44     case '+':
    45         return new OperatorPlus(opStr, A, B);
    46     case '-':
    47         return new OperatorMinus(opStr, A, B);
    48     case '*':
    49         return new OperatorMultiply(opStr, A, B);
    50     case '/':
    51         return new OperatorDivision(opStr, A, B);
    52     default:
    53         cout << "NOT FOUND OPERATOR" << endl;
    54         return nullptr;
    55     }
    56 }

    测试文件testCalculator.cpp,调用运算器类的客户端

     1 #include "calculator.h"
     2 #include <iostream>
     3 
     4 using namespace std;
     5 
     6 int main()
     7 {
     8     //声明一个指针数组,用于保存运算类对象
     9     MyOperator* myOp[4];
    10 
    11     //获取一个加法类的对象
    12     myOp[0] = OperatorFactory::getOperator('+', 1.0, 2.0);
    13     //获取一个减法类的对象
    14     myOp[1] = OperatorFactory::getOperator('-', 1.0, 2.0);
    15     //获取一个乘法类的对象
    16     myOp[2] = OperatorFactory::getOperator('*', 1.0, 2.0);
    17     //获取一个除法类的对象
    18     myOp[3] = OperatorFactory::getOperator('/', 1.0, 2.0);
    19 
    20     //多态的运用,用基类指针指向每个子类的对象,并且获取计算结果getResult()
    21     MyOperator *p = myOp[0];
    22     for (int i = 0; i < 4; p = myOp[++i])
    23     {
    24         cout << p->getNumA() << p->getOp() << p->getNumB() << "=" << p->getResult() << endl;
    25         //用完就清理现场
    26         delete p;
    27         p = nullptr;
    28     }
    29     return 0;
    30 }

    运行结果

  • 相关阅读:
    二叉树之求叶子结点个数
    求二叉树的深度
    二叉树的基本操作
    二叉树之求结点个数
    数组面试
    数组之求子数组的最大乘积
    字符串之子串
    最近遇到的几个纯C编程的陷阱
    Ubuntu 16.04 64位安装YouCompleteMe
    Linux和Windows的遍历目录下所有文件的方法对比
  • 原文地址:https://www.cnblogs.com/huangwenhao/p/11152249.html
Copyright © 2011-2022 走看看