外观模式
C++ 设计模式——外观模式
外观模式(Facade Pattern)是一种结构性设计模式,它为复杂的子系统提供一个简单的接口。通过外观模式,客户端通过外观类与子系统进行交互,外观类将客户端的请求委托给适当的子系统对象,从而实现功能的调用。这样的设计隐藏了子系统的复杂性,使得客户端与子系统之间的耦合度降低。
引入“外观”设计模式的定义(实现意图):提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
主要组成部分
1. 外观类(Facade)
- 核心角色:外观类是外观模式的核心,提供一个统一的接口,简化客户端与子系统之间的交互。
- 封装子系统:它封装了多个子系统类,对外提供高层次的接口,使得客户端无需直接与子系统交互。
- 协调功能:外观类负责协调子系统对象的调用顺序,确保它们能够协同工作。
2. 子系统类(Subsystem)
- 具体实现:子系统类实现系统的具体功能,通常由多个子系统类组成。
- 独立性:每个子系统类独立于外观类,它们不需要了解外观类的存在。
- 相互通信:子系统类之间可以相互通信,也可以被外观类调用。
3. 客户端(Client)
- 简化交互:客户端使用外观类提供的接口与系统交互。
- 隐藏细节:客户端不需要了解子系统的具体实现细节,只需与外观类进行交互。
例一:工作流程示例
以《魔兽世界》这款流行的网络游戏为例,其配置选项丰富多样,包括图形、声音和语音聊天等多个方面。每个方面都有详细的配置项。
1. 外观类(Facade)
conffacade
类是外观类,它提供了 LowConfComputer
和 HighConfComputer
两个方法,分别用于配置低配置和高配置的电脑。它内部调用了多个子系统的实例,简化了客户端的调用。
//扮演外观模式角色的类
class conffacade
{
//--------------单件类实现相关begin----------------
private:
conffacade() {};
conffacade(const conffacade& tmpobj);
conffacade& operator = (const conffacade& tmpobj);
~conffacade() {};
public:
static conffacade& getInstance()
{
static conffacade instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void LowConfComputer() //对于低配置电脑,只开启一些低配置选项
{
graphic& g_gp = graphic::getInstance();
g_gp.display(true); //全屏耗费资源更低
g_gp.effect(false);
g_gp.resolution(2);
g_gp.antialiasing(false);
sound& g_snd = sound::getInstance();
g_snd.bgsound(false);
g_snd.envirsound(false);
g_snd.expsound(false);
g_snd.setvolume(15);
chatvoice& g_cv = chatvoice::getInstance();
g_cv.micvolume(20);
g_cv.micsens(50);
g_cv.chatvolume(60);
}
void HighConfComputer() //对于高配置电脑,能达到最好效果的项全部开启
{
graphic& g_gp = graphic::getInstance();
g_gp.display(false);
g_gp.effect(true);
g_gp.resolution(0);
g_gp.antialiasing(true);
sound& g_snd = sound::getInstance();
g_snd.bgsound(true);
g_snd.envirsound(true);
g_snd.expsound(true);
g_snd.setvolume(50);
chatvoice& g_cv = chatvoice::getInstance();
g_cv.micvolume(100);
g_cv.micsens(100);
g_cv.chatvolume(100);
}
};
2. 子系统类(Subsystem)
每个子系统类(graphic
、sound
、chatvoice
)负责自己的特定功能。它们提供了具体的方法供外观类调用,执行实际的业务逻辑。
//图形相关类
class graphic
{
//--------------单件类实现相关begin----------------
private:
graphic() {};
graphic(const graphic& tmpobj);
graphic& operator = (const graphic& tmpobj);
~graphic() {};
public:
static graphic& getInstance()
{
static graphic instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void display(bool enable) //是否全屏显示(true:是)
{
cout << "图形->是否全屏显示->" << enable << endl;
//其他代码略......
}
void effect(bool enable)//是否开启特效(true:是)
{
cout << "图形->是否开启特效->" << enable << endl;
}
void resolution(int index) //设置窗口分辨率
{
cout << "图形->分辨率设置选项->" << index << endl;
}
void antialiasing(bool enable)//是否开启抗锯齿(true:是)
{
cout << "图形->是否开启抗锯齿->" << enable << endl;
}
//其他接口略......
};
//声音相关类
class sound
{
//--------------单件类实现相关begin----------------
private:
sound() {};
sound(const sound& tmpobj);
sound& operator = (const sound& tmpobj);
~sound() {};
public:
static sound& getInstance()
{
static sound instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void bgsound(bool enable) //是否开启背景声音(true:是)
{
cout << "声音->是否开启背景声音->" << enable << endl;
}
void envirsound(bool enable)//是否开启环境音效(true:是)
{
cout << "声音->是否开启环境音效->" << enable << endl;
}
void expsound(bool enable) //是否开启表情声音(true:是)
{
cout << "声音->是否开启表情声音->" << enable << endl;
}
void setvolume(int level) //音量大小设置(0-100)
{
cout << "声音->音量大小为->" << level << endl;
}
//其他接口略......
};
//语音聊天相关类
class chatvoice
{
//--------------单件类实现相关begin----------------
private:
chatvoice() {};
chatvoice(const chatvoice& tmpobj);
chatvoice& operator = (const chatvoice& tmpobj);
~chatvoice() {};
public:
static chatvoice& getInstance()
{
static chatvoice instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void micvolume(int level) //麦克风音量大小设置(0-100)
{
cout << "语音聊天->麦克风音量大小为->" << level << endl;
}
void micsens(int level)//麦克灵敏度设置(0-100)
{
cout << "语音聊天->麦克风灵敏度为->" << level << endl;
}
void chatvolume(int level) //聊天音量设置(0-100)
{
cout << "语音聊天->聊天音量为->" << level << endl;
}
//其他接口略......
};
3. 客户端(Client)
在 main
函数中,客户端通过调用外观类的 LowConfComputer
和 HighConfComputer
方法来配置电脑,而不需要了解各个子系统的内部实现。这种方式显著降低了复杂性。
int main()
{
conffacade& g_cffde = conffacade::getInstance();
cout << "低配置电脑,调用LowConfComputer接口" << endl;
g_cffde.LowConfComputer();
cout << "------------------" << endl;
cout << "高配置电脑,调用HighConfComputer接口" << endl;
g_cffde.HighConfComputer();
return 0;
}
UML 图
UML 图解析
1. 外观类与子系统类的依赖
- 外观类(
conffacade
)依赖于子系统类:conffacade
类内部需要调用graphic
、sound
和chatvoice
这三个子系统类的方法,以实现不同配置电脑的功能。- 例如,在
LowConfComputer
和HighConfComputer
方法中,外观类通过调用graphic::getInstance()
、sound::getInstance()
和chatvoice::getInstance()
来获取各个子系统的实例,然后调用它们的方法。
2. 子系统类之间的独立性
- 子系统类之间的独立性:
- 每个子系统类(如
graphic
、sound
和chatvoice
)是独立的,客户端和外观类并不直接依赖于它们的具体实现。 - 这使得在未来需要修改或扩展某个子系统时,不会影响到客户端或外观类的其他部分。
- 每个子系统类(如
3. 客户端与外观类的依赖
- 客户端(
main
函数)只依赖于外观类:- 客户端通过外观类
conffacade
的实例来调用LowConfComputer
和HighConfComputer
方法,而不需要直接与graphic
、sound
和chatvoice
等子系统类交互。 - 这种设计减少了客户端与子系统之间的耦合,使得系统更易于维护和扩展。
- 客户端通过外观类
例二:工作流程示例
在家庭影院系统中,多个设备(如屏幕、灯光、音箱、DVD播放器和游戏机)需要协同工作以提供良好的观影和游戏体验。使用外观模式,创建一个 HomeTheaterFacade
类。
1. 设备类
每个设备类都有 On
和 Off
方法,用于打开和关闭设备。简化这些设备的操作
//屏幕
class Screen
{
public:
void On()
{
cout << "屏幕打开了!" << endl;
}
void Off()
{
cout << "屏幕关闭了!" << endl;
}
};
//灯光
class Light
{
public:
void On()
{
cout << "灯光打开了!" << endl;
}
void Off()
{
cout << "灯光关闭了!" << endl;
}
};
//音箱
class Speaker
{
public:
void On()
{
cout << "音箱打开了!" << endl;
}
void Off()
{
cout << "音箱关闭了!" << endl;
}
};
//DVD播放器
class DvdPlayer
{
public:
void On()
{
cout << "DVD播放器打开了!" << endl;
}
void Off()
{
cout << "DVD播放器关闭了!" << endl;
}
};
//游戏机
class PlayerStation
{
public:
void On()
{
cout << "游戏机打开了!" << endl;
}
void Off()
{
cout << "游戏机关闭了!" << endl;
}
};
2. 外观类(HomeTheaterFacade)
HomeTheaterFacade
类提供了两个主要方法:WatchMovie
和 PlayGame
。这两个方法分别用于观看电影和玩游戏,内部调用相关设备的开关方法。
//家庭影院外观模式类
class HomeTheaterFacade
{
public:
void WatchMovie() //看电影
{
//屏幕打开,灯光熄灭,音箱打开,DVD播放器打开,游戏机关闭。
scnobj.On();
lgobj.Off();
spkobj.On();
dpobj.On();
psobj.Off();
}
void PlayGame() //玩游戏
{
//屏幕打开,灯光打开,音箱打开,DVD播放器关闭,游戏机打开。
scnobj.On();
lgobj.On();
spkobj.On();
dpobj.Off();
psobj.On();
}
private:
Screen scnobj;
Light lgobj;
Speaker spkobj;
DvdPlayer dpobj;
PlayerStation psobj;
};
3. 客户端(main 函数)
在 main
函数中,客户端通过 HomeTheaterFacade
实例来调用 WatchMovie
和 PlayGame
方法,简化了多个设备的操作。
int main()
{
HomeTheaterFacade htfacobj;
cout << "开始看电影---------------" << endl;
htfacobj.WatchMovie();
cout << "开始玩游戏---------------" << endl;
htfacobj.PlayGame();
return 0;
}
UML 图
UML 图解析
1. 外观类与子系统类的依赖
HomeTheaterFacade
类:- 该类依赖于多个子系统类(
Screen
、Light
、Speaker
、DvdPlayer
、PlayerStation
)。它通过这些子系统类的实例来控制家庭影院的各个设备。 - 在
WatchMovie
和PlayGame
方法中,HomeTheaterFacade
调用这些子系统的On
和Off
方法来实现设备的开关操作。
- 该类依赖于多个子系统类(
2. 子系统类之间的独立性
- 子系统类(
Screen
、Light
、Speaker
、DvdPlayer
、PlayerStation
):- 每个子系统类独立实现自己的功能,不依赖于其他子系统类。这种设计确保了每个设备的封装性和独立性,使得在未来的扩展中可以单独修改某个设备的实现,而不影响其他设备或外观类。
3. 客户端与外观类的关系
- 客户端(
main
函数):- 客户端通过
HomeTheaterFacade
的实例htfacobj
调用WatchMovie
和PlayGame
方法。客户端只依赖于外观类,而不需要直接与子系统类交互。 - 这种依赖关系使得客户端的代码更为简洁,隐藏了系统的复杂性。
- 客户端通过
优缺点
优点:
- 简化接口:外观模式提供了一个统一的接口,使得子系统更加容易使用。
- 降低耦合度:客户端与子系统之间的直接依赖关系减少,客户端只需与外观类交互。
- 提高灵活性:当子系统发生变更时,只需要调整外观类的实现,而不需要修改客户端代码。
- 易于维护:由于客户端与子系统解耦,系统的维护和扩展变得更加容易。
缺点:
- 违反开闭原则:如果增加新的子系统或者修改现有子系统的行为,可能需要修改外观类,这违反了开闭原则。
- 可能导致设计过重:如果不恰当地使用外观模式,可能会导致系统出现大量细粒度的外观类,增加系统的复杂性。
- 性能问题:在某些情况下,如果外观类需要处理多个子系统的交互,可能会引入额外的性能开销。
适用场景
- 子系统复杂:当子系统非常复杂,客户端直接与子系统交互会非常困难时,可以使用外观模式。
- 简化客户端调用:当需要提供一个简单的接口来隐藏系统的复杂性,让客户端不需要了解系统的内部细节时。
- 构建层次结构:在构建多层结构的系统时,可以使用外观模式定义每一层的入口点,从而简化层与层之间的调用。
- 解耦系统:当需要减少客户端与多个子系统之间的依赖关系,以降低系统耦合度时。
- 逐步引入变化:当需要逐步引入新的子系统到现有系统中,而又不想影响现有客户端代码时,可以使用外观模式作为中间层逐步引入变化。
例一:完整代码
#include <iostream>
using namespace std;
//图形相关类
class graphic
{
//--------------单件类实现相关begin----------------
private:
graphic() {};
graphic(const graphic& tmpobj);
graphic& operator = (const graphic& tmpobj);
~graphic() {};
public:
static graphic& getInstance()
{
static graphic instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void display(bool enable) //是否全屏显示(true:是)
{
cout << "图形->是否全屏显示->" << enable << endl;
//其他代码略......
}
void effect(bool enable)//是否开启特效(true:是)
{
cout << "图形->是否开启特效->" << enable << endl;
}
void resolution(int index) //设置窗口分辨率
{
cout << "图形->分辨率设置选项->" << index << endl;
}
void antialiasing(bool enable)//是否开启抗锯齿(true:是)
{
cout << "图形->是否开启抗锯齿->" << enable << endl;
}
//其他接口略......
};
//声音相关类
class sound
{
//--------------单件类实现相关begin----------------
private:
sound() {};
sound(const sound& tmpobj);
sound& operator = (const sound& tmpobj);
~sound() {};
public:
static sound& getInstance()
{
static sound instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void bgsound(bool enable) //是否开启背景声音(true:是)
{
cout << "声音->是否开启背景声音->" << enable << endl;
}
void envirsound(bool enable)//是否开启环境音效(true:是)
{
cout << "声音->是否开启环境音效->" << enable << endl;
}
void expsound(bool enable) //是否开启表情声音(true:是)
{
cout << "声音->是否开启表情声音->" << enable << endl;
}
void setvolume(int level) //音量大小设置(0-100)
{
cout << "声音->音量大小为->" << level << endl;
}
//其他接口略......
};
//语音聊天相关类
class chatvoice
{
//--------------单件类实现相关begin----------------
private:
chatvoice() {};
chatvoice(const chatvoice& tmpobj);
chatvoice& operator = (const chatvoice& tmpobj);
~chatvoice() {};
public:
static chatvoice& getInstance()
{
static chatvoice instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void micvolume(int level) //麦克风音量大小设置(0-100)
{
cout << "语音聊天->麦克风音量大小为->" << level << endl;
}
void micsens(int level)//麦克灵敏度设置(0-100)
{
cout << "语音聊天->麦克风灵敏度为->" << level << endl;
}
void chatvolume(int level) //聊天音量设置(0-100)
{
cout << "语音聊天->聊天音量为->" << level << endl;
}
//其他接口略......
};
//---------------------------------------------------
//扮演外观模式角色的类
class conffacade
{
//--------------单件类实现相关begin----------------
private:
conffacade() {};
conffacade(const conffacade& tmpobj);
conffacade& operator = (const conffacade& tmpobj);
~conffacade() {};
public:
static conffacade& getInstance()
{
static conffacade instance;
return instance;
}
//--------------单件类实现相关end----------------
public:
void LowConfComputer() //对于低配置电脑,只开启一些低配置选项
{
graphic& g_gp = graphic::getInstance();
g_gp.display(true); //全屏耗费资源更低
g_gp.effect(false);
g_gp.resolution(2);
g_gp.antialiasing(false);
sound& g_snd = sound::getInstance();
g_snd.bgsound(false);
g_snd.envirsound(false);
g_snd.expsound(false);
g_snd.setvolume(15);
chatvoice& g_cv = chatvoice::getInstance();
g_cv.micvolume(20);
g_cv.micsens(50);
g_cv.chatvolume(60);
}
void HighConfComputer() //对于高配置电脑,能达到最好效果的项全部开启
{
graphic& g_gp = graphic::getInstance();
g_gp.display(false);
g_gp.effect(true);
g_gp.resolution(0);
g_gp.antialiasing(true);
sound& g_snd = sound::getInstance();
g_snd.bgsound(true);
g_snd.envirsound(true);
g_snd.expsound(true);
g_snd.setvolume(50);
chatvoice& g_cv = chatvoice::getInstance();
g_cv.micvolume(100);
g_cv.micsens(100);
g_cv.chatvolume(100);
}
};
int main()
{
conffacade& g_cffde = conffacade::getInstance();
cout << "低配置电脑,调用LowConfComputer接口" << endl;
g_cffde.LowConfComputer();
cout << "------------------" << endl;
cout << "高配置电脑,调用HighConfComputer接口" << endl;
g_cffde.HighConfComputer();
return 0;
}
例二:完整代码
//屏幕
class Screen
{
public:
void On()
{
cout << "屏幕打开了!" << endl;
}
void Off()
{
cout << "屏幕关闭了!" << endl;
}
};
//灯光
class Light
{
public:
void On()
{
cout << "灯光打开了!" << endl;
}
void Off()
{
cout << "灯光关闭了!" << endl;
}
};
//音箱
class Speaker
{
public:
void On()
{
cout << "音箱打开了!" << endl;
}
void Off()
{
cout << "音箱关闭了!" << endl;
}
};
//DVD播放器
class DvdPlayer
{
public:
void On()
{
cout << "DVD播放器打开了!" << endl;
}
void Off()
{
cout << "DVD播放器关闭了!" << endl;
}
};
//游戏机
class PlayerStation
{
public:
void On()
{
cout << "游戏机打开了!" << endl;
}
void Off()
{
cout << "游戏机关闭了!" << endl;
}
};
//家庭影院外观模式类
class HomeTheaterFacade
{
public:
void WatchMovie() //看电影
{
//屏幕打开,灯光熄灭,音箱打开,DVD播放器打开,游戏机关闭。
scnobj.On();
lgobj.Off();
spkobj.On();
dpobj.On();
psobj.Off();
}
void PlayGame() //玩游戏
{
//屏幕打开,灯光打开,音箱打开,DVD播放器关闭,游戏机打开。
scnobj.On();
lgobj.On();
spkobj.On();
dpobj.Off();
psobj.On();
}
private:
Screen scnobj;
Light lgobj;
Speaker spkobj;
DvdPlayer dpobj;
PlayerStation psobj;
};
int main()
{
HomeTheaterFacade htfacobj;
cout << "开始看电影---------------" << endl;
htfacobj.WatchMovie();
cout << "开始玩游戏---------------" << endl;
htfacobj.PlayGame();
return 0;
}