版权声明:请注明转发出处 https://blog.csdn.net/mafucun1988/article/details/89630990
《重构:改善既有代码的设计》中提到过很多重构方法,关于重新组织数据的方法有8种。本文介绍:
复制“被监控数据” dumplicate Observed Data
- 名称:复制“被监控数据” dumplicate Observed Data
- 概要:有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据
- 动机: 分层良好的系统,应该将处理用户界面和处理业务逻辑的代码分开。好处是
- 使用不同的用户界面表现相同的业务逻辑
- 隔离后,不同的开发者开发不同部分的开发
- 做法:
- 修改展现类(GUI),使其成为领域类的observer。如果没有领域类,就建立一个。如果没有“从展现类到领域类”的关联,就将领域类保存于展现类的一个字段中
- 针对GUI类中的领域数据,使用self encapsulate field, 在事件处理函数中调用设值函数,直接更新GUI组件。
- 编译,测试
- 在领域类中定义数据及其相关访问函数。确保领域类中的设值函数能够触发Observer模式的通报机制。对于被观察的数据,在领域类中使用与展现类所用的相同类型(通常是字符串)来保存。
- 修改展现类中的访问函数,将他们的操作对象改为领域对象(而非GUI组件)
- 修改Observer的Update(),使其从相应的领域对象中将所需数据复制给GUI组件。
- 编译,测试
- 代码演示
修改之前的代码:
////////////////////////.h
class MainWindowTest : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindowTest(QWidget *parent = nullptr);
~MainWindowTest();
private slots:
void slot_LineEditStartEditingFinished();
void slot_LineEditEndEditingFinished();
void slot_LineEditLengthEditingFinished();
private:
Ui::MainWindowTest *ui;
Interval *m_pInterval;
};
////////////////////////.cpp
#include "MainWindowTest.h"
#include "ui_MainWindowTest.h"
MainWindowTest::MainWindowTest(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindowTest)
{
m_pInterval = Interval::getInstance();
ui->setupUi(this);
setStart(m_pInterval->getStart());
setEnd(m_pInterval->getEnd());
setLength(m_pInterval->getLength());
connect(ui->lineEdit_Start, SIGNAL(editingFinished()), this, SLOT(slot_LineEditStartEditingFinished()));
connect(ui->lineEdit_End, SIGNAL(editingFinished()), this, SLOT(slot_LineEditEndEditingFinished()));
connect(ui->lineEdit_Length, SIGNAL(editingFinished()), this, SLOT(slot_LineEditLengthEditingFinished()));
}
MainWindowTest::~MainWindowTest()
{
delete ui;
}
Interval* Interval::m_pInterval = nullptr;
Interval::Interval():m_Start(0), m_End(0),m_Length(0)
{
}
Interval *Interval::getInstance()
{
if (nullptr == m_pInterval)
{
m_pInterval = new Interval();
}
return m_pInterval;
}
void Interval::calculateLength()
{
setLength( m_End - m_Start);
}
void Interval::calculateEnd()
{
setEnd( m_Start + m_Length);
}
void MainWindowTest::Update()
{
m_pInterval->setStart(ui->lineEdit_Start->text().toDouble());
m_pInterval->setEnd(ui->lineEdit_End->text().toDouble());
m_pInterval->setLength(ui->lineEdit_Length->text().toDouble());
}
void MainWindowTest::slot_LineEditStartEditingFinished()
{
Update();
m_pInterval->calculateLength();
setLength(m_pInterval->getLength());
}
//void MainWindowTest::on_lineEdit_Start_editingFinished()
//{
//}
void MainWindowTest::on_lineEdit_Test_editingFinished()
{
ui->lineEdit_Test->setText(ui->lineEdit_Test->text()+"*");
}
void MainWindowTest::slot_LineEditEndEditingFinished()
{
Update();
m_pInterval->calculateLength();
setLength(m_pInterval->getLength());
}
void MainWindowTest::slot_LineEditLengthEditingFinished()
{
Update();
m_pInterval->calculateEnd();
setEnd(m_pInterval->getEnd());
}
double MainWindowTest::getStart() const
{
return ui->lineEdit_Start->text().toDouble();
}
void MainWindowTest::setStart(double Start)
{
ui->lineEdit_Start->setText(QString("%1").arg(Start));
}
double MainWindowTest::getEnd() const
{
return ui->lineEdit_End->text().toDouble();
}
void MainWindowTest::setEnd(double End)
{
ui->lineEdit_End->setText(QString("%1").arg(End));
}
double MainWindowTest::getLength() const
{
return ui->lineEdit_Length->text().toDouble();
}
void MainWindowTest::setLength(double Length)
{
ui->lineEdit_Length->setText(QString("%1").arg(Length));
}
double Interval::getStart() const
{
return m_Start;
}
void Interval::setStart(double Start)
{
m_Start = Start;
notify();
}
double Interval::getEnd() const
{
return m_End;
}
void Interval::setEnd(double End)
{
m_End = End;
notify();
}
double Interval::getLength() const
{
return m_Length;
}
void Interval::setLength(double Length)
{
m_Length = Length;
notify();
}
void Interval::notify()
{
for (MainWindowTest *pWindow : m_pWindowList)
{
pWindow->setStart(m_Start);
pWindow->setEnd(m_End);
pWindow->setLength(m_Length);
}
}
运行后:
修改之后的代码:
1) 创建领域类interval
2)对start,end,length三个变量使用使用self encapsulate field
3) 在领域类中定义数据及其相关访问函数,将calculateLength()和calculateEnd()移到领域类,当数据变化时触发notify()
4) 修改展现类MainwindowTest中的访问函数,将他们的操作对象改为领域对象Interval
5)修改Observer的Update(),使其从相应的领域对象中将所需数据复制给GUI组件。
/////////////////////////.h
#ifndef MAINWINDOWTEST_H
#define MAINWINDOWTEST_H
#include <QMainWindow>
#include <QList>
namespace Ui {
class MainWindowTest;
}
class MainWindowTest;
class Interval
{
public:
Interval();
static Interval* getInstance();
void calculateLength();
void calculateEnd();
double getStart() const;
void setStart(double Start);
double getEnd() const;
void setEnd(double End);
double getLength() const;
void setLength(double Lenght);
void notify();
private:
double m_Start;
double m_End;
double m_Length;
QList<MainWindowTest *> m_pWindowList;
static Interval* m_pInterval;
};
class MainWindowTest : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindowTest(QWidget *parent = nullptr);
~MainWindowTest();
void Update();
double getStart() const;
void setStart(double Start);
double getEnd() const;
void setEnd(double End);
double getLength() const;
void setLength(double Length);
private slots:
// void on_lineEdit_Start_editingFinished();
void on_lineEdit_Test_editingFinished();
void slot_LineEditStartEditingFinished();
void slot_LineEditEndEditingFinished();
void slot_LineEditLengthEditingFinished();
private:
Ui::MainWindowTest *ui;
Interval *m_pInterval;
};
#endif // MAINWINDOWTEST_H
//////////////////////////.cpp
#include "MainWindowTest.h"
#include "ui_MainWindowTest.h"
MainWindowTest::MainWindowTest(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindowTest)
{
m_pInterval = Interval::getInstance();
ui->setupUi(this);
ui->lineEdit_Start->setText("0");
ui->lineEdit_End->setText("0");
ui->lineEdit_Length->setText("0");
connect(ui->lineEdit_Start, SIGNAL(editingFinished()), this, SLOT(slot_LineEditStartEditingFinished()));
connect(ui->lineEdit_End, SIGNAL(editingFinished()), this, SLOT(slot_LineEditEndEditingFinished()));
connect(ui->lineEdit_Length, SIGNAL(editingFinished()), this, SLOT(slot_LineEditLengthEditingFinished()));
}
MainWindowTest::~MainWindowTest()
{
delete ui;
}
Interval* Interval::m_pInterval = nullptr;
Interval::Interval()
{
}
Interval *Interval::getInstance()
{
if (nullptr == m_pInterval)
{
m_pInterval = new Interval();
}
return m_pInterval;
}
void Interval::calculateLength()
{
setLength( m_End - m_Start);
}
void Interval::calculateEnd()
{
setEnd( m_Start + m_Length);
}
void MainWindowTest::Update()
{
m_pInterval->setStart(ui->lineEdit_Start->text().toDouble());
m_pInterval->setEnd(ui->lineEdit_End->text().toDouble());
m_pInterval->setLength(ui->lineEdit_Length->text().toDouble());
}
void MainWindowTest::slot_LineEditStartEditingFinished()
{
Update();
m_pInterval->calculateLength();
setLength(m_pInterval->getLength());
}
//void MainWindowTest::on_lineEdit_Start_editingFinished()
//{
//}
void MainWindowTest::on_lineEdit_Test_editingFinished()
{
ui->lineEdit_Test->setText(ui->lineEdit_Test->text()+"*");
}
void MainWindowTest::slot_LineEditEndEditingFinished()
{
Update();
m_pInterval->calculateLength();
setLength(m_pInterval->getLength());
}
void MainWindowTest::slot_LineEditLengthEditingFinished()
{
Update();
m_pInterval->calculateEnd();
setEnd(m_pInterval->getEnd());
}
double MainWindowTest::getStart() const
{
return ui->lineEdit_Start->text().toDouble();
}
void MainWindowTest::setStart(double Start)
{
ui->lineEdit_Start->setText(QString("%1").arg(Start));
}
double MainWindowTest::getEnd() const
{
return ui->lineEdit_End->text().toDouble();
}
void MainWindowTest::setEnd(double End)
{
ui->lineEdit_End->setText(QString("%1").arg(End));
}
double MainWindowTest::getLength() const
{
return ui->lineEdit_Length->text().toDouble();
}
void MainWindowTest::setLength(double Length)
{
ui->lineEdit_Length->setText(QString("%1").arg(Length));
}
double Interval::getStart() const
{
return m_Start;
}
void Interval::setStart(double Start)
{
m_Start = Start;
notify();
}
double Interval::getEnd() const
{
return m_End;
}
void Interval::setEnd(double End)
{
m_End = End;
notify();
}
double Interval::getLength() const
{
return m_Length;
}
void Interval::setLength(double Length)
{
m_Length = Length;
notify();
}
void Interval::notify()
{
for (MainWindowTest *pWindow : m_pWindowList)
{
pWindow->setStart(m_Start);
pWindow->setEnd(m_End);
pWindow->setLength(m_Length);
}
}