结构型设计模式主要有一下模式:
- 适配器模式(Adpater)
- 桥接模式(Bridge)
- 组合模式(Composite)
- 装饰模式(Decotator)
- 外观模式(Facade)
- 享元模式(Flyweight)
- 代理模式(Proxy)
1、适配器(Adapter)
适配器模式可以将一个类的接口转换成客户端希望的另一个接口,使得原来由于接口不兼容而不能在一起工作的那些类可以在一起工作。通俗的讲就是当我们已经有了一些类,而这些类不能满足新的需求,此时就可以考虑是否能将现有的类适配成可以满足新需求的类。适配器类需要继承或依赖已有的类,实现想要的目标接口。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
适配器有两种实现方式,一种是复合模式,另外一种是继承模式
1.1使用复合实现适配器
/*
* 关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
* 以下示例中,假设我们之前有了一个双端队列,新的需求要求使用栈和队列来完成。
双端队列可以在头尾删减或增加元素。而栈是一种先进后出的数据结构,添加数据时添加到栈的顶部,删除数据时先删 除栈顶部的数据。因此我们完全可以将一个现有的双端队列适配成一个栈。
*/
//双端队列, 被适配类
class Deque
{
public:
void push_back(int x)
{
cout << "Deque push_back:" << x << endl;
}
void push_front(int x)
{
cout << "Deque push_front:" << x << endl;
}
void pop_back()
{
cout << "Deque pop_back" << endl;
}
void pop_front()
{
cout << "Deque pop_front" << endl;
}
};
//顺序类,抽象目标类
class Sequence
{
public:
virtual void push(int x) = 0;
virtual void pop() = 0;
};
//栈,后进先出, 适配类
class Stack:public Sequence
{
public:
//将元素添加到堆栈的顶部。
void push(int x) override
{
m_deque.push_front(x);
}
//从堆栈中删除顶部元素
void pop() override
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
//队列,先进先出,适配类
class Queue:public Sequence
{
public:
//将元素添加到队列尾部
void push(int x) override
{
m_deque.push_back(x);
}
//从队列中删除顶部元素
void pop() override
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
1.2使用继承实现适配器模式
/*
* 关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
* 以下示例中,假设我们之前有了一个双端队列,新的需求要求使用栈和队列来完成。
双端队列可以在头尾删减或增加元素。而栈是一种先进后出的数据结构,添加数据时添加到栈的顶部,删除数据时先删 除栈顶部的数据。因此我们完全可以将一个现有的双端队列适配成一个栈。
*/
//双端队列, 被适配类
class Deque
{
public:
void push_back(int x)
{
cout << "Deque push_back:" << x << endl;
}
void push_front(int x)
{
cout << "Deque push_front:" << x << endl;
}
void pop_back()
{
cout << "Deque pop_back" << endl;
}
void pop_front()
{
cout << "Deque pop_front" << endl;
}
};
//顺序类,抽象目标类
class Sequence
{
public:
virtual void push(int x) = 0;
virtual void pop() = 0;
};
//栈,后进先出, 适配类
class Stack:public Sequence
{
public:
//将元素添加到堆栈的顶部。
void push(int x) override
{
m_deque.push_front(x);
}
//从堆栈中删除顶部元素
void pop() override
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
//队列,先进先出,适配类
class Queue:public Sequence
{
public:
//将元素添加到队列尾部
void push(int x) override
{
m_deque.push_back(x);
}
//从队列中删除顶部元素
void pop() override
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
2、桥接模式
桥接模式:将抽象部分与实现部分分离,使它们都可以独立变换。
以下情形考虑使用桥接模式:
当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。
当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来。
当一个对象的多个变化因素可以动态变化的时候。
优点:
将实现抽离出来,再实现抽象,使得对象的具体实现依赖于抽象,满足了依赖倒转原则。
更好的可扩展性。
可动态的切换实现。桥接模式实现了抽象和实现的分离,在实现桥接模式时,就可以实现动态的选择具体的实现。
/*
* 关键代码:将现实独立出来,抽象类依赖现实类。
* 以下示例中,将各类App、各类手机独立开来,实现各种App和各种手机的自由桥接。
*/
#include <iostream>
using namespace std;
//抽象App类,提供接口
class App
{
public:
virtual ~App(){ cout << "~App()" << endl; }
virtual void run() = 0;
};
//具体的App实现类
class GameApp:public App
{
public:
void run()
{
cout << "GameApp Running" << endl;
}
};
//具体的App实现类
class TranslateApp:public App
{
public:
void run()
{
cout << "TranslateApp Running" << endl;
}
};
//抽象手机类,提供接口
class MobilePhone
{
public:
virtual ~MobilePhone(){ cout << "~MobilePhone()" << endl;}
virtual void appRun(App* app) = 0; //实现App与手机的桥接
};
//具体的手机实现类
class XiaoMi:public MobilePhone
{
public:
void appRun(App* app)
{
cout << "XiaoMi: ";
app->run();
}
};
//具体的手机实现类
class HuaWei:public MobilePhone
{
public:
void appRun(App* app)
{
cout << "HuaWei: ";
app->run();
}
};
int main()
{
App* gameApp = new GameApp;
App* translateApp = new TranslateApp;
MobilePhone* mi = new XiaoMi;
MobilePhone* hua = new HuaWei;
mi->appRun(gameApp);
mi->appRun(translateApp);
hua->appRun(gameApp);
hua->appRun(translateApp);
delete hua;
hua = nullptr;
delete mi;
mi = nullptr;
delete gameApp;
gameApp = nullptr;
delete translateApp;
translateApp = nullptr;
return 0;
}
3、组合(Composite)
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得客户端对单个对象和组合对象的使用具有一直性。
既然讲到以树形结构表示“部分-整体”,那可以将组合模式想象成一根大树,将大树分成树枝和树叶两部分,树枝上可以再长树枝,也可以长树叶,树叶上则不能再长出别的东西。
以下情况可以考虑使用组合模式:
希望表示对象的部分-整体层次结构。
希望客户端忽略组合对象与单个对象的不同,客户端将统一的使用组合结构中的所有对象。
/*
* 关键代码:树枝内部组合该接口,并且含有内部属性list,里面放Component。
*/
#include <iostream>
#include <list>
#include <memory>
using namespace std;
//抽象类,提供组合和单个对象的一致接口
class Company
{
public:
Company(const string& name): m_name(name){}
virtual ~Company(){ cout << "~Company()" << endl;}
virtual void add(Company* ) = 0;
virtual void remove(const string&) = 0;
virtual void display(int depth) = 0;
virtual const string& name()
{
return m_name;
}
protected:
string m_name;
};
//具体的单个对象实现类,“树枝”类
class HeadCompany : public Company
{
public:
HeadCompany(const string& name): Company(name){}
virtual ~HeadCompany(){ cout << "~HeadCompany()" << endl;}
void add(Company* company) override
{
shared_ptr<Company> temp(company);
m_companyList.push_back(temp);
}
void remove(const string& strName) override
{
list<shared_ptr<Company>>::iterator iter = m_companyList.begin();
for(; iter != m_companyList.end(); iter++)
{
if((*iter).get()->name() == strName)
{
//不应该在此处使用list<T>.erase(list<T>::iterator iter),会导致iter++错误,这里删除目 标元素之后,必须return。
m_companyList.erase(iter);
return;
}
}
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << this->name().data() << endl;
list<shared_ptr<Company>>::iterator iter = m_companyList.begin();
for(; iter!= m_companyList.end(); iter++)
{
(*iter).get()->display(depth + 1);
}
}
private:
list<shared_ptr<Company>> m_companyList;
};
//具体的单个对象实现类,“树叶”类
class ResearchCompany : public Company
{
public:
ResearchCompany(const string& name): Company(name){}
virtual ~ResearchCompany(){ cout << "~ResearchCompany()" << endl;}
void add(Company* ) override
{
}
void remove(const string&) override
{
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << m_name.data() << endl;
}
};
//具体的单个对象实现类,“树叶”类
class SalesCompany : public Company
{
public:
SalesCompany(const string& name): Company(name){}
virtual ~SalesCompany(){ cout << "~SalesCompany()" << endl;}
void add(Company* ) override
{
}
void remove(const string&) override
{
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << m_name.data() << endl;
}
};
//具体的单个对象实现类,“树叶”类
class FinanceCompany : public Company
{
public:
FinanceCompany(const string& name): Company(name){}
virtual ~FinanceCompany(){ cout << "~FinanceCompany()" << endl;}
void add(Company* ) override
{
}
void remove(const string&) override
{
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << m_name.data() << endl;
}
};
int main()
{
HeadCompany* headRoot = new HeadCompany("Head Root Company");
HeadCompany* childRoot1 = new HeadCompany("Child Company A");
ResearchCompany* r1 = new ResearchCompany("Research Company A");
SalesCompany* s1 = new SalesCompany("Sales Company A");
SalesCompany* s2 = new SalesCompany("Sales Company B");
FinanceCompany* f1 = new FinanceCompany("FinanceCompany A");
childRoot1->add(r1);
childRoot1->add(s1);
childRoot1->add(s2);
childRoot1->add(f1);
HeadCompany* childRoot2 = new HeadCompany("Child Company B");
ResearchCompany* r2 = new ResearchCompany("Research Company B");
SalesCompany* s3 = new SalesCompany("Sales Company C");
SalesCompany* s4 = new SalesCompany("Sales Company D");
FinanceCompany* f2 = new FinanceCompany("FinanceCompany B");
childRoot2->add(r2);
childRoot2->add(s3);
childRoot2->add(s4);
childRoot2->add(f2);
headRoot->add(childRoot1);
headRoot->add(childRoot2);
headRoot->display(1);
cout << "\n***************\n" << endl;
childRoot1->remove("Sales Company B");
headRoot->display(1);
cout << "\n***************\n" << endl;
delete headRoot;
headRoot = nullptr;
return 0;
}
4、装饰(Decorator)
装饰模式:动态地给一个对象添加一些额外的功能,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。新增加功能来说,装饰器模式比生产子类更加灵活。
以下情形考虑使用装饰模式:
需要扩展一个类的功能,或给一个类添加附加职责。
需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
/*
- 关键代码:1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
/
#include
using namespace std;
//抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
class Component
{
public:
virtual ~Component(){}
virtual void configuration() = 0;
};
//具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
class Car : public Component
{
public:
void configuration() override
{
cout << “A Car” << endl;
}
};
//装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
class DecorateCar : public Component
{
public:
DecorateCar(Component car) : m_pCar(car){}
void configuration() override
{
m_pCar->configuration();
}
private:
Component* m_pCar;
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateLED : public DecorateCar
{
public:
DecorateLED(Component* car) : DecorateCar(car){}
void configuration() override
{
DecorateCar::configuration();
addLED();
}
private:
void addLED()
{
cout << “Install LED” << endl;
}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecoratePC : public DecorateCar
{
public:
DecoratePC(Component* car) : DecorateCar(car){}
void configuration() override
{
DecorateCar::configuration();
addPC();
}
private:
void addPC()
{
cout << “Install PC” << endl;
}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateEPB : public DecorateCar
{
public:
DecorateEPB(Component* car) : DecorateCar(car){}
void configuration() override
{
DecorateCar::configuration();
addEPB();
}
private:
void addEPB()
{
cout << “Install Electrical Park Brake” << endl;
}
};
int main()
{
Car* car = new Car;
DecorateLED* ledCar = new DecorateLED(car);
DecoratePC* pcCar = new DecoratePC(ledCar);
DecorateEPB* epbCar = new DecorateEPB(pcCar);
epbCar->configuration();
delete epbCar;
epbCar = nullptr;
delete pcCar;
pcCar = nullptr;
delete ledCar;
ledCar = nullptr;
delete car;
car = nullptr;
return 0;
}
5、外观 (Facade)
外观模式:为子系统中的一组接口定义一个一致的界面;外观模式提供一个高层的接口,这个接口使得这一子系统更加容易被使用;对于复杂的系统,系统为客户端提供一个简单的接口,把负责的实现过程封装起来,客户端不需要连接系统内部的细节。
以下情形建议考虑外观模式:
设计初期阶段,应有意识的将不同层分离,层与层之间建立外观模式。
开发阶段,子系统越来越复杂,使用外观模式提供一个简单的调用接口。
一个系统可能已经非常难易维护和扩展,但又包含了非常重要的功能,可以为其开发一个外观类,使得新系统可以方便的与其交互。
优点:
实现了子系统与客户端之间的松耦合关系。
客户端屏蔽了子系统组件,减少了客户端所需要处理的对象数据,使得子系统使用起来更方便容易。
更好的划分了设计层次,对于后期维护更加的容易。
/*
* 关键代码:客户与系统之间加一个外观层,外观层处理系统的调用关系、依赖关系等。
*以下实例以电脑的启动过程为例,客户端只关心电脑开机的、关机的过程,并不需要了解电脑内部子系统的启动过程。
*/
#include <iostream>
using namespace std;
//抽象控件类,提供接口
class Control
{
public:
virtual void start() = 0;
virtual void shutdown() = 0;
};
//子控件, 主机
class Host : public Control
{
public:
void start() override
{
cout << "Host start" << endl;
}
void shutdown() override
{
cout << "Host shutdown" << endl;
}
};
//子控件, 显示屏
class LCDDisplay : public Control
{
public:
void start() override
{
cout << "LCD Display start" << endl;
}
void shutdown() override
{
cout << "LCD Display shutdonw" << endl;
}
};
//子控件, 外部设备
class Peripheral : public Control
{
public:
void start() override
{
cout << "Peripheral start" << endl;
}
void shutdown() override
{
cout << "Peripheral shutdown" << endl;
}
};
class Computer
{
public:
void start()
{
m_host.start();
m_display.start();
m_peripheral.start();
cout << "Computer start" << endl;
}
void shutdown()
{
m_host.shutdown();
m_display.shutdown();
m_peripheral.shutdown();
cout << "Computer shutdown" << endl;
}
private:
Host m_host;
LCDDisplay m_display;
Peripheral m_peripheral;
};
int main()
{
Computer computer;
computer.start();
//do something
computer.shutdown();
return 0;
}
6、享元(Flyweight)
享元模式:运用共享技术有效地支持大量细粒度的对象。在有大量对象时,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回内存中已有的对象,避免重新创建。
以下情况可以考虑使用享元模式:
系统中有大量的对象,这些对象消耗大量的内存,且这些对象的状态可以被外部化。
对于享元模式,需要将对象的信息分为两个部分:内部状态和外部状态。内部状态是指被共享出来的信息,储存在享元对象内部且不随环境变化而改变;外部状态是不可以共享的,它随环境改变而改变,是由客户端控制的。
/*
* 关键代码:将内部状态作为标识,进行共享。
*/
#include <iostream>
#include <map>
#include <memory>
using namespace std;
//抽象享元类,提供享元类外部接口。
class AbstractConsumer
{
public:
virtual ~AbstractConsumer(){}
virtual void setArticle(const string&) = 0;
virtual const string& article() = 0;
};
//具体的享元类
class Consumer : public AbstractConsumer
{
public:
Consumer(const string& strName) : m_user(strName){}
~Consumer()
{
cout << " ~Consumer()" << endl;
}
void setArticle(const string& info) override
{
m_article = info;
}
const string& article() override
{
return m_article;
}
private:
string m_user;
string m_article;
};
//享元工厂类
class Trusteeship
{
public:
~Trusteeship()
{
m_consumerMap.clear();
}
void hosting(const string& user, const string& article)
{
if(m_consumerMap.count(user))
{
cout << "A customer named " << user.data() << " already exists" << endl;
Consumer* consumer = m_consumerMap.at(user).get();
consumer->setArticle(article);
}
else
{
shared_ptr<Consumer> consumer(new Consumer(user));
consumer.get()->setArticle(article);
m_consumerMap.insert(pair<string, shared_ptr<Consumer>>(user, consumer));
}
}
void display()
{
map<string, shared_ptr<Consumer>>::iterator iter = m_consumerMap.begin();
for(; iter != m_consumerMap.end(); iter++)
{
cout << iter->first.data() << " : "<< iter->second.get()->article().data() << endl;
}
}
private:
map<string, shared_ptr<Consumer>> m_consumerMap;
};
int main()
{
Trusteeship* ts = new Trusteeship;
ts->hosting("zhangsan", "computer");
ts->hosting("lisi", "phone");
ts->hosting("wangwu", "watch");
ts->display();
ts->hosting("zhangsan", "TT");
ts->hosting("lisi", "TT");
ts->hosting("wangwu", "TT");
ts->display();
delete ts;
ts = nullptr;
return 0;
}
7、代理(Proxy)
代理模式:为其它对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
优点:
职责清晰。真实的角色只负责实现实际的业务逻辑,不用关心其它非本职责的事务,通过后期的代理完成具体的任务。这样代码会简洁清晰。
代理对象可以在客户端和目标对象之间起到中介的作用,这样就保护了目标对象。
扩展性好。
/*
* 关键代码:一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理* 类再访问真正要访问的对象。
*/
#include <iostream>
using namespace std;
class Gril
{
public:
Gril(const string& name = "gril"):m_string(name){}
string getName()
{
return m_string;
}
private:
string m_string;
};
class Profession
{
public:
virtual ~Profession(){}
virtual void profess() = 0;
};
class YoungMan : public Profession
{
public:
YoungMan(const Gril& gril):m_gril(gril){}
void profess()
{
cout << "Young man love " << m_gril.getName().data() << endl;
}
private:
Gril m_gril;
};
class ManProxy : public Profession
{
public:
ManProxy(const Gril& gril):m_pMan(new YoungMan(gril)){}
~ManProxy()
{
delete m_pMan;
m_pMan = nullptr;
}
void profess()
{
m_pMan->profess();
}
private:
YoungMan* m_pMan;
};
int main(int argc, char *argv[])
{
Gril gril("heihei");
ManProxy* proxy = new ManProxy(gril);
proxy->profess();
delete proxy;
proxy = nullptr;
return 0;
}
参考文章
【1】https://www.cnblogs.com/chengjundu/p/8473564.html