外观模式
外观模式又叫门面模式,是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。
外观模式的作用主要是封装一套一致对外的接口,具体实现由多个独立模块函数协作完成。如此一来,客户端需要实现一个复杂的功能时,无需关注具体的实现,只需要调用一个接口即可。
意义
外观模式非常适用于解决客户端业务逻辑代码与第三方类代码的耦合问题。
Android源码Hal接口虽然是C语言实现,但是其应用到了外观模式的设计思想。Hal层运用结构体指针封装了一套对外的功能接口,其内部硬件操作由各个驱动实现。
当应用层控制某一个硬件时,只需要调用对应的Hal接口即可,无需关注具体实现。如此一来,驱动与应用通过Hal层便实现了解耦。
实际场景
计划一次旅行时,并规划出此次旅行的路线和所需要携带的装备。
分析
旅行路线取决于出发地与目的地,携带装备取决于旅行的天气与时间。
因此需要通过地图APP挑选出最佳路线,通过天气APP了解旅行期间两地天气温度,从而准备好装备。
旅行规划1
上述方法对于用户很不友好,需要分别查阅地图APP与天气APP,从而得出具体的计划。若存在一个APP能够访问地图APP、和天气APP获取的信息,用户便可通过一个APP实现相同的效果。
旅行规划2
类图
外观模式- CPlan: 提供客户端使用的接口。
- CWeatherManager: 可访问天气的功能类。
- CRouteManager: 可访问地图及交通的功能类。
- CEquipment: 结合天气得出携带装备的功能类。
源码实现
编程环境
- 编译环境: Linux环境
- 语言: C++语言
- 编译命令: make
工程结构
Facade/
├── equipment_manager.cc
├── equipment_manager.h
├── main.cc
├── Makefile
├── plan.cc
├── plan.h
├── route_manager.cc
├── route_manager.h
├── whether_manager.cc
└── whether_manager.h
- plan: 对客户端main.c提供统一接口。
- equipment_manager,route_manager,whether_manager: 具体获取装备、地图和天气的功能类。
- Makefile: 编译工具
- main: 客户端代码
对客户端的接口
// plan.h
class CPlan
{
public:
CPlan() {}
~CPlan() {}
int TravelPlan(int startDay, int endDay, std::string place);
void SetCurPlace(std::string place) { mCurPlace = place; }
std::string GetCurPlace() { return mCurPlace; }
private:
std::string mCurPlace;
};
对外接口的实现
// plan.cc
int CPlan::TravelPlan(int startDay, int endDay, std::string place)
{
CWhetherManager *theWhetherManager = CWhetherManager::GetInstance();
CRouteManager *theRouteManager = CRouteManager::GetInstance();
CEquipmentManager *theEquipmentManager = CEquipmentManager::GetInstance();
if (mCurPlace.empty())
{
PLAN_LOGE("Current place is empty\n");
return -1;
}
PLAN_LOG("--------------- Travel Plan --------------- \n"
"- Place : %s -> %s \n"
"- Time : %s -> %s \n"
"- Wheher: %s(%s) \n"
"- Route : %s \n"
"- Prepare Equipment: %s \n",
GetCurPlace().c_str(), place.c_str(),
std::__cxx11::to_string(startDay).c_str(), std::__cxx11::to_string(endDay).c_str(),
theWhetherManager->GetDuringWeather(startDay, endDay, place).c_str(), place.c_str(),
theRouteManager->GetRoutePlan(mCurPlace, place).c_str(),
theEquipmentManager->GetEquipment(startDay, endDay, place).c_str()
);
return 0;
}
-
在外观接口的实现中,调用了第三方类CWhetherManager、CRouteManager、CEquipmentManager。第三方类的具体业务本例程没有打算实现。
-
具体原理就是在对外接口的实现上,调用一个或多个第三方类满足外观接口的功能。
第三方类代码
route_manager.h
//route_manager.h
class CRouteManager
{
public:
CRouteManager() {}
~CRouteManager() {}
static CRouteManager* GetInstance();
std::string GetRoutePlan(std::string curPlace, std::string dstPlace);
};
route_manager.cc
// route_manager.cc
CRouteManager* CRouteManager::GetInstance()
{
static CRouteManager mObj;
return &mObj;
}
string CRouteManager::GetRoutePlan(std::string curPlace, std::string dstPlace)
{
string routePlan = "xxxx";
return routePlan;
}
- 未避免影响查阅,这里仅route_manager的声明和实现。完整工程在后台输入标题获取。
客户端代码
//main.cc
int main(int argc, char *argv[])
{
CPlan thePlan;
thePlan.SetCurPlace("ShenZhen-BaoAn");
thePlan.TravelPlan(20221001, 20221003, "BeiJing-TianAnMen");
return 0;
}
测试效果
$ ./exe
--------------- Travel Plan ---------------
- Place : ShenZhen-BaoAn -> BeiJing-TianAnMen
- Time : 20221001 -> 20221003
- Wheher: xxxx(BeiJing-TianAnMen)
- Route : xxxx
- Prepare Equipment: xxxx
- 由于第三方类的业务没有实现,这里就没有具体的计划。能够看出代码已经执行的第三方类中即可。
总结
- 外观模式实现起来比较简单,其存在的意义大概有:一是将复杂的调用简易化。二是将业务代码和功能代码解耦,实现层级关系。三是能够使功能代码更容易的复用。
- 本例只是一个简单的demo测试,主要在于理解外观模式的思想。在实际编程中,Android Hal层的存在就是外观模式的一种应用。除此之外,每个调用层级之间的衔接,很多都是外观模式的体现。
- 外观模式一般可以单例模式存在,大部分情况下一个外观对足以满足需求。
- 外观模式看上去比较简单,其实用性是比较大的。在日常的编码中运用此模式,能够使代码逻辑更加清晰,“墙裂”推荐!
网友评论