【设计模式】 模式PK:门面模式VS中介者模式
1、概述
门面模式为复杂的子系统提供一个统一的访问界面,它定义的是一个高层接口,该接口使得子系统更加容易使用,避免外部模块深入到子系统内部而产生与子系统内部细节耦合的问题。中介者模式使用一个中介对象来封装一系列同事对象的交互行为,它使各对象之间不再显式地引用,从而使其耦合松散,建立一个可扩展的应用架构。
2、中介者模式实现工资计算
2.1 类图
大家工作会得到工资,那么工资与哪些因素有关呢?这里假设工资与职位、税收有关,职位提升工资就会增加,同时税收也增加,职位下降了工资也同步降低,当然税收也降低。而如果税收比率增加了呢?工资自然就减少了!这三者之间两两都有关系,很适合中介者模式的场景。
类图中的方法比较简单,我们主要分析的是三者之间的关系,通过类图可以发现三者之间已经没有耦合,原本在需求分析时我们发现三者有直接的交互,采用中介者模式后,三个对象之间已经相互独立了,全部委托中介者完成。我们在类图中还定义了一个抽象同事类,它是一个标志性接口,其子类都是同事类,都可以被中介者接收。
2.2 代码
2.2.1 抽象同事类
// CAbsColleague.h
class CAbsColleague
{
public:
CAbsColleague(CAbsMediator *opMediator);
~CAbsColleague(); protected:
//每个同事类都对中介者非常了解
CAbsMediator *mopMediator;
}; // CAbsColleague.cpp
CAbsColleague::CAbsColleague(CAbsMediator *opMediator) :mopMediator(opMediator){} CAbsColleague::~CAbsColleague(){}
抽象同事类中定义了每个同事类对中介者都非常了解,如此才能把请求委托给中介者完成。三个同事类都具有相同的设计,即定义一个业务接口以及每个对象必须实现的职责,同时既然是同事类就都继承CAbsColleague。抽象同事类只是一个标志性父类,并没有限制子类的业务逻辑,因此每一个同事类并没有违背单一职责原则。
2.2.2 职位
// CIPosition.h
class CIPosition
{
public:
CIPosition();
~CIPosition(); virtual void mvPromote() = ;
virtual void mvDemote() = ;
}; // CIPosition.cpp
CIPosition::CIPosition(){}; CIPosition::~CIPosition(){};
职位会有升有降。
// CPosition.h
class CPosition : public CAbsColleague, public CIPosition
{
public:
CPosition(CAbsMediator *opMediator);
~CPosition(); void mvPromote(); void mvDemote();
}; // CPosition.cpp
CPosition::CPosition(CAbsMediator *opMediator) : CAbsColleague(opMediator)
{
mopMediator->mvSetPosition(this);
} CPosition::~CPosition(){} void CPosition::mvPromote()
{
mopMediator->mvUp(this);
} void CPosition::mvDemote()
{
mopMediator->mvDown(this);
}
每一个职位的升降动作都委托给中介者执行,具体一个职位升降影响到谁这里没有定义,完全由中介者完成,简单而且扩展性非常好。
2.2.3 工资
// CISalary.h
class CISalary
{
public:
CISalary();
~CISalary(); //加薪
virtual void mvIncreaseSalary() = ; //降薪
virtual void mvDecreaseSalary() = ;
};
// CISalary.cpp
CISalary::CISalary(){} CISalary::~CISalary(){}
工资也会有升有降。
// CSalary.h
class CSalary :public CAbsColleague, public CISalary
{
public:
CSalary(CAbsMediator *opMediator);
~CSalary(); void mvIncreaseSalary(); void mvDecreaseSalary();
}; // CSalary.cpp
CSalary::CSalary(CAbsMediator *opMediator) : CAbsColleague(opMediator)
{
mopMediator->mvSetSalary(this);
} CSalary::~CSalary(){} void CSalary::mvIncreaseSalary()
{
mopMediator->mvUp(this);
} void CSalary::mvDecreaseSalary()
{
mopMediator->mvDown(this);
}
2.2.4 税收
// CITax.h
class CITax
{
public:
CITax();
~CITax(); // 税收上升
virtual void mvRaise() = ; // 税收下降
virtual void mvDrop() = ;
}; // CITax.cpp
CITax::CITax(){} CITax::~CITax(){}
税收同样有升有降。
//CTax.h
class CTax : public CAbsColleague, public CITax
{
public:
CTax(CAbsMediator *opMediator);
~CTax(); void mvRaise(); void mvDrop();
}; // CTax.cpp
CTax::CTax(CAbsMediator *opMediator) : CAbsColleague(opMediator)
{
mopMediator->mvSetTax(this);
} CTax::~CTax(){} void CTax::mvRaise()
{
mopMediator->mvUp(this);
} void CTax::mvDrop()
{
mopMediator->mvDown(this);
}
2.2.5 中介类
以上同事类的业务都委托给了中介者,其本类已经没有任何的逻辑了,非常简单,现在的问题是中介者类非常复杂,因为它要处理三者之间的关系。
// CAbsMediator.h
class CAbsMediator
{
public:
CAbsMediator(); ~CAbsMediator(); void mvSetSalary(CISalary *opSalary); void mvSetPosition(CIPosition *opPosition); void mvSetTax(CITax *opTax); //工资增加了
virtual void mvUp(CISalary *opSalary) = ;
//职位提升了
virtual void mvUp(CIPosition *opPosition) = ;
//税收增加了
virtual void mvUp(CITax *opTax) = ;
//工资降低了
virtual void mvDown(CISalary *opSalary) = ;
//职位降低了
virtual void mvDown(CIPosition *opPosition) = ;
//税收降低了
virtual void mvDown(CITax *opTax) = ; protected:
//工资
CISalary *mopSalary;
//职位
CIPosition *mopPosition;
//税收
CITax *mopTax;
}; // CAbsMediator.cpp
CAbsMediator::CAbsMediator(){} CAbsMediator::~CAbsMediator(){} void CAbsMediator::mvSetSalary(CISalary *opSalary)
{
mopSalary = opSalary;
} void CAbsMediator::mvSetPosition(CIPosition *opPosition)
{
mopPosition = opPosition;
} void CAbsMediator::mvSetTax(CITax *opTax)
{
mopTax = opTax;
}
在抽象中介者中我们定义了6个方法,分别处理职位升降、工资升降以及税收升降的业务逻辑,采用多态机制来实现。我们来看具体的实现类。
// CMediator.h
class CMediator : public CAbsMediator
{
public:
CMediator();
~CMediator(); //工资增加了
void mvUp(CISalary *opSalary); //职位提升了
void mvUp(CIPosition *opPosition); //税收增加了
void mvUp(CITax *opTax); //工资降低了
void mvDown(CISalary *opSalary); //职位降低了
void mvDown(CIPosition *opPosition); //税收降低了
void mvDown(CITax *opTax);
}; // CMediator.cpp
CMediator::CMediator(){} CMediator::~CMediator(){} // 工资增加了
void CMediator::mvUp(CISalary *opSalary)
{
cout << "增加税收" << endl;
cout << "增加工资" << endl;
} //职位提升了
void CMediator::mvUp(CIPosition *opPosition)
{
cout << "增加税收." << endl;
cout << "增加工资." << endl;
cout << "提升职位." << endl;
} //税收增加了
void CMediator::mvUp(CITax *opTax)
{
cout << "增加税收." << endl;
cout << "增加工资." << endl;
} //工资降低了
void CMediator::mvDown(CISalary *opSalary)
{
cout << "减少税收. " << endl;
cout << "降低工资." << endl;
} //职位降低了
void CMediator::mvDown(CIPosition *opPosition)
{
cout << "减少税收. " << endl;
cout << "降低工资." << endl;
cout << "降低职位." << endl;
} //税收降低了
void CMediator::mvDown(CITax *opTax)
{
cout << "减少税收. " << endl;
cout << "降低工资." << endl;
}
该类的方法较多,但是还是非常简单的,它的方法分为两大类型:一类是每个业务的独立流程,比如增加工资,仅仅实现单独增加工资的职能,而不关心职位、税收是如何变化的,该类型的方法是private私有类型,只能提供本类内访问,这里为了方便使用打印来替代调用的处理方法;另一类是实现抽象中介者定义的方法,完成具体的每一个逻辑,比如职位上升,同时也引起了工资增加、税收增加。
2.2.6 场景调用
int main()
{
//定义中介者
CAbsMediator *op_mediator = new CMediator;
//定义各个同事类
CIPosition *op_position = new CPosition(op_mediator);
CISalary *op_salary = new CSalary(op_mediator);
CITax *op_tax = new CTax(op_mediator); //职位提升了
cout << "===职位提升===" << endl;
op_position->mvPromote(); return ;
}
2.2.7 执行结果
2.3 小结
我们回过头来分析一下设计,在接收到需求后我们发现职位、工资、税收之间有着紧密的耦合关系,如果不采用中介者模式,则每个对象都要与其他两个对象进行通信,这势必会增加系统的复杂性,同时也使系统处于僵化状态,很难实现拥抱变化的理想。通过增加一个中介者,每个同事类的职位、工资、税收都只与中介者通信,中介者封装了各个同事类之间的逻辑关系,方便系统的扩展和维护。
3、门面模式实现工资计算
3.1 类图
工资计算是一件非常复杂的事情,简单来说,它是对基本工资、月奖金、岗位津贴、绩效、考勤、税收、福利等因素综合运算后的一个数字。即使设计一个HR(人力资源)系统,员工工资计算也是非常复杂的模块,但是对于外界,比如高管层,最希望看到的结果是张三拿了多少钱,李四拿了多少钱,而不是看中间的计算过程,怎么计算那是人事部门的事情。换句话说,对外界的访问者来说,它只要传递进去一个人员名称和月份即可获得工资数,而不用关心其中的计算有多么复杂,这就用得上门面模式了。
门面模式对子系统起封装作用,它可以提供一个统一的对外服务接口。
该类图主要实现了工资计算,通过HRFacade门面可以查询用户的工资以及出勤天数等,而不用关心这个工资或者出勤天数是怎么计算出来的,从而屏蔽了外系统对工资计算模块的内部细节依赖。
3.2 代码
我们先看子系统内部的各个实现。
3.2.1 考勤情况
非常简单, 只用一个方法获得一个员工的出勤天数。
class CAttendance
{
public:
CAttendance(){}
~CAttendance(){} //得到出勤天数
int miGetWorkDays()
{
return rand() % ;
}
};
3.2.2 奖金计算
我们在这里实现了一个示意方法,实际的奖金计算是非常复杂的,与考勤、绩效、基本工资、岗位都有关系,单单一个奖金计算就可以设计出一个门面。
class CBonus
{
public:
CBonus() { mopAttendance = new CAttendance; } //奖金
int miGetBonus()
{
//获得出勤情况
int i_work = mopAttendance->miGetWorkDays();
//奖金计算模型
int i_bonus = i_work * / ; return i_bonus;
}
private:
//考勤情况
CAttendance *mopAttendance;
};
3.2.3 基本工资
我们再来看基本工资,这个基本上是按照职位而定的,比较固定。我们定义了员工的基本工资都为2000元, 没有任何浮动的余地。
class CBasicSalary
{
public:
CBasicSalary(){}
~CBasicSalary(){} //获得一个人的基本工资
int miGetBasicSalary() { return ; }
};
3.2.4 绩效
绩效按照一个非常简单的算法,即基本工资乘以一个随机的百分比。
class CPerformance
{
public:
CPerformance(){ mopBasicSalary = new CBasicSalary; }
~CPerformance(){} //绩效奖励
int miGetPerformance()
{
//随机绩效
int i_perf = rand() % ;
return mopBasicSalary->miGetBasicSalary()*i_perf / ;
} private:
CBasicSalary *mopBasicSalary;
};
3.2.5 税收
class CTax
{
public:
CTax(){}
~CTax(){} //收取多少税金
int miGetTax()
{
//交纳一个随机数量的税金
return rand() % ;
}
};
3.2.6 薪酬
一个计算员工薪酬的所有子元素都已经具备了,剩下的就是编写组合逻辑类,总工资的计算。
这里只是对前面的元素值做了一个加减法计算,这是对实际HR系统的简化处理,如果把这个类暴露给外系统,那么被修改的风险是非常大的,因为它的方法miGetTotalSalary是一个具体的业务逻辑。我们采用门面模式的目的是要求门面是无逻辑的,与业务无关,只是一个子系统的访问入口。门面模式只是一个技术层次上的实现, 全部业务还是在子系统内实现。
class CSalaryProvider
{
public:
CSalaryProvider()
{
mopBasicSalary = new CBasicSalary;
mopBonus = new CBonus;
mopPerf = new CPerformance;
mopTax = new CTax;
} ~CSalaryProvider(){} int miGetTotalSalary()
{
return mopBasicSalary->miGetBasicSalary() + mopBonus->miGetBonus() + mopPerf->miGetPerformance() - mopTax->miGetTax();
}
private:
//基本工资
CBasicSalary *mopBasicSalary;
//奖金
CBonus *mopBonus;
//绩效
CPerformance *mopPerf;
//税收
CTax *mopTax;
};
3.2.7 HR门面
所有的行为都是委托行为,由具体的子系统实现,门面只是提供了一个统一访问的基础而已,不做任何的校验、判断、异常等处理。
class CHRFacade
{
public:
CHRFacade()
{
mopSalary = new CSalaryProvider;
mopAttendance = new CAttendance;
} ~CHRFacade(){} //查询一个人的总收入
int miGetSalary(const string &sName, int iWork)
{
return mopSalary->miGetTotalSalary();
} //查询一个员工一个月工作了多少天
int miGetWorkDays(const string &sName)
{
return mopAttendance->miGetWorkDays();
} private:
//总工资情况
CSalaryProvider *mopSalary;
//考勤情况
CAttendance *mopAttendance;
};
3.2.8 场景调用
int main()
{
//定义门面
CHRFacade *op_facede = new CHRFacade; //查询工资
cout << "===外系统查询总收入===" << endl;
int i_salary = op_facede->miGetSalary("张三", rand()%);
cout << "张三 11月 总收入为: " << i_salary << endl; //再查询出勤天数
cout << "===外系统查询出勤天数===" << endl;
int i_work_day = op_facede->miGetWorkDays("李四");
cout << "李四 本月出勤: " << i_work_day << endl; return ;
}
3.2.9 运行结果
3.3 小结
在该例中,我们使用了门面模式对薪水计算子系统进行封装,避免子系统内部复杂逻辑外泄,确保子系统的业务逻辑的单纯性,即使业务流程需要变更,影响的也是子系统内部功能,比如奖金需要与基本工资挂钩,这样的修改对外系统来说是透明的,只需要子系统内部变更即可。
4、总结
门面模式和中介者模式之间的区别还是比较明显的,门面模式是以封装和隔离为主要任务,而中介者模式则是以调和同事类之间的关系为主,因为要调和,所以具有了部分的业务逻辑控制。两者的主要区别如下:
● 功能区别
门面模式只是增加了一个门面, 它对子系统来说没有增加任何的功能, 子系统若脱离门面模式是完全可以独立运行的。而中介者模式则增加了业务功能,它把各个同事类中的原有耦合关系移植到了中介者,同事类不可能脱离中介者而独立存在,除非是想增加系统的复杂性和降低扩展性。
● 知晓状态不同
对门面模式来说,子系统不知道有门面存在,而对中介者来说,每个同事类都知道中介者存在,因为要依靠中介者调和同事之间的关系,它们对中介者非常了解。
● 封装程度不同
门面模式是一种简单的封装,所有的请求处理都委托给子系统完成,而中介者模式则需要有一个中心,由中心协调同事类完成,并且中心本身也完成部分业务,它属于更进一步的业务功能封装。
【设计模式】 模式PK:门面模式VS中介者模式的更多相关文章
- Java设计模式菜鸟系列(二十二)中介者模式建模与实现
转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/40027109 中介者模式(Mediator):主要用来减少类与类之间的耦合的,由于假设类与 ...
- 【设计模式】 模式PK:观察者模式VS责任链模式
1.概述 为什么要把观察者模式和责任链模式放在一起对比呢?看起来这两个模式没有太多的相似性,真没有吗?回答是有.我们在观察者模式中也提到了触发链(也叫做观察者链)的问题,一个具体的角色既可以是观察者, ...
- 再起航,我的学习笔记之JavaScript设计模式23(中介者模式)
中介者模式 概念介绍 中介者模式(Mediator):通过中介者对象封装一系列对象之间的交互,使对象之间不再相互引用降低他们之间的耦合,有时中介者对象也可以改变对象之间的交互. 创建一个中介 中介者模 ...
- C++设计模式——中介者模式
前言 我们都知道,这个国际政治是一门很深的学问,不玩政治的人是搞不懂的.那么多的国家,国家之间的关系又及其复杂:就好比现在,美国和中国有经济利益关系,美国又和日本有盟友关系,朝鲜又和中国有说不清道不明 ...
- 设计模式之中介者模式(Mediator)摘录
23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程.它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...
- 23种设计模式--中介者模式-Mediator Pattern
一.中介者模式的介绍 中介者模式第一下想到的就是中介,房子中介,婚姻中介啊等等,当然笔者也希望来个婚姻中介给我介绍一个哈哈哈,,回归正题中介者模式分成中介者类和用户类,根据接口编程的方式我们再 ...
- C#设计模式-中介者模式
在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室.QQ群和短信平台,这些都是中介者模式在现实生活中的应用,下面就具体分享下我对中介者模式的理解. 一. 中介者(Mediator)模式 从 ...
- 设计模式学习之中介者模式(Mediator,行为型模式)(18)
转载地址:http://www.cnblogs.com/zhili/p/MediatorPattern.html 一.引言 在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室.QQ群和短信 ...
- C#设计模式(18)——中介者模式(Mediator Pattern)
一.引言 在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室.QQ群和短信平台,这些都是中介者模式在现实生活中的应用,下面就具体分享下我对中介者模式的理解. 二. 中介者模式的介绍 2.1 ...
- javascript设计模式--中介者模式(Mediator)
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
随机推荐
- php+原生ajax实现图片文件上传功能实例
html+js 代码 <!DOCTYPE html> <html> <head> <title>Html5 Ajax 上传文件</title> ...
- 自测之Lesson10:管道
题目:建立双向管道,实现:父进程向子进程传送一个字符串,子进程对该字符串进行处理(小写字母转为大写字母)后再传回父进程. 实现代码: #include <stdio.h> #include ...
- 并查集(Union/Find)模板及详解
概念: 并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的Kruskal 算法和求最近公共祖先等. 操作: 并查集的基本操作有两个 ...
- Debian以及Ubuntu源设置
在使用Debian和Ubuntu时,经常为了软件源烦恼,最近发现了一个网页,可以根据国家来设置源的地址,效果还不错. Debian:http://debgen.simplylinux.ch/ Ubun ...
- requests保持cookies的问题
获取cookie,返回CookieJar对象:url = 'http://www.baidu.com'r = requests.get(url) r.cookies#将CookieJar转为字典: c ...
- ubuntu 安装xdebug
Add XDebug to Ubuntu 14.04 Submitted by Wilbur on Tue, 06/17/2014 - 12:49pm It's pretty easy to add ...
- 【Linux】- CentOS安装Mysql 5.7
CentOS7默认数据库是mariadb,而不是mysql.CentOS7的yum源中默认是没有mysql的.所以不能使用yum install直接安装. 下载mysql的repo源 cd /usr/ ...
- NeoLoad系列- 快速上手教程
1.新建工程 2.点击录制脚本按钮 3.在弹出的开始录制对话框中,填写虚拟用户信息. Record in下拉框,用来填写用户路径,一般有三个容器组成: Init, Actions, and End.当 ...
- Java模块化开发
包配置, 静态资源, 视图解析器, 数据库,
- InstallShield Limited Edition for Visual Studio 国内注册时国家无下拉框解决方法
注册地址:http://learn.flexerasoftware.com/content/IS-EVAL-InstallShield-Limited-Edition-Visual-Studio 火狐 ...