一、界面与逻辑
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_
#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() { }
#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
#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() { }
#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
#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; }
#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
#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; }
二、小结
(1)、模块直接的交互需要通过接口来完成
(2)、接口是开发中模块之间的一种契约
(3)、模块之间不能出现循环依赖
(4)、基本设计原则:强内聚,弱耦合