本文记录外观模式。
外观模式(Facade)又称为门面模式,是最简单的模式。该模式的作用是用来隔离接口,就是扮演中间层的角色,把结合紧密的两部分分割开,让这两部分内容通过中间层来打交道,类似于依赖倒置原则,高层和底层依赖于抽象层。从而极大的降低了两部分的耦合性。
一个配置相关的范例
一个游戏有如下配置:图形配置,声音配置和语音聊天配置,创建这三个类,因为这些类在项目中只保持一个对象即可,因此将这三个类设计为单例类。
cpp
class Graphic
{
private:
Graphic() {};
~Graphic() {};
Graphic(const Graphic& ojb) {}
Graphic& operator=(const Graphic& obj) {}
public:
static Graphic& getInstance()
{
static Graphic instance;
return instance;
}
public:
void displayFull(bool enable) // 显示全屏
{
cout << "displayFull" << endl;
}
void effect(bool enable) // 显示特效
{
cout << "effect" << enable << endl;
}
void resolution(int w, int h) //设置窗口分辨率
{
cout << "窗口分辨率宽:" << w << "分辨率高:" << h << endl;
}
void antialiasing(bool enable) // 抗锯齿
{
cout << "不开启抗锯齿 antialiasing" << endl;
}
};
class Sound
{
public:
Sound() {};
~Sound() {};
Sound(const Sound& ojb) {}
Sound& operator=(const Sound& obj) {}
public:
static Sound& getInstance()
{
static Sound instance;
return instance;
}
public:
void volume(int v) // 设置音量
{
cout << "设置音量大小:" << v << endl;;
}
void setenvironmentVolume(int ev)
{
cout << "开启环境音量 << " << ev << endl;
}
void setvolume(int v)
{
cout << "设置音量大小为:" << v << endl;
}
};
class chatVoice
{
private:
chatVoice() {};
~chatVoice() {};
chatVoice(const chatVoice& ojb) {}
chatVoice& operator=(const chatVoice& obj) {}
public:
static chatVoice& getInstance()
{
static chatVoice instance;
return instance;
}
public:
void setMicVoice(int v)
{
cout << "设置麦克风音量" << v << endl;
}
void setMicSense(int s)
{
cout << "设置麦克风灵敏度" << endl;
}
void setChatVolume(int c)
{
cout << "聊天音量为 " << c << endl;
}
};
void test()
{
Graphic& g1 = Graphic::getInstance();
g1.displayFull(true);
g1.effect(true);
g1.antialiasing(true);
g1.resolution(1920, 1080);
Sound& s1 = Sound::getInstance();
s1.setenvironmentVolume(100);
s1.setvolume(100);
s1.volume(100);
chatVoice& c1 = chatVoice::getInstance();
c1.setChatVolume(100);
c1.setMicSense(100);
c1.setMicVoice(111);
/*
displayFull
effect1
不开启抗锯齿 antialiasing
窗口分辨率宽:1920分辨率高:1080
开启环境音量 << 100
设置音量大小为:100
设置音量大小:100
聊天音量为 100
设置麦克风灵敏度
设置麦克风音量111
*/
}
通过结果可以看出,达到了设计这些类的目的。
引入外观(facade)模式
上面的代码中,在void test()类中(由main函数调用)的上面两个类Graphic ,Sound类的接口称为客户端代码,把这些具体的类称为业务类;上边代码中的test()中体现了客户端代码和业务类代码的直接交互,这不是一种好的设计方法。
现在希望将业务类和客户端代码解耦,实现一个新类,扮演中间层的角色,客户端代码不在需要直接与业务类打交道,而是直接和中间类打交道,这个中间的类就是外观模式类,该类对客户端提供了一些简单的调用接口,客户端直接通过这些简单的接口达到调用业务类的目的,新的设计如下图所示:
外观模式强调的是一种程序设计思想,并不是一种特殊的编程手法。上面代码加了一个新类Façade类,实现了客户端代码和业务代码的解耦,代码设计如下:
cpp
class Graphic
{
private:
Graphic() {};
~Graphic() {};
Graphic(const Graphic& ojb) {}
Graphic& operator=(const Graphic& obj) {}
public:
static Graphic& getInstance()
{
static Graphic instance;
return instance;
}
public:
void displayFull(bool enable) // 显示全屏
{
cout << "displayFull" << endl;
}
void effect(bool enable) // 显示特效
{
cout << "effect" << enable << endl;
}
void resolution(int w, int h) //设置窗口分辨率
{
cout << "窗口分辨率宽:" << w << "分辨率高:" << h << endl;
}
void antialiasing(bool enable) // 抗锯齿
{
cout << "不开启抗锯齿 antialiasing" << endl;
}
};
class Sound
{
public:
Sound() {};
~Sound() {};
Sound(const Sound& ojb) {}
Sound& operator=(const Sound& obj) {}
public:
static Sound& getInstance()
{
static Sound instance;
return instance;
}
public:
void volume(int v) // 设置音量
{
cout << "设置音量大小:" << v << endl;;
}
void setenvironmentVolume(int ev)
{
cout << "开启环境音量 << " << ev << endl;
}
void setvolume(int v)
{
cout << "设置音量大小为:" << v << endl;
}
};
class chatVoice
{
private:
chatVoice() {};
~chatVoice() {};
chatVoice(const chatVoice& ojb) {}
chatVoice& operator=(const chatVoice& obj) {}
public:
static chatVoice& getInstance()
{
static chatVoice instance;
return instance;
}
public:
void setMicVoice(int v)
{
cout << "设置麦克风音量" << v << endl;
}
void setMicSense(int s)
{
cout << "设置麦克风灵敏度" << endl;
}
void setChatVolume(int c)
{
cout << "聊天音量为 " << c << endl;
}
};
class Facade
{
private:
Facade() {};
~Facade()
{
}
Facade(const Facade& ojb) {}
Facade& operator=(const Facade& obj) {}
public:
static Facade& getInstance()
{
static Facade instance;
return instance;
}
public:
// 定义一个高配置的方法
void getHighConfig()
{
Graphic& g1 = Graphic::getInstance();
g1.displayFull(true);
g1.effect(true);
g1.antialiasing(true);
g1.resolution(1920, 1080);
Sound& s1 = Sound::getInstance();
s1.setenvironmentVolume(100);
s1.setvolume(100);
s1.volume(100);
chatVoice& c1 = chatVoice::getInstance();
c1.setChatVolume(100);
c1.setMicSense(100);
c1.setMicVoice(111);
}
void getLowConfig()
{
Graphic& g1 = Graphic::getInstance();
g1.displayFull(false);
g1.effect(false);
g1.antialiasing(false);
g1.resolution(1024, 180);
Sound& s1 = Sound::getInstance();
s1.setenvironmentVolume(10);
s1.setvolume(10);
s1.volume(10);
chatVoice& c1 = chatVoice::getInstance();
c1.setChatVolume(10);
c1.setMicSense(10);
c1.setMicVoice(11);
}
};
// 定义一个低配置的方法
void test2()
{
Facade& f1 = Facade::getInstance();
f1.getHighConfig();
f1.getLowConfig();
}
迪米特法则--- 原则5
外观模式体现了面向对象程序设计的一个原则---迪米特原则,该原则如下:一个对象对其他对象的了解应该尽可能少,从而降低了对象之间的耦合,提高系统的可维护性。例如,在一个系统中,模块之间调用时,通过提供一个统一的接口来实现,这样其他模块就不需要了解另外一个模块内部的实现细节了,当一个模块内部发生改变时,也不会影响到其他模块。
外观模式有两种角色:
外观角色Façade ,客户端用Façade角色的方法,该方法将客户端的请求传递到业务类中。
子系统角色:子系统就是业务类 Graphic Sound chatVoice 类,这些子类被外观类调用。
外观模式定义:提供了一个统一的接口,用来访问子系统中的一群接口。
另一个外观模式的范例
下面的例子也是一个外观模式的例子,在这个例子中,Façade 是外观角色,其调用了其他的类的接口,实现了和客户端的解耦。
cpp
// Screen Light Speaker DvdPlayer PlayerStation
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;
}
};
class Facade
{
public:
void On()
{
m_screen.On();
m_light.On();
m_speaker.On();
}
void Off()
{
m_screen.Off();
m_light.Off();
m_speaker.Off();
}
private:
Screen m_screen;
Light m_light;
Speaker m_speaker;
};
void test()
{
Facade f1;
f1.On();
f1.Off();
}
UML图如下: