动机(Motivation)
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”,一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者模式)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
红色框内是稳定的,蓝色框内是变化的。
要点分析:
使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使两者之间的依赖关系达到松耦合;
目标发送通知时,无需制定观察者,通知(可以携带通知信息作为参数)会自动传播。
Observer模式是基于事件UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。
案例1:
FileSpliter1.h
#pragma once
class ProgressBar {
public:
void setText(const int val) {
//...
}
};
class FileSpliter {
private:
string file_path_;
int number_ = 0;
// 具体通知控件
ProgressBar* progress_bar_ = nullptr;
public:
FileSpliter(const string& file_path, const int number, ProgressBar* progress_bar)
: file_path_(file_path)
, number_(number)
, progress_bar_(progress_bar)
{
}
void Split() {
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < number_; ++i){
//...
// 更改
if (nullptr != progress_bar_){
progress_bar_->setText(i + 1);
}
}
}
};
MainForm1.h:
#pragma once
class ProgressBar {
public:
void setText(const int val) {
//...
}
};
class FileSpliter {
private:
string file_path_;
int number_ = 0;
// 具体通知控件
ProgressBar* progress_bar_ = nullptr;
public:
FileSpliter(const string& file_path, const int number, ProgressBar* progress_bar)
: file_path_(file_path)
, number_(number)
, progress_bar_(progress_bar)
{
}
void Split() {
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < number_; ++i){
//...
// 更改
if (nullptr != progress_bar_){
progress_bar_->setText(i + 1);
}
}
}
};
修改版本1:
FileSpliter2.h
#pragma once
class ProgressBar {
public:
void setText(const int val) {
//...
}
};
class FileSpliter {
private:
string file_path_;
int number_ = 0;
// 具体通知控件
ProgressBar* progress_bar_ = nullptr;
public:
FileSpliter(const string& file_path, const int number, ProgressBar* progress_bar)
: file_path_(file_path)
, number_(number)
, progress_bar_(progress_bar)
{
}
void Split() {
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < number_; ++i){
//...
// 更改
if (nullptr != progress_bar_){
progress_bar_->setText(i + 1);
}
}
}
};
MainForm2.h
#pragma once
#include "FileSpliter2.h"
class Form {
public:
virtual ~Form() {}
};
class TextBox {
public:
string getText() {
return "xxx";
}
};
class ProgressBar {
public:
void setText(const int val) {
//...
}
};
class MainForm : public Form, public IProgress {
TextBox* txtFilePath = nullptr;
TextBox* txtFileNumber = nullptr;
// 更改
ProgressBar* progress_bar_ = nullptr;
public:
void Button1_Click() {
string file_path = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
// 更改
FileSpliter file_spliter(file_path, number, this);
file_spliter.Split();
}
virtual void DoProgress(const float val) {
progress_bar_->setText(val);
}
};
修改2:
FIleSpliter3.h
#pragma once
#include <string>
#include <set>
using namespace std;
class IProgress {
public:
virtual void DoProgress(const float val) = 0;
virtual ~IProgress() {}
};
class FileSpliter {
private:
string file_path_;
int number_ = 0;
//具体通知控件
//ProgressBar* progress_bar_ = nullptr;
//抽象通知机制
set<IProgress*> list_progress_;
public:
FileSpliter(const string& file_path, const int number)
: file_path_(file_path)
, number_(number)
{
}
void Split() {
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < number_; ++i) {
//...
OnProgress(i + 1);
}
}
void AddIProgress(IProgress* i_progress) {
list_progress_.insert(i_progress);
}
void RemoveIProgress(IProgress* i_progress) {
list_progress_.erase(i_progress);
}
protected:
void OnProgress(const float val) {
//更新进度条
for (auto iter_progress : list_progress_)
{
iter_progress->DoProgress(val);
}
}
};
class Con
MainForm3.h
#pragma once
#include "FileSpliter3.h"
class Form {
public:
virtual ~Form() {}
};
class TextBox {
public:
string getText() {
return "xxx";
}
};
class ProgressBar {
public:
void setText(const int val) {
//...
}
};
class ProgressBar : public IProgress {
public:
virtual void DoProgress(const float val) {
//...
}
};
class ConsolNotifer : public IProgress {
public:
virtual void DoProgress(const float val) {
//...
}
};
class MainForm : public Form, public IProgress {
TextBox* txtFilePath = nullptr;
TextBox* txtFileNumber = nullptr;
public:
void Button1_Click() {
string file_path = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
// 更改
FileSpliter file_spliter(file_path, number);
ProgressBar* pregress_bar = new ProgressBar();
ConsolNotifer* consol_notifer = new ConsolNotifer();
file_spliter.AddIProgress(pregress_bar);
file_spliter.AddIProgress(consol_notifer);
file_spliter.Split();
}
};