• 第十五课、用户界面与业务逻辑的分离------------------狄泰软件学院


    一、界面与逻辑

    1、基本程序框架一般包括

    (1)、用户界面模块(UI)

    A、接受用户输入及呈现数据

    (2)、业务逻辑模块

    A、根据用户需要处理数据

    二、基本设计原则

    1、功能模块之间需要进行解耦

    2、核心思想:强内聚,弱耦合

    (1)、每个模块应该只实现单一的功能

    (2)、模块内部的子模块只为整体的单一功能而存在

    (3)、模块之间通过约定好的接口进行交互

    3、工程开发中的接口

    (1)、广义

    A、接口是一种契约(协议、语法、格式等)

    (2)、狭义

    A、面向过程:接口是一种预定义的函数模型

    B、面向对象:接口是纯虚类(c#和Java直接支持接口)

    4、用户界面和业务逻辑交互

    5、原则

    (1)、模块之间仅通过接口进行关联

    A、必然存在模块会使用接口

    B、必然存在模块实现对应接口

    (2)、模块间的关系是单项依赖的

    A、避免模块间存在循环依赖的情况

    B、循环依赖是糟糕的设计标准之一

    6、计算器应用程序的整体框架

    (1)、定义接口类:实现接收用户输入和返回计算结果

    (2)、界面模块使用接口

    (3)、计算模块实现接口的具体功能

    (4)、定义一个类将界面模块和计算模块结合起来

    #ifndef _QCALCULATORUI_H_
    #define _QCALCULATORUI_H_
    
    #include <QtGui/QApplication>
    #include <QLineEdit>
    #include <QPushButton>
    #include "ICalculator.h"
    
    class QCalculatorUI : public QWidget//继承自Qwid,可知。QCalculatorUI是QObject的间接子类
    {
        Q_OBJECT    //类声明最开始处使用Q_Object关键字
        QLineEdit* m_edit;//组合关系
        QPushButton* m_buttons[20];
    
        ICalculator* m_cal;//使用接口
    
        QCalculatorUI();
        bool construct();
    
    private slots://slots关键字
        void onButtonClicked();//与消息的函数签名一样,消息的clicked()没有参数,所以这里也没有
    
    public:
        static QCalculatorUI* NewInstance();
        void show();
        void setCalculator(ICalculator* cal);
        ICalculator* getCalculator();
    
        ~QCalculatorUI();
    };
    
    #endif // _QCALCULATORUI_H_
    QCalculatorUI.h
    #include "QCalculatorUI.h"
    
    QCalculatorUI::QCalculatorUI()  : QWidget(NULL,Qt::WindowCloseButtonHint )
    {
        m_cal = NULL;
    }
    
    bool QCalculatorUI::construct()
    {
    
        bool ret = true;
        m_edit = new QLineEdit(this);//父组件是this的原因:组合关系,同生死共存亡
        const char* btnText[20] =
        {
            "7", "8", "9", "+", "(",
            "4", "5", "6", "-", ")",
            "1", "2", "3", "*", "<-",
            "0", ".", "=", "/", "C"
        };
    
        if(m_edit != NULL)
        {
            m_edit->resize(240,30);
            m_edit->move(10,10);
            m_edit->setReadOnly(true);//设置文本框为只读,不输入字符串
            m_edit->setAlignment(Qt::AlignRight);//设置向右对齐
    
        }
        else
        {
            ret = false;
        }
    
        for(int i=0; (i<4) && ret; i++)//(i<4) && ret表示QLineEdit没有生成,这里也 没必要运行了
        {
            for(int j=0; (j<5) && ret; j++)
            {
                m_buttons[i*5 + j] = new QPushButton(this);
                if(m_buttons[i*5 + j])
                {
                    m_buttons[i*5 + j] ->resize(40,40);//[i*5 + j]是转换为一维来算
                    m_buttons[i*5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);//横坐标移五个,纵坐标移四个
                    m_buttons[i*5 + j]->setText(btnText[i*5 + j]);
                    connect(m_buttons[i*5 + j], SIGNAL(clicked()), this, SLOT(onButtonClicked()));//将信号映射到当前对象的onButtonclick()
                }
                else
                {
                    ret = false;
                }
    
            }
        }
    
        return ret;
    
    }
    QCalculatorUI* QCalculatorUI::NewInstance()
    {
        QCalculatorUI* ret = new QCalculatorUI();
    
        if(!(ret && ret->construct()))
        {
            delete ret;
            ret = NULL;
        }
    
        return ret;
    }
    void QCalculatorUI::show()
    {
        QWidget::show();
        setFixedSize(width(), height());//要放在show()后,否则是先固定再显示
    }
    
    void QCalculatorUI::onButtonClicked()
    {
        QPushButton* btn = dynamic_cast<QPushButton*>(sender());//返回一个指向发送信号的对象的指针
    
        if(btn != NULL)
        {
            QString clicktext = btn->text();
            if(clicktext == "<-")
            {
                QString text =  m_edit->text();
                if(text.length() > 0)
                {
                    text.remove(text.length() - 1, 1);
                    m_edit->setText(text);
                }
    
            }
            else if(clicktext == "C")
            {
                m_edit->setText("");
            }
            else if(clicktext == "=")
            {
                if(m_cal != NULL)
                {
                    m_cal->expression(m_edit->text());//使用接口
                    m_edit->setText(m_cal->result());
                }
            }
            else
            {
                m_edit->setText(m_edit->text() + clicktext);
            }
    
        }
    }
    
    void QCalculatorUI::setCalculator(ICalculator* cal)
    {
        m_cal = cal;
    }
    ICalculator* QCalculatorUI::getCalculator()
    {
        return m_cal;
    }
    
    QCalculatorUI::~QCalculatorUI()
    {
    
    }
    QCalculatorUI.cpp
    #ifndef QCalculatorDec_H
    #define QCalculatorDec_H
    
    #include<QQueue>
    #include <QString>
    #include <QStack>
    #include "ICalculator.h"
    
    class QCalculatorDec : public ICalculator //继承接口类并实现具体功能
    {
    protected:
        QString m_exp;
        QString m_result;
        //分离算法
        bool isDigitOrDot(QChar c);//数字和.
        bool isSymbol(QChar c);//字符
        bool isSign(QChar c);//符合+-
        bool isOperator(QString s);//参数为Qstring 的原因是后面要将pre作为参数传入
        QQueue<QString> split(const QString& exp);//将分离后的结果保存到队列中
    
        //中缀转后缀
        bool isNumber(QString s);
        bool isLeft(QString s);
        bool isRight(QString s);
        int priority(QString s);
        bool match(QQueue<QString>& exp);
        bool transform(QQueue<QString>& exp, QQueue<QString>& output);
    
        //计算结果
        QString calculator(QString lp, QString op, QString rp);
        QString calculator(QQueue<QString>& exp);
    
    public:
        //计算结果
        bool expression(const QString& exp);//实现接口的功能
        QString result();
    
        QCalculatorDec();
        ~QCalculatorDec();
    };
    
    #endif // QCalculatorDec_H
    QCalculatorDec.h
    #include "QCalculatorDec.h"
    #include <QDebug>
    
    QCalculatorDec::QCalculatorDec()
    {
        m_exp = "";
        m_result = "";
    }
    //1.分离算法
    
    bool QCalculatorDec::isDigitOrDot(QChar c)
    {
        return ( ('0' <= c) && (c <= '9') ) || (c == '.');
    }
    
    bool QCalculatorDec::isSymbol(QChar c)
    {
        return isOperator(c) || (c == '(') || (c == ')');
    }
    
    bool QCalculatorDec::isSign(QChar c)
    {
        return (c == '+') || (c == '-');
    }
    
    bool QCalculatorDec::isOperator(QString s)
    {
        return (s == "+") || (s == "-") || (s == "*") || (s == "/");
    }
    
    QQueue<QString> QCalculatorDec::split(const QString& exp)
    {
        QQueue<QString> ret;
        QString num = "";
        QString pre = "";
        for(int i=0; i<exp.length(); i++)
        {
            if(isDigitOrDot(exp[i]))
            {
                num += exp[i];
                pre = exp[i];
            }
            else if(isSymbol(exp[i]))
            {
                if( ! num.isEmpty())
                {
                    ret.enqueue(num);
                    num.clear();
                }
    
                if(isSign(exp[i]) && ((pre == "") || (pre == "(") || (isOperator(pre))))
                {
                    num += exp[i];
                }
                else
                {
                    ret.enqueue(exp[i]);
                }
                pre = exp[i];
    
            }
        }
    
        if(! num.isEmpty())
        {
            ret.enqueue(num);
        }
        return ret;
    }
    
    
    
    
    
    //2.中缀转后缀
    bool QCalculatorDec::isNumber(QString s)
    {
        bool ret = false;
    
        s.toDouble(&ret);//能转换说明是数字
    
        return ret;
    }
    bool QCalculatorDec::isLeft(QString s)
    {
        return (s == "(");
    }
    bool QCalculatorDec::isRight(QString s)
    {
        return (s == ")");
    }
    int QCalculatorDec::priority(QString s)
    {
        int ret = 0;//这里设定括号的优先级为0
        if(s == "+" || s == "-")
        {
            ret = 1;
        }
        if(s == "*" || s == "/")
        {
            ret = 2;
        }
        return ret;
    }
    bool QCalculatorDec::match(QQueue<QString>& exp)
    {
        bool ret = true;
        QStack<QString> statck;
    
        for(int i=0; i<exp.length(); i++)
        {
            if(isLeft(exp[i]))//1.是左括号就直接入栈
            {
                statck.push(exp[i]);
            }
            else if(isRight(exp[i]))//2.由括号就判断栈顶元素是不是左括号,是就弹出不要
            {
                if(!statck.isEmpty() && isLeft(statck.top()))
                {
                    statck.pop();
                }
                else
                {
                    ret = false;
                    break;//如果有出错也就没必要往下继续判断了
                }
            }
    
        }
    
        return ret && statck.isEmpty();
    }
    bool QCalculatorDec::transform(QQueue<QString>& exp, QQueue<QString>& output)
    {
        bool ret = match(exp);
        QStack<QString> statck;
        output.clear();
    
        while(ret && !exp.isEmpty())
        {
            QString e = exp.dequeue();
            if(isNumber(e))//1.是数字直接输出
            {
                output.enqueue(e);
            }
            else if(isOperator(e))//2.是操作符先判断优先级
            {
                while(!statck.isEmpty() && (priority(e) <= priority(statck.top())))
                {
                    output.enqueue(statck.pop());
                }
    
                statck.push(e);
    
            }
            else if(isLeft(e))//3.是左括号直接入栈
            {
                statck.push(e);
            }
            else if(isRight(e))//4.是右括号就把栈元素输出直至遇到左括号
            {
                if(!statck.isEmpty() && (!isLeft(statck.top())))
                {
                    output.enqueue(statck.pop());
                }
                if(!statck.isEmpty())
                {
                    statck.pop();
                }
    
            }
            else
            {
                ret = false;
            }
        }
    
        while (!statck.isEmpty())//5.将栈里剩余的元素全部输出
        {
            output.enqueue(statck.pop());
        }
        if(!ret)
        {
            output.clear();
        }
    
        return ret;
    }
    
    //计算结果
    QString QCalculatorDec::calculator(QString lp, QString op, QString rp)
    {
        QString ret = "Error";
        if(isNumber(lp) && isNumber(rp))
        {
            double l = lp.toDouble();
            double r = rp.toDouble();
    
            if(op == "+")
            {
                ret.sprintf("%f", l + r);
            }
            else if(op == "-")
            {
                ret.sprintf("%f", l - r);
            }
            else if(op == "*")
            {
                ret.sprintf("%f", l * r);
            }
            else if(op == "/")
            {
                const double p = 0.00000000000001;
                if((-p < r) && (r < p))
                {
                    ret = "Error";
                }
                else
                {
                    ret.sprintf("%f", l / r);
                }
            }
            else
            {
                ret = "Error";
            }
        }
        return ret;
    }
    QString QCalculatorDec::calculator(QQueue<QString>& exp)
    {
        QString ret = "Error";
        QStack<QString> statck;
    
        while(!exp.isEmpty())
        {
            QString e = exp.dequeue();
    
            if( isNumber(e) )//1.若是数字,进栈
            {
                statck.push(e);
            }
            else if( isOperator(e) )//2.是操作符,弹出右操作数,弹出左操作数,将结果入栈
            {
                if( !statck.isEmpty() )
                {
                    QString rp = statck.pop();
                    QString lp = statck.pop();
    
                    QString result = calculator(lp, e, rp);
    
                    if(result != "Error")
                    {
                        statck.push(result);
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else//3.else表达式错误
            {
                break;
            }
        }
        if(!statck.isEmpty() && statck.size() == 1 && isNumber(statck.top()))
        {
            ret = statck.pop();
        }
        return ret;
    }
    bool QCalculatorDec::expression(const QString& exp)
    {
        bool ret = false;
        QQueue<QString> spExp = split(exp);
        QQueue<QString> posExp;
    
        m_exp = exp;
    
        if(transform(spExp, posExp))
        {
            m_result = calculator(posExp);
    
            ret = (m_result != "Error");
        }
        else
        {
            m_result = "Error";
        }
    
        return ret;
    }
    QString QCalculatorDec::result()
    {
        return m_result;
    }
    QCalculatorDec::~QCalculatorDec()
    {
        
    }
    QCalculatorDec.cpp
    #ifndef QCalculator_H
    #define QCalculator_H
    #include "QCalculatorDec.h"
    #include "QCalculatorUI.h"
    
    //实现界面类与计算逻辑类的关联
    class QCalculator
    {
    protected:
        QCalculatorUI* m_ui;//用指针的原因是它使用二阶构造
        QCalculatorDec m_cal;
        QCalculator();
        bool construct();
    
    public:
        static QCalculator* NewInstance();//成员有二阶构造,使用它的类也要二阶构造
        void show();
        ~QCalculator();
    };
    
    #endif // QCalculator_H
    QCalculator.h
    #include "QCalculator.h"
    
    QCalculator::QCalculator()
    {
    
    }
    bool QCalculator::construct()
    {
        m_ui = QCalculatorUI::NewInstance();
    
        if(m_ui != NULL)
        {
            m_ui->setCalculator(&m_cal);//关联界面类和计算类
        }
    
        return (m_ui != NULL);
    }
    QCalculator* QCalculator::NewInstance()
    {
        QCalculator* ret = new QCalculator();
    
        if((ret == NULL) || !ret->construct())
        {
            delete ret;
            ret = NULL;
        }
    
        return ret;
    }
    void QCalculator::show()
    {
        m_ui->show();
    }
    QCalculator::~QCalculator()
    {
        delete m_ui;
    }
    QCalculator.cpp
    #ifndef ICALCULATOR_H
    #define ICALCULATOR_H
    #include <QString>
    
    //定义接口类
    //1.提供一个接收输入的接口
    //2.提供一个输出结果的接口
    //单依赖:接口的具体功能由核心算法类实现,接口由界面文件使用
    class ICalculator
    {
    public:
        virtual bool expression(const QString& exp) = 0;
        virtual QString result() = 0;
    };
    
    #endif // ICALCULATOR_H
    ICalculator.h
    #include <QtGui/QApplication>
    #include "QCalculator.h"
    
    int main(int argc, char *argv[])
    {
    
        QApplication a(argc, argv);
    
        QCalculator* cal = QCalculator::NewInstance();
        int ret =-1;
    
        if(cal != NULL)
        {
    
            cal->show();
            ret = a.exec();
            delete cal;//记得删除父对象
    
        }
    
        return ret;
    
    }
    main.cpp

    二、小结

    (1)、模块直接的交互需要通过接口来完成

    (2)、接口是开发中模块之间的一种契约

    (3)、模块之间不能出现循环依赖

    (4)、基本设计原则:强内聚,弱耦合

  • 相关阅读:
    打破国外垄断,开发中国人自己的编程语言(2):使用监听器实现计算器
    寒假arcpy arcgis python培训通知
    分户图制作工具
    分户图制作工具
    Python3操作AutoCAD
    arcgis更新注记要素类
    arcgis field for cad
    为什么中小学培训这么火,怎么打击也收效甚微?
    arcgis arcmap使用 Python 加载 CAD 数据
    神仙渡劫
  • 原文地址:https://www.cnblogs.com/gui-lin/p/6398435.html
Copyright © 2020-2023  润新知