还记得我刚毕业的第一家公司是做b2c的,当时要做一个进销存系统,相信做过的人都应该清楚。拿最简单的进销存系统为例,其可分为采购管理、销售管理、和存货管理,三个模块相互依赖,假设现在公司为一家卖电脑的经销商:
1、销售情况
销售部门要反馈销售情况,畅销就多采购,滞销就不采购。
2、库存情况
即使是畅销产品,库存都有100台了,每天才卖出去1台,也不需要采购了!
库房有货,才能销售,空手套白狼是不行的。
库房的有容积限制的,不可有无限大,所以就有了清仓处理,需要停止采购,打拆销售。
3、督促采购
在特殊情况下,比如一个企业客户一次性购买100台,库存只有80台,这时需要催促采购。
实现代码如下:
getSaleStatus(); if($saleStatus>80){ echo "采购电脑".$number."台\n"; $stock->increase($number); }else{ $buyComputer = $number/2; echo "采购电脑".$buyComputer."台\n"; $stock->increase($buyComputer); } } public function refuseBuy(){ echo "不再采购电脑\n"; } } class Stock{ private static $computer_number = 100; public function increase($number){ self::$computer_number += $number; echo "库存数量为:".self::$computer_number."\n"; } public function decrease($number){ self::$computer_number -= $number; echo "库存数量为:".self::$computer_number."\n"; } public function getStockNumber(){ return self::$computer_number; } public function clearStock(){ $purchase = new Purchase(); $sale = new Sale(); echo "库存数量为:".self::$computer_number."\n"; //要求拆价销售 $sale->offSale(); //要求采购人员停止采购 $purchase->refuseBuy(); } } class Sale{ public function sellComputer($number){ $stock = new Stock(); $purchase = new Purchase(); if($stock->getStockNumber()<$number){ $purchase->buyComputer($number); } echo "销售电脑".$number."台\n"; $stock->decrease($number); } public function getSaleStatus(){ $saleStatus = mt_rand(0,100); echo "电脑的销售情况为:".$saleStatus."\n"; return $saleStatus; } public function offSale(){ $stock = new Stock(); echo "拆价销售电脑".$stock->getStockNumber()."台\n"; } } $purchase = new Purchase(); $purchase->buyComputer(100); $sale = new Sale(); $sale->sellComputer(1); $stock = new Stock(); $stock->clearStock(); ?> 运行结果: 电脑的销售情况为:70 采购电脑50台 库存数量为:150 销售电脑1台 库存数量为:149 库存数量为:149 拆价销售电脑149台 不再采购电脑 [Finished in 0.3s]
运行结果是我们所期望的,三个不同类型的参与者完成了各自的活动。但你有没有发现这三个类是彼此关联的?在之前的博客设计模式六原则中提到的迪米特法则认为“每个类只和朋友类交流”,这个朋友类并非越多越好,朋友类越多,耦合性越大,要想修改一个就得修改一片。何况这只是最简单的进销存,如果再加入物流管理,供应商管理,资产管理等等,那程序将会像蜘蛛网般。
现有一个助人为乐的中介者作为三个模块的交流核心,每个模块之间不再相互交流,要交流都通过中介者进行。每个模块只负责自己的业务逻辑,不属于自己的则丢给中介者处理,简化了各模块之间的耦合关系。类图如下:
purchase = new Purchase( $this ); $this->stock = new Stock( $this ); $this->sale = new Sale( $this ); } abstract public function execute(); } class Mediator extends AbstractMediator{ public function execute() { $args = func_get_args(); if ( !is_array( $args ) || empty( $args ) ) return; $method = $args[0]; unset( $args[0] ); call_user_func_array( array( $this, $method ), $args ); } //中介者 Mediator定义多个private方法,目的是处理各个对象之间的依赖关系。 private function buyComputer( $number ) { $saleStatus = $this->sale->getSaleStatus(); if ( $saleStatus<=80 ) { $number = $number/2; } echo "采购电脑".$number."台\n"; $this->stock->increase( $number ); } private function clearStock() { echo "库存数量为:".$this->stock->getStockNumber()."\n"; //要求拆价销售 $this->sale->offSale(); //要求采购人员停止采购 $this->purchase->refuseBuy(); } private function sellComputer( $number ) { if ( $this->stock->getStockNumber()<$number ) { $this->purchase->buyComputer( $number ); } echo "销售电脑".$number."台\n"; $this->stock->decrease( $number ); } private function offSale() { echo "拆价销售电脑".$this->stock->getStockNumber()."台\n"; } } abstract class AbstractColleague { protected $mediator = null; public function __construct( $mediator ) { $this->mediator = $mediator; } } class Purchase extends AbstractColleague{ public function buyComputer( $number ) { $this->mediator->execute( 'buyComputer', $number ); } public function refuseBuy() { echo "不再采购电脑\n"; } } class Stock extends AbstractColleague{ private static $computer_number = 100; public function increase( $number ) { self::$computer_number += $number; echo "库存数量为:".self::$computer_number."\n"; } public function decrease( $number ) { self::$computer_number -= $number; echo "库存数量为:".self::$computer_number."\n"; } public function getStockNumber() { return self::$computer_number; } public function clearStock() { $this->mediator->execute( 'clearStock' ); } } class Sale extends AbstractColleague{ public function sellComputer( $number ) { $this->mediator->execute( 'sellComputer', $number ); } public function getSaleStatus() { $saleStatus = mt_rand( 0, 100 ); echo "电脑的销售情况为:".$saleStatus."\n"; return $saleStatus; } public function offSale() { $this->mediator->execute( 'offSale' ); } } $mediator = new Mediator(); $purchase = new Purchase( $mediator ); $purchase->buyComputer( 100 ); $sale = new Sale( $mediator ); $sale->sellComputer( 1 ); $stock = new Stock( $mediator ); $stock->clearStock(); ?> 运行结果: 电脑的销售情况为:70 采购电脑50台 库存数量为:150 销售电脑1台 库存数量为:149 库存数量为:149 拆价销售电脑149台 不再采购电脑 [Finished in 0.3s]
加入中介者后,设计结构清晰了很多,而且类间的耦合性大大减少,代码质量也有了很大的提升。
中介者模式的定义
用一个中介对象封装一系列的对象交互,中介者使各对象不需要显性地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
中介者模式由以下几部门组成:
1、Mediator 抽象中介者角色
抽象中介者角色定义统一的接口,用于各同事角色之间的通信。
2、 Concrete Mediator 具体中介者角色
具体中介者角色通过协调各同事角色实现协作行为,因此它必须依赖于各个同事角色。
3、Colleague 同事角色
每一个同事角色都知道中介者角色,并且与其他的同事角色通信的时候,一定要通过中介者角色协作。每个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种行为叫做自发行为(Self-Method),与其他的同事类或中介者没有任何的依赖;第二种是必须依赖中介者才能完成的行为,叫做依赖方法(Dep-Method)。
中介者模式的优点
减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合
中介者模式的缺点
中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑也就越复杂。
中介者模式的实际应用(MVC)
相信大家都使用过MVC框架,其中的C(controller)就是一个中介者,叫做前端控制器(Front Controller),它的作用就是把M和V隔离开,协调M和V协同工作,把M运行的结果和V代表的视图融合成一个前端可以展示的页面,减少M和V的依赖关系。
中介者模式是一个非常好的封装模式,也是一个很容易被滥用的模式,一个对象依赖几个对象是再正常不过的事情,但是纯理论家就会要求使用中介者模式来封装这种依赖关系,这是非常危险的!因此在使用时请考虑中介者带来的膨胀问题。