委托:类a将功能委托类b实现
翻译:在类a中包含一个功能类b指针或对象,用到b功能类的功能时通过其指针或者对象调用b的功能,在调用模块看来,问题是交给类a解决了,而实际上,类a通过其成员类b对象或指针解决的该问题,此过程为“a委托b完成了问题”。
顺便一提:a依赖b指的是a类中包含一个b的基类的指针,就不一定是基类对象了,因为基类可能就是虚基类,产生不了对象。
定义:将一个子系统的外部与内部的通信必须通过一个统一的对象进行。
翻译:
子系统:由一个或多个功能类组成,一般是一群功能类组成,对某个或某些业务进行一系列处理后实现了一个服务内容。
统一的对象(门面):一个类,
该类中依赖(包含功能类基类)或关联(包含功能类子类)了子系统中调用到的所有功能类,
该类对外只提供一个接口(只有一个public函数,剩下的数据或函数访问权限都不是public),代表外部可调用的服务;
在这个对外开放的接口函数中,需要对完成对所有子系统中功能类的调用以达到需求,
当需求改变时,内部依赖或关联的功能类与调用逻辑按照需求扩展,该类提供的服务接口函数保持不变,
作用:
使功能类和高层调用模块解耦,减少系统的相互依赖,
外界访问到子系统内部便会强耦合,低内聚,维护困难;
外界访问门面类的一个服务接口就能弱耦合,高内聚,内部扩展方便,
对调用者毫无影响,因为对外接口没变化。
优点:
减少依赖,降低耦合,主要是调用模块和子系统功能类之间的依赖和耦合。
使用灵活,门面接口不变,子系统自由活动。
安全,不在门面上开通的方法外部访问不到。
缺点:
不符合开闭原则,对修改关闭,对扩展开放,在系统投产后发现有需要修复的错误时,只能修改门面的代码,继承覆写都不顶用,所以,设计时要慎之又慎。
使用场景:
为一个复杂的模块或子系统提供一个供外界访问的借口。
子系统相对独立,对外部而言是黑盒,例如:计算利息,计算健康分数,计算综合分数等。
预防低水平人员带来的风险扩散,项目开发中的低水平技术人员容易因个人代码质量影响整体项目,故需规定其在指定的子系统中开发,然后再提供接口进行访问操作。
一个子系统有多个门面的场景:
门面大到不能忍:代码超过200行就建议拆分。
子系统有不同访问路径:等级不同,权限不同,资源不同等,就可以给门面进行扩展(继承或代理)。
注意点:
门面类不应该参与业务逻辑。
原因:门面类封装了服务并且时直接对外开放调用的,这要求门面类的稳定,稳定就是少修改,
倘若门面类对外开放的接口中有业务逻辑的组成部分,比如先调用a功能,然后b功能,然后c功能,业务改变时,门面就要相应的改,业务逻辑依赖于门面类,导致门面类不稳定。
避免门面类参与业务逻辑的方法:
子系统中不仅要有功能类实现功能,还要有业务逻辑类组合功能形成完整业务并提供该业务接口,门面类使用业务逻辑类提供的接口向外界提供服务,通过在子系统中增加业务逻辑类,使门面类与业务逻辑解耦,只负责向外界提供服务接口,并将外界需求委托给业务逻辑类。
代码示例:前台不生产咖啡,只是咖啡的搬运工,并将生产咖啡的任务委托给了厨房
//饮料类 class Drink { public: Drink() { kind = 0; } Drink(int value) { kind = value; } void get() { if (kind < 2) { cout << "一杯凉茶" << "准备好了" << endl; } else if (kind < 4) { cout << "一杯奶" << "准备好了" << endl; } else if (kind < 6) { cout << "一杯咖啡" << "准备好了" << endl; } else { cout << "一杯饮料" << "准备好了" << endl; } } int GetKind() { return kind; } protected: int kind; }; //厨房员工 class WaterPullIn { public: Drink* Handle(Drink* drink) { return (new Drink(drink->GetKind() + 1)); } }; //厨房员工 class SugarPullIn { public: Drink* Handle(Drink* drink) { return (new Drink(drink->GetKind() + 1)); } }; //厨房员工 class MilkPullIn { public: Drink* Handle(Drink* drink) { return (new Drink(drink->GetKind() + 1)); } }; //厨房员工 class CoffeeBeanPullIn { public: Drink* Handle(Drink* drink) { return (new Drink(drink->GetKind() + 1)); } }; //厨房类,包含了所有厨房员工,并提供饮品供应服务 class Kitchen { public: Drink* GetCoffee() { mDrink = new Drink(); mDrink = mWaterPullIn->Handle(mDrink); mDrink = mSugarPullIn->Handle(mDrink); mDrink = mMilkPullIn->Handle(mDrink); mDrink = mCoffeeBeanPullIn->Handle(mDrink); return mDrink; } Drink* GetMilk() { mDrink = new Drink(); mDrink = mWaterPullIn->Handle(mDrink); mDrink = mMilkPullIn->Handle(mDrink); return mDrink; } Drink* GetCooltea() { mDrink = new Drink(); mDrink = mWaterPullIn->Handle(mDrink); return mDrink; } protected: Drink* mDrink; WaterPullIn* mWaterPullIn; SugarPullIn* mSugarPullIn; MilkPullIn* mMilkPullIn; CoffeeBeanPullIn* mCoffeeBeanPullIn; }; //前台类,也就是门面类 class Stage { public: void SetKitchen(Kitchen* kitchen) { mKitchen = kitchen; } Drink* GetCoffee() { return mKitchen->GetCoffee(); } protected: Kitchen* mKitchen; }; void func() { Kitchen* WaiBao = new Kitchen(); Stage* HotelStage = new Stage(); HotelStage->SetKitchen(WaiBao); //在前台点一杯咖啡 Drink* Coffee = HotelStage->GetCoffee(); Coffee->get(); } int main() { func(); return 0; }
运行结果正确,在这次代码中,门面类Stage接收了客户想要咖啡的请求,但并没有去做咖啡,而是将该服务委托给了厨房Kitchen,厨房类中包含了制作饮品的功能类,WaterPullIn,SugarPullIn,milkPullIn,CoffeeBeanPullIn,厨房通过调用这些功能类实现了对业务逻辑“做咖啡”的维护,并维护其他几个饮料提供服务,与功能类共同组成了门面类的子系统,门面类不参与功能以及业务,只是好好负责接活和委托,在调用模块客户看来,这个门面系统是相当稳定且简单的。