一、题目
采用观察者设计模式实现文本转换和排序功能
二、实验要求
问题描述
要求接收一些行,每行有若干英文单词,每个单词由若干字符组成;将每个单词的首字母转换为大写;然后对转换操作后的所有行按每行首字母的字典次序排序。实现一交互式文本转换和排序功能,需要支持的命令为:Input、Print、Quit。
Input命令:从指定的文件名中读入文本数据。
Print命令:输出结果。
Quit命令:退出系统。
> Input, Print, Quit: I
InputFileName
> Input, Print, Quit: p
Star Wars
The Empire Strikes Back
设计要求
1)采用观察者设计模式实现:
- 被观察者:
2个行存储模块,第一个行存储模块负责存储所有原先的行,第二个行存储模块负责存储所有大小写转换后的行和排序后的序列。
- 观察者:
1)大小写转换模块:第1个行存储模块模块的观察者;
2)排序模块:第2个行存储模块模块的观察者。
2)
主控模块负责主控流程;
输入模块负责设定路径并且从输入文件中读入并且存储到第一个行存储模块中;
大小写转换模块负责大小写转换并且存储在第二个行存储模块。
输入模块添加一行到第一个行存储模块就触发了一个事件向大小写转换模块发送,大小写转换模块会根据这个事件对这一行就行处理,存储到第二个行存储模块,这就会另一个事件发送到排序模块,
排序模块会把新加进来的行与旧行进行混合然后排序;
最后,输出模块负责输出到文件。
3)编写测试用例,对软件的功能和性能进行测试。
扩展要求
在上述系统中,增加如下要求:
1)对一个包含大量英文单词的文件进行如下处理:剔除该文件中的噪声词后,对文件所有行按其首字母排序。如:
> Input, Delete, Print, Quit: d
the
2)采用什么样的设计方案提高软件的性能。
观察者设计模式
1)意图
定义对象间的一种一对多的依赖关系。当一方的对象改变状态时,所有的依赖者都会得到通知并被自动更新。
观察者模式使得任意数目的观察者不必知道彼此的存在,且主题发生变化时都可以得到主题的通知,而同步改变状态。是一种很松的耦合。具有很好的可重用性。
观察者模式符合松耦合的原则。因为:
1)主题(subject)只需要知道其观察者(Observer)实现了某个接口。
2)可以随时加入观察者。
3)不需要修改主题就可以加入新的类型的观察者
4)修改主题或观察者都不会影响另一方。
5)观察者之间互不相干。
代码:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include <cctype>
#include <memory>
#include <stdexcept>
using namespace std;
/*
* 输入模块
*/
class inputModule {
public:
inputModule()
{
cout << "请输入当前需要读取的文件名:";
cin >> fileName;
in.open(fileName);
if(in) {
cout << "文件读取成功!\n";
string line;
while (getline(in, line)) {
++lineNum;
file.insert({lineNum, line});
}
}
else {
cerr << "没有该文件!\n";
}
}
string write2row(const size_t &lineNo)
{
return file[lineNo];
}
private:
ifstream in;
string fileName;
size_t lineNum = 0;
map<size_t, string> file;
};
/*
* 行模块1(被观察者1)
*/
class rowModule {
public:
rowModule() = default;
void addRow(inputModule &input)
{
cout << "请输入要添加到模块1的行数及对应的行号:";
cin >> num;
while (num--) {
size_t lineNum; //行号
cin >> lineNum;
lineNo.insert(lineNum);
vec.push_back(input.write2row(lineNum));
Notify(input.write2row(lineNum));
}
}
void delRow(const string &line);
void Notify(string row) const; //告知观察者(大小写转换模块)
private:
size_t num = 0;
set<size_t> lineNo;
vector<string> vec;
};
/*
* 行模块2(被观察者2)
*/
class rowModule2 {
// friend class seq;
friend class Print;
friend void function2();
public:
rowModule2() :data(new vector<string>()) {}
void addRow(const string &newLine) //引用,优化
{
data->push_back(newLine);
Notify();
}
void delRow(const string &line);
void Notify() const; //告知观察者(排序模块)
protected:
shared_ptr<vector<string>> data;
};
rowModule2 row2;
/*
* 大小写转换模块
*/
class convert {
public:
void Update(string line)
{
string word, newLine;
int flag = 0;
istringstream is(line);
while (is >> word) {
word[0] = toupper(word[0]);
if (flag++) newLine += " ";
newLine += word;
}
row2.addRow(newLine);
}
private:
};
/*
* 排序模块
*/
class seq {
public:
seq() :spv(new vector<string>()) {}
seq(shared_ptr<vector<string>> ps) :spv(ps) {}
void Update()
{
sort(spv->begin(), spv->end());
}
private:
shared_ptr<vector<string>> spv;
};
void rowModule::Notify(string row) const { convert conv; conv.Update(row); }
void rowModule2::Notify() const { seq iseq(row2.data); iseq.Update(); }
/*
* 输出模块
*
*/
class Print {
public:
Print()
{
string fileName{"output.txt"};
out.open(fileName);
for (auto it = row2.data->begin(); it != row2.data->end(); ++it)
out << *it << endl;
}
private:
ofstream out;
};
void function1()
{
inputModule input;
rowModule row;
row.addRow(input);
}
void function2()
{
Print output;
}
void read()
{
cout << "基于观察者设计模式的文本转换和排序程序\n";
cout << "1. 输入模块\n";
cout << "2. 输出模块\n";
while (1) {
int num = 0;
cout << "请选择功能:";
f1: cin >> num;
if (num > 3 || num < 1) {
cout << "请输入整数1~3中的一个: ";
goto f1;
}
switch (num) {
case 1:
function1();
break;
case 2:
function2();
break;
}
}
}
int main()
{
read();
return 0;
}