委托:类a将功能委托类b实现 翻译:在类a中包含一个功能类b指针或对象,用到b功能类的功能时通过其指针或者对象调用b的功能,在调用模块看来,问题是交给类a解决了,而实际上,类a通过其成员类b对象或指针解决的该问题,此过程为“a委托b完成了问题”。 顺便一提:a依赖b指的是a类中包含一个b的基类的指针,就不一定是基类对象了,因为基类可能就是虚基类,产生不了对象。
门面模式:或叫外观模式(是前台吗?来杯咖啡。)
定义:将一个子系统的外部与内部的通信必须通过一个统一的对象进行。
翻译: 子系统:由一个或多个功能类组成,一般是一群功能类组成,对某个或某些业务进行一系列处理后实现了一个服务。
统一的对象(门面):一个类, 该类对外只提供一个接口(只有一个public函数,剩下的数据或函数访问权限都不是public),代表外部可调用的服务; 在这个对外开放的接口函数中,需要对完成对子系统中功能类的调用以达到需求, 当需求改变时,内部依赖或关联的功能类与调用逻辑按照需求扩展,该类提供的服务接口函数保持不变。
类图摘自设计模式之禅:越简单越不好理解呢
作用: 1.使功能类和高层调用模块解耦,减少系统的相互依赖, 2.外界访问到子系统内部便会强耦合,低内聚,维护困难; 3.外界访问门面类的一个服务接口就能弱耦合,高内聚,内部扩展方便, 4.对调用者毫无影响,因为对外接口没变化。
优点: 1.减少依赖,降低耦合,主要是调用模块和子系统功能类之间的依赖和耦合。 2.使用灵活,门面接口不变,子系统自由活动。 3.安全,不在门面上开通的方法外部访问不到。
缺点: 不符合开闭原则,对修改关闭,对扩展开放,在系统投产后发现有需要修复的错误时,只能修改门面的代码,继承覆写都不顶用,所以,设计时要慎之又慎。
使用场景: 1.为一个复杂的模块或子系统提供一个供外界访问的借口。 2.子系统相对独立,对外部而言是黑盒,例如:计算利息,计算健康分数,计算综合分数等。 3.预防低水平人员带来的风险扩散,项目开发中的低水平技术人员容易因个人代码质量影响整体项目,故需规定其在指定的子系统中开发,然后再提供接口进行访问操作。
一个子系统有多个门面的场景: 1.门面大到不能忍:代码超过200行就建议拆分。 2.子系统有不同访问路径:等级不同,权限不同,资源不同等,就可以给门面进行扩展(继承或代理)。
注意点: 门面类不应该参与业务逻辑。 原因:门面类封装了服务并且时直接对外开放调用的,这要求门面类的稳定,稳定就是少修改, 倘若门面类对外开放的接口中有业务逻辑的组成部分,比如先调用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,厨房通过调用这些功能类实现了对业务逻辑“做咖啡”的维护,并维护其他几个饮料提供服务,与功能类共同组成了门面类的子系统,门面类不参与功能以及业务,只是好好负责接活和委托,在调用模块客户看来,这个门面系统是相当稳定且简单的。
|