设计模式简介
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
什么是 GOF(四人帮,全拼 Gang of Four)?
在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。
四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。
- 对接口编程而不是对实现编程。
- 优先使用对象组合而不是继承。
设计模式的使用
设计模式在软件开发中的两个主要用途。
开发人员的共同平台
设计模式提供了一个标准的术语系统,且具体到特定的情景。例如,单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。
最佳的实践
设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。
设计模式的类型
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一类设计模式:J2EE 设计模式。
序号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式: 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
2 | 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern )装饰器模式(Decorator Pattern) 外观模式(Facade Pattern)享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
3 | 行为型模式 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern) 业务代表模式(Business Delegate Pattern) 组合实体模式(Composite Entity Pattern) 数据访问对象模式(Data Access Object Pattern) 前端控制器模式(Front Controller Pattern) 拦截过滤器模式(Intercepting Filter Pattern) 服务定位器模式(Service Locator Pattern) 传输对象模式(Transfer Object Pattern) |
下面用一个图片来整体描述一下设计模式之间的关系:
设计模式的八大原则
- 开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 - 里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 - 依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。 - 接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。 - 迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 - 合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。 - 单一职责原则(SRP):一个类应该仅有一个引起它变化的原因.变化的方向隐含着类的责任.
- 针对接口变成,而不是针对实现编程
将设计原则提升为设计经验
- 设计习语 Design Idioms: Design Idioms 描述与特定编程语言相关的低层模式惯用法
- 设计模式 Design Patterns: Design Patterns 主要描述的是 类与相互通信的对象之间的组织关系,包括他们的角色职责写作方式等方面
- 架构模式 Architectural Patterns: Architectural Patterns 描述系统中与基本结构 关系密切的高层模式,包括子系统划分职责以及如何组着他们之间的关系的规则
重构获得模式 Rafactoring to Patterns
- 面向对象设计模式 是”好的面向对象设计”,所谓”好的面向对象设计” 指是那些可以满足”应对变化,提高复用”的设计.
- 现代软件设计的特征是”需求的频繁变化”.设计模式的要点是”寻找变化点,然后在变化点处应用设计模式.从而来更好的应对需求的变化,”什么时候,什么地点应用设计模式”比”理解设计模式结构本身”更为重要
- 设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大的无用.没有一步到位的设计模式.敏捷软件开发提倡的”Refactoring to Patterns”是目前普遍公认的最好的使用设计模式方法.
重构关键技法
- 静态->动态
- 早绑定->晚绑定
- 继承->组合
- 编译时依赖->运行时依赖
- 紧耦合->松耦合
“组件协作”模式
- 现代软件专业分工之后的第一个结果是”框架与应用程序的划分,”组件写作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式.
- 典型模式
- Template Method
- Strategy
- Observer/Event
Template Method
- 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
- 何时使用:有一些通用的方法。
- 如何解决:将这些通用算法抽象出来。
- 关键代码:在抽象类实现,其他步骤在子类实现。
- 应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
- 优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
- 缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
- 使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
介绍
- 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
- 主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
- 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
- 如何解决:将这些算法封装成一个一个的类,任意地替换。
- 关键代码:实现同一个接口。
- 应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,一种旅行方式都是一个策略。
- 优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
- 缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
- 使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
- 注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
Observer 观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
介绍
- 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
- 何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
- 如何解决:使用面向对象技术,可以将这种依赖关系弱化。
- 关键代码:在抽象类里有一个 ArrayList 存放观察者们。
- 应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
- 优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
- 缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
- 使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项: 1、许多语言中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
//filename observer.h
#include <iostream>
#include <set>
#include <string>
using namespace std;
/抽象模式定义
class CObservable;
//观察者,纯虚基类
class CObserver
{
public:
CObserver(){};
virtual ~CObserver(){};
//当被观察的目标发生变化时,通知调用该方法
//来自被观察者pObs, 扩展参数为pArg
virtual void Update(CObservable* pObs, void* pArg = NULL) = 0;
};
//被观察者,即Subject
class CObservable
{
public:
CObservable() : m_bChanged(false) {};
virtual ~CObservable() {};
void Attach(CObserver* pObs); //注册观察者
void Detach(CObserver* pObs); //注销观察者
void DetachAll(); //注销所有观察者
void Notify(void* pArg = NULL); //若状态变化,则遍历观察者,逐个通知更新
bool HasChanged(); //测试目标状态是否变化
int GetObserversCount(); //获取观察者数量
protected:
void SetChanged(); //设置状态变化!!!必须继承CObservable才能设置目标状态
void ClearChanged(); //初始化目标为未变化状态
private:
bool m_bChanged; //状态
set<CObserver*> m_setObs; //set保证目标唯一性
};
/抽象模式实现
void CObservable::Attach(CObserver* pObs){
if (!pObs) return;
m_setObs.insert(pObs);
}
void CObservable::Detach(CObserver* pObs){
if (!pObs) return;
m_setObs.erase(pObs);
}
void CObservable::DetachAll(){
m_setObs.clear();
}
void CObservable::SetChanged(){
m_bChanged = true;
}
void CObservable::ClearChanged(){
m_bChanged = false;
}
bool CObservable::HasChanged(){
return m_bChanged;
}
int CObservable::GetObserversCount(){
return m_setObs.size();
}
void CObservable::Notify(void* pArg /* = NULL */){
if (!HasChanged()) return;
cout << "notify observers…" << endl;
ClearChanged();
set<CObserver*>::iterator itr = m_setObs.begin();
for (; itr != m_setObs.end(); itr++){
(*itr)->Update(this, pArg);
}
}
/具体应用类定义和实现
//bloger是发布者,即被观察者(subject)
class CBloger : public CObservable
{
public:
void Publish(const string &strContent){
cout << "bloger publish, content: " << strContent << endl;
SetChanged();
Notify(const_cast<char*>(strContent.c_str()));
}
};
//portal是发布者,即被观察者(subject)
class CPortal : public CObservable
{
public:
void Publish(const string &strContent){
cout << "portal publish, content: " << strContent << endl;
SetChanged();
Notify(const_cast<char*>(strContent.c_str()));
}
};
//RSS阅读器,观察者
class CRSSReader : public CObserver
{
public:
CRSSReader(const string &strName) : m_strName(strName){}
virtual void Update(CObservable* pObs, void* pArg = NULL){
char* pContent = static_cast<char*>(pArg);
//观察多个目标
if (dynamic_cast<CBloger*>(pObs)){
cout << m_strName << " updated from bloger, content: " << pContent << endl;
}else if (dynamic_cast<CPortal*>(pObs)){
cout << m_strName << " updated from portal, content: " << pContent << endl;
}
}
private:
string m_strName;
};
//Mail阅读器,观察者
class CMailReader : public CObserver
{
public:
CMailReader(const string &strName) : m_strName(strName){}
virtual void Update(CObservable* pObs, void* pArg = NULL){
char* pContent = static_cast<char*>(pArg);
if (dynamic_cast<CBloger*>(pObs)){
cout << m_strName << " updated from bloger, content: " << pContent << endl;
}
if (dynamic_cast<CPortal*>(pObs)){
cout << m_strName << " updated from portal, content: " << pContent << endl;
}
}
private:
string m_strName;
};
#include "observer.h"
int main()
{
//目标(被观察者)
CBloger* pBloger = new CBloger();
CPortal* pPortal = new CPortal();
//观察者. 一个观察者可以观察多个目标
CRSSReader* pRssReader = new CRSSReader("rss reader");
CMailReader* pMailReader = new CMailReader("mail reader");
pBloger->Attach(pRssReader); //bloger注册观察者
pBloger->Attach(pMailReader); //bloger注册观察者
pPortal->Attach(pRssReader); //portal注册观察者
pPortal->Attach(pMailReader); //portal注册观察者
//博客发布信息
pBloger->Publish("博客分享设计模式");
cout << endl;
//门户发布信息
pPortal->Publish("门户分享设计模式");
cout << "\nportal detached mail reader" << endl;
pPortal->Detach(pMailReader);
cout << "portal observers count: " << pPortal->GetObserversCount() << endl << endl;
pPortal->Publish("门户分享设计模式");
system("pause");
return 0;
“单一职责” 模式
- 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求变化,子类急剧膨胀,同事充斥着重复代码,这时候的关键是划清责任.
- 典型模式
- Decorator
- Bridge
Decorator
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。
介绍
- 意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。 - 何时使用:在不想增加很多子类的情况下扩展类。
- 如何解决:将具体功能职责划分,同时继承装饰者模式。
- 关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
- 应用实例: 1、孙悟空有 72 变,当他变成”庙宇”后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
- 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
- 缺点:多层装饰比较复杂。
- 使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
- 注意事项:可代替继承。
实现
我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象作为它的实例变量。
RedShapeDecorator 是实现了 ShapeDecorator 的实体类。
DecoratorPatternDemo,我们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。
#include <iostream>
using namespace std;
//1 shape 基类
class shape{
public:
virtual void draw() = 0;
virtual ~shape(){};
};
//2 创建实现接口的实体类
class rectangle:public shape{
public:
virtual void draw(){
cout<<"画一个矩形"<<this<<endl;
}
};
class circle:public shape{
public:
virtual void draw(){
cout<<"画一个圆形"<<this<<endl;
}
};
// 3 创建实现了shape接口的抽象装饰类
//
class ShapeDecorator :public shape{
protected:
shape& decoratedShape;
public:
ShapeDecorator(shape& decoratedShape):decoratedShape(decoratedShape){}
virtual void draw(){
decoratedShape.draw();
}
~ShapeDecorator(){
//delete decoratedShape;
}
};
//4 创建扩展了 ShapeDecorator 类的实体装饰类
class RedShapeDecorator:public ShapeDecorator{
public:
RedShapeDecorator(shape& decoratedShape):ShapeDecorator(decoratedShape){}
virtual void draw(){
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private:
void setRedBorder(shape& decoratedShape){
//...
cout<<"给"<<&decoratedShape<<"加红框"<<endl;
}
};
class operator1ShapeDecorator:public ShapeDecorator{
public:
operator1ShapeDecorator(shape& decoratedShape):ShapeDecorator(decoratedShape){}
virtual void draw(){
decoratedShape.draw();
setoperator1(decoratedShape);
}
private:
void setoperator1(shape& decoratedShape){
//...
cout<<"给"<<&decoratedShape<<"进行操作1"<<endl;
}
};
int main(){
cout<<"装饰器模式"<<endl;
rectangle r1;
circle r2;
RedShapeDecorator s1(r1);
operator1ShapeDecorator op1(s1);
op1.draw();
}
//打印结果
装饰器模式
画一个矩形0x7fff5fbff678
给0x7fff5fbff678加红框
给0x7fff5fbff660进行操作1
上面是自己尝试实现的一个装饰模式, 只是增加了两个操作. 考虑到如果有大量的扩展操作,进行单纯子类化扩展有些死板,而且需要写的子类太多. 我的感受装饰器模式,更灵活.
桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
介绍
- 意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
- 主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
- 何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
- 如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
- 关键代码:抽象类依赖实现类。
- 应用实例: 1、猪八戒从天蓬元帅转世投胎到猪,转世投胎的机制将尘世划分为两个等级,即:灵魂和肉体,前者相当于抽象化,后者相当于实现化。生灵通过功能的委派,调用肉体对象的功能,使得生灵可以动态地选择。 2、墙上的开关,可以看到的开关是抽象的,不用管里面具体怎么实现的。
- 优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
- 缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
- 使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
- 注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。
我找了一个不错的示例:设计模式—–桥接模式(Bridge Pattern)
一个练习
考虑一个文件分割器的设计。MainForm为界面类,收集用户输入的文件路径,和分割数量。FileSplitter为实现文件分割的类型。其中Split( )实现文件分割算法。
1.要求为Split( )支持多种文件分割算法(至少3种),在MainForm中灵活切换多种算法。
2.在Split( )分割过程中,实现对进度条的实时通知,即对progressBar的赋值。
3.使用松耦合面向对象设计方法和思想,无需编写具体算法实现,可使用伪码表示设计。
#include <iostream>
#include <list>
using namespace std;
//为了避免编译错误增加的类
class Form{
public:
virtual ~Form(){};
};
class TextBox{
//...
};
class ProgressBar{
//...other code
public:
void setValue(float value){cout<<"当前进度条"<<value<<endl;}
};
//观察类
class IProgress{
public:
virtual void DoProgress(float value) = 0;
virtual ~IProgress(){}
};
//策略类
class carve_up{
list<IProgress*> m_iprogressList;//抽象通知机制支持多个观察者
// int value;
public:
//carve_up(int value = 0):value(value){}
virtual void algorithm(const string& filePath, const int fileNumber) =0;//在这里考虑一个问题,onProgress(float) 所需要的参数value,只有在算法内部才可以知道value值什么时候变化,变化多少,所以留给了子类在合适的时机调用onProgress(float),也因此决定carve_up 类作为被观察者, 我觉得更好的做法应该是value变化时直接调用代码,子类都不需要做过多考虑,可以利用setter 方法来做,每次给value 赋值时候调用onProgress,最后发现过度设计了,因为setter方法是提供给外部调用的,不必要,而且调用setValue等同于调用onProgress 多此一举了还多维护了一个值 value 代码见注释部分
// virtual void setValue(float value1){
// value = value1;
// onProgress(value);
//
// }
virtual void addIprogree(IProgress* iprogress){
m_iprogressList.push_back(iprogress);
}
virtual void removeIprogree(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
virtual ~carve_up(){}
protected:
virtual void onProgress(float value){
for(auto i : m_iprogressList){
i->DoProgress(value);//更新进度条
}
}
};
class carve_1:public carve_up{
public:
virtual void algorithm(const string& filePath, const int fileNumber){
cout<<"用方法1分割"<<endl;
for(float i= 0;i <=10;++i){
//...
onProgress(i/10.0f);
}
}
};
class carve_2:public carve_up{
public:
virtual void algorithm(const string& filePath, const int fileNumber){
cout<<"用方法2分割"<<endl;
//...
for(float i= 0;i <=10;++i){
//...
onProgress(i/10.0f);
}
//...
}
};
class carve_3:public carve_up{
public:
virtual void algorithm(const string& filePath, const int fileNumber){
cout<<"用方法3分割"<<endl;
//...
for(float i= 0;i <=10;++i){
//...
onProgress(i/10.0f);
}
//...
}
};
class carve_4:public carve_up{
public:
virtual void algorithm(const string& filePath, const int fileNumber){
cout<<"用方法4分割"<<endl;
//...
for(float i= 0;i <=10;++i){
//...
onProgress(i/10.0f);
}
//...
}
};
class FileSplitter
{
string filePath;
int fileNumber;
carve_up* carve;
public:
FileSplitter(const string filePath,const int fileNumber,carve_up* carve):filePath(filePath),fileNumber(fileNumber),carve(carve){
}
void Split()
{
//...some operators...
carve->algorithm(filePath, fileNumber);
}
void addIprogree(IProgress* iprogress){
carve->addIprogree(iprogress);
}
void removeIprogree(IProgress* iprogress){
carve->addIprogree(iprogress);
}
};
//界面类
class MainForm: public Form,public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click( )
{
//...some operators...
string filepath = "/usr/mmf";
int num = 10;
FileSplitter fspl = FileSplitter(filepath,num,new carve_1());
fspl.addIprogree(this);
fspl.Split();
}
~ MainForm(){};
protected:
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
int main(){
MainForm mainForm;
mainForm.Button1_Click();
}