一、界面与逻辑
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_HQCalculatorDec.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;//这里设定括号的优先级为0if(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_HQCalculator.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_HICalculator.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)、基本设计原则:强内聚,弱耦合
如果觉得《第十五课 用户界面与业务逻辑的分离------------------狄泰软件学院》对你有帮助,请点赞、收藏,并留下你的观点哦!