情景假设🤔
现在假设要做一款游戏,而我们要负责的部分呢,就是生产系统、财务系统、人口管理系统(当成是一个女朋友生成游戏吧😏,生成一个女朋友需要扣除现有金钱,人口还需要增加1或者2😏)
代码的坏味道🤮
首先,我们来把这三个系统用普通的写法来实现一下
财务系统
这里就假设一下,初始存款为100k(巨额哈哈哈哈)。
public class FinancialSystem
{
// 拥有的金钱
private int _money = 100000;
// 是否有足够的钱进行消费
public bool Enough(int cost)
{
return _money > cost;
}
// 消费
public bool Consume(int cost)
{
if (Enough(cost))
{
this._money -= cost;
return true;
}
return false;
}
}
人口管理系统
因为生产女朋友的话,说明你还单身嘛,所以初始人口为1(当然了,你也可以不为1的时候去生产咯🤨,只要你应付得来)。
public class PopulationSystem
{
// 最大人口
private int _maxPopulation = 3;
// 现有人口
private int _population = 1;
// 人口是否足够
public bool Enough(int cost)
{
return (_population + cost) <= _maxPopulation;
}
// 增加人口
public bool AddPopulation(int populationCost)
{
if (Enough(populationCost))
{
this._population += populationCost;
return true;
}
return false;
}
}
生产系统
生产系统通过询问(调用)财务系统和人口管理系统,是否有足够的资源(钱和人口)来进行女朋友的生产,如果有的话,就进行生产。
这里假设生产一个女朋友的花费为10k(对于100k来说,只用了十分之一,还是负担得起的,人口花费为1(其实也有可能不为1的,哈哈哈))
public class ManufacturingSystem
{
// 财务系统
private FinancialSystem _financialSystem;
// 人口管理系统
private PopulationSystem _populationSystem;
// 女朋友的价格
private int _priceOfGirlFriend = 10000;
// 女朋友所占的人口数量
private int _populationOfGirlFriend = 1;
// 构造函数
public ManufacturingSystem(FinancialSystem financialSystem, PopulationSystem populationSystem)
{
this._financialSystem = financialSystem;
this._populationSystem = populationSystem;
}
// 生产女朋友
public void ProduceGrilFriend()
{
// 判断是否有足够的钱和人口来生产女朋友
bool enoughMoneyToProduceGirlfriend = _financialSystem.Enough(this._priceOfGirlFriend);
bool enoughPopulationToProduceGrilfriend = _populationSystem.Enough(this._populationOfGirlFriend);
// 资源都足够,就进行女朋友的生产
if (enoughMoneyToProduceGirlfriend && enoughPopulationToProduceGrilfriend)
{
_financialSystem.Consume(this._priceOfGirlFriend);
_populationSystem.AddPopulation(this._populationOfGirlFriend);
}
}
}
在这里如果读者看过或者了解过设计的模式的话,那么就会知道,生产系统这里的代码有坏味道了。为什么?因为生产系统与财务和人口管理系统高度耦合了(在这里,生产系统对其他系统的调用,是通过构造函数来传参,并用变量保存下来,然后再进行调用)。
如果财务系统和人口管理系统进行了升级或者调用对应的API变更了(比如函数名变更了),那么这个生产系统,也需要跟着变更,但实际生产的逻辑可能并没有改变。
有的读者可能会想,那我们使用面向接口编程的思想,将对应的财务系统和人口管理系统都抽象为接口的形式来改造可以吗?Emm,这的确可以降低耦合,但是,我们这里只展示了生产系统对财务和人口管理系统的调用,但实际的游戏或者一个实际的系统,可能会存在多个系统之间的相互调用,如果使用接口,仍然无法解决系统间互相调用的混乱。比如像下图所示的。为了显示调用的混乱,所以,画的也比较混乱。比如系统1需要调用系统4和5,则需要管理系统4和5,如此类推。
这个时候,中介者模式就该上场了。
让代码散发芬芳🥳
为了减少系统之间的相互依赖,我们可以使用一个中介者来负责对不同系统直接的调用。
先上图。
这里看图都感觉要比之前清晰多了。
这里主要变化的是生产系统和多了一个中介者,财务系统和人口管理系统没有变化,所以就不再重复展示了。
中介者
通过中介者,可以对财务系统和人口管理系统进行封装,这样,生产系统就可以不需要知道实现该功能的其他系统,只需要对中介者中暴露出来API进行使用就可以了。
public class Mediator
{
private FinancialSystem _financialSystem;
private PopulationSystem _populationSystem;
public Mediator(FinancialSystem financialSystem, PopulationSystem populationSystem)
{
this._financialSystem = financialSystem;
this._populationSystem = populationSystem;
}
public bool CheckEnoughMoney(int cost){
return _financialSystem.Enough(cost);
}
public bool CheckEnoughPopulation(int populationCost){
return _populationSystem.Enough(populationCost);
}
public bool PayMoney(int cost){
return _financialSystem.Consume(cost);
}
public bool AddPopulation(int population){
return _populationSystem.AddPopulation(population);
}
}
改版后的生产系统
生产系统不再通过调用财务系统和人口管理系统了,而是直接通过中介者暴露出来的API进行调用,完全不需要知道实际到底用到了哪些系统。
public class ManufacturingSystem
{
private Mediator _mediator;
private int _priceOfGirlFriend = 10000;
private int _populationOfGirlFriend = 1;
public ManufacturingSystem(Mediator mediator)
{
this._mediator = mediator;
}
public void ProduceGrilFriend()
{
bool enoughMoneyToProduceGirlfriend = _mediator.CheckEnoughMoney(this._priceOfGirlFriend);
bool enoughPopulationToProduceGrilfriend = _mediator.CheckEnoughPopulation(this._populationOfGirlFriend);
if(enoughPopulationToProduceGrilfriend && enoughMoneyToProduceGirlfriend){
_mediator.PayMoney(_priceOfGirlFriend);
_mediator.AddPopulation(_populationOfGirlFriend);
}
}
}
其实呢,这里还可以将财务系统和人口管理系统抽象成接口,进一步减少依赖,甚至,中介者本身也可以抽象成接口的形式。
如果系统很多怎么办?那就可以通过增加中介者来对不同的系统进行分类。
比如系统1、2、3同属一种类型的系统,系统4、5、6属于另外一种类型的,那就可以用两个中介者,来进行划分,而调用,就在中介者和中介者之间,各个系统,仍然不需要知道其他系统的存在,只需要知道自己归属的那个中介者就可以了。
总结
其实这个中介者模式,跟快递的物流系统很类似。比如你的快递从北京市xxx区运往广州市xxx区,那么肯定是先从北京运到广州某个总的仓库,然后再根据区进行运送,这里广州的某个总的仓库,就可以类比成是中介者,北京市和广州市,就可以类比成是子系统。
中介者模式适用于多子系统互相进行调用和依赖的系统中,在子系统特别多的情况下,也有可能会造成中介者的臃肿(比如一个中介者负责的指责太多),这个时候就需要考虑将不同的系统划分给不同的中介者来解决这个问题。