上篇文章“設計模式概覽——創建型模式”整理了創建型模式的5種類型,今天我們接着來整理一下結構型模式。
結構型模式旨在描述如何將類或對象結合在一起形成更大的結構。類結構型模式關注類的組合,一般只存在繼承關係和實現關係;對象結構型模式關心類與對象的組合,通過關聯關係使得在一個類中定義另一個類的實例對象,並實現對該對象方法的調用(我們現在開發的系統裏就有不少這種應用)。大部分結構型模式都是對象結構型模式。
結構型模式總共有7種具體的模式,分別是適配器模式、橋接模式、組合模式、裝飾模式、外觀模式、享元模式、代理模式。
下面我們就來看幾種結構型模式的定義和簡單描述。
1. 適配器模式(Adapter Pattern)
適配器模式的作用是將一個形式的接口轉換成另一個形式的接口,使接口不兼容的類可以一起工作,因此適配器也叫包裝器。適配器模式可以作爲類結構模式,也可以作爲對象結構模式。
適配器模式包含4個角色:
(1) 目標抽象類Target
(2) 適配器類Adapter
(3) 適配者類Adaptee
(4) 客戶類Client,或用戶類,即調用者。
示例代碼:
#include <iostream>
#include "Adapter.h"
#include "Adaptee.h"
#include "Target.h"
using namespace std;
int main(int argc, char *argv[])
{
Adaptee * adaptee = new Adaptee();
Target * tar = new Adapter(adaptee);
tar->request();
return 0;
}
///////////////////////////////////////////////////////////
// Adapter.h
// Definition of the Class Adapter
///////////////////////////////////////////////////////////
#include "Target.h"
#include "Adaptee.h"
class Adapter : public Target
{
public:
Adapter(Adaptee *adaptee);
virtual ~Adapter();
virtual void request();
private:
Adaptee* m_pAdaptee;
};
///////////////////////////////////////////////////////////
// Adapter.cpp
// Implementation of the Class Adapter
///////////////////////////////////////////////////////////
#include "Adapter.h"
Adapter::Adapter(Adaptee * adaptee)
{
m_pAdaptee = adaptee;
}
Adapter::~Adapter()
{
}
void Adapter::request()
{
m_pAdaptee->specificRequest();
}
///////////////////////////////////////////////////////////
// Adaptee.h
// Definition of the Class Adaptee
///////////////////////////////////////////////////////////
class Adaptee
{
public:
Adaptee();
virtual ~Adaptee();
void specificRequest();
};
適配器模式的適用場景:
(1) 系統需要使用現有的類,而這些類的接口不符合系統的需要;
(2) 希望建立一個可以重複使用的類,用於將彼此之間沒有太大關聯的類組織起來一起工作。
2. 橋接模式(Bridge Pattern)
橋接模式是將抽象部分與其實現部分分離,使兩者可以獨立變化,又稱爲柄體(Handle and Body)模式或接口(Interface)模式。
橋接模式包含四種角色:
(1) 抽象類Abstraction
(2) 擴充抽象類RefinedAbstraction
(3) 實現類接口Implementor
(4) 具體實現類ConcreteImplementor
示例代碼:
#include <iostream>
#include "ConcreteImplementorA.h"
#include "ConcreteImplementorB.h"
#include "RefinedAbstraction.h"
#include "Abstraction.h"
using namespace std;
int main(int argc, char *argv[])
{
Implementor * pImp = new ConcreteImplementorA();
Abstraction * pa = new RefinedAbstraction(pImp);
pa->operation();
Abstraction * pb = new RefinedAbstraction(new ConcreteImplementorB());
pb->operation();
delete pa;
delete pb;
return 0;
}
///////////////////////////////////////////////////////////
// RefinedAbstraction.h
// Definition of the Class RefinedAbstraction
///////////////////////////////////////////////////////////
#include "Abstraction.h"
class RefinedAbstraction : public Abstraction
{
public:
RefinedAbstraction();
RefinedAbstraction(Implementor* imp);
virtual ~RefinedAbstraction();
virtual void operation();
};
///////////////////////////////////////////////////////////
// RefinedAbstraction.cpp
// Implementation of the Class RefinedAbstraction
///////////////////////////////////////////////////////////
#include "RefinedAbstraction.h"
#include <iostream>
using namespace std;
RefinedAbstraction::RefinedAbstraction()
{
}
RefinedAbstraction::RefinedAbstraction(Implementor* imp)
:Abstraction(imp)
{
}
RefinedAbstraction::~RefinedAbstraction()
{
}
void RefinedAbstraction::operation()
{
cout << "do something else ,and then " << endl;
m_pImp->operationImp();
}
橋接模式理解起來有一定的難度。該模式的要點在於如何將抽象化和實現化脫耦,使兩者可以獨立地變化。橋接模式中的脫耦,是指在一個軟件系統的抽象化和實現化之間,使用關聯關係而不是繼承關係,從而使兩者的變化互不干擾。想象一個跨平臺視頻播放器,可以應用在不同的操作系統如Windows、Linux、iOS上面播放不同格式的視頻文件,如MP4、AVI、WMV等。
3. 裝飾模式(Decorator Pattern)
裝飾模式是動態地給一個對象增加一些額外的職責,在增加對象功能方面,裝飾模式比生成子類更加靈活。裝飾模式與適配器模式比較像,但它們適用於不同的場景。
裝飾模式包含4個角色:
(1) 抽象構件Component
(2) 具體構件ConcreteComponent
(3) 抽象裝飾類Decorator
(4) 具體裝飾類ConcreteDecorator
示例代碼:
///////////////////////////////////////////////////////////
// ConcreteComponent.cpp
// Implementation of the Class ConcreteComponent
///////////////////////////////////////////////////////////
#include "ConcreteComponent.h"
#include <iostream>
using namespace std;
ConcreteComponent::ConcreteComponent()
{
}
ConcreteComponent::~ConcreteComponent()
{
}
void ConcreteComponent::operation()
{
cout << "ConcreteComponent's normal operation!" << endl;
}
///////////////////////////////////////////////////////////
// ConcreteDecoratorA.h
// Definition of the Class ConcreteDecoratorA
///////////////////////////////////////////////////////////
#include "Decorator.h"
#include "Component.h"
class ConcreteDecoratorA : public Decorator
{
public:
ConcreteDecoratorA(Component* pcmp);
virtual ~ConcreteDecoratorA();
void addBehavior();
virtual void operation();
};
///////////////////////////////////////////////////////////
// ConcreteDecoratorA.cpp
// Implementation of the Class ConcreteDecoratorA
///////////////////////////////////////////////////////////
#include "ConcreteDecoratorA.h"
#include <iostream>
using namespace std;
ConcreteDecoratorA::ConcreteDecoratorA(Component* pcmp):Decorator(pcmp)
{
}
ConcreteDecoratorA::~ConcreteDecoratorA()
{
}
void ConcreteDecoratorA::addBehavior()
{
cout << "addBehavior AAAA" << endl;
}
void ConcreteDecoratorA::operation()
{
Decorator::operation();
addBehavior();
}
關聯關係與繼承關係相比,優勢在於不會破壞類的封裝性,是一種鬆耦合關係,特別適用於系統在維護階段增加行爲。裝飾者模式可以動態地給一個對象附加更多的行爲,在不創造更多子類的情況下,實現對象功能的擴展。
4. 外觀模式(Facade Pattern)
外觀模式又稱爲門面模式,它爲系統中的一組子系統提供了一個一致的高層接口(外觀對象),外部與這些子系統的通信必須通過這個外觀對象進行。
外觀模式包含2個角色:
(1) 外觀角色Facade
(2) 子系統角色SubSystem
示例代碼:
#include <iostream>
#include "Facade.h"
using namespace std;
int main(int argc, char *argv[])
{
Facade fa;
fa.wrapOpration();
return 0;
}
///////////////////////////////////////////////////////////
// Facade.h
// Definition of the Class Facade
///////////////////////////////////////////////////////////
#ifndef __FACADE_H__
#define __FACADE_H__
#include "SystemC.h"
#include "SystemA.h"
#include "SystemB.h"
class Facade
{
public:
Facade();
virtual ~Facade();
void wrapOpration();
private:
SystemC *m_SystemC;
SystemA *m_SystemA;
SystemB *m_SystemB;
};
#endif
///////////////////////////////////////////////////////////
// Facade.cpp
// Implementation of the Class Facade
///////////////////////////////////////////////////////////
#include "Facade.h"
Facade::Facade()
{
m_SystemA = new SystemA();
m_SystemB = new SystemB();
m_SystemC = new SystemC();
}
Facade::~Facade()
{
delete m_SystemA;
delete m_SystemB;
delete m_SystemC;
}
void Facade::wrapOpration()
{
m_SystemA->operationA();
m_SystemB->operationB();
m_SystemC->opeartionC();
}
外觀模式通過引入一個外觀對象,爲子系統的訪問提供了一個簡單而單一的入口,從而降低原有系統的複雜度,降低主調模塊與子系統類的耦合度,是“迪米特法則”的一種體現。主調模塊無須關心各個子系統的工作細節,通過外觀角色即可調用相應的功能。
5. 享元模式(Flaywight Pattern)
享元模式是利用共享技術支持大量細粒度對象的複用。系統只使用少量的對象,而這些對象很相似,狀態變化很小,可以實現對象的多次複用。由於享元模式要求能夠共享的對象必須是細粒度對象,因此它又稱爲輕量級模式。
享元模式中,可以共享的內容成爲內部狀態(Intrinsic State),而那些需要外部環境來設置的不能共享的內容稱爲外部狀態(Extrinsic State)。
享元模式包含4個角色:
(1) 抽象享元類Flyweight
(2) 具體享元類ConcreteFlyweight
(3) 非共享具體享元類UnsharedConcreteFlyweight
(4) 享元工廠類FltyweightFactory
示例代碼:
#include <iostream>
#include "ConcreteFlyweight.h"
#include "FlyweightFactory.h"
#include "Flyweight.h"
using namespace std;
int main(int argc, char *argv[])
{
FlyweightFactory factory;
Flyweight * fw = factory.getFlyweight("one");
fw->operation();
Flyweight * fw2 = factory.getFlyweight("two");
fw2->operation();
//aready exist in pool
Flyweight * fw3 = factory.getFlyweight("one");
fw3->operation();
return 0;
}
///////////////////////////////////////////////////////////
// FlyweightFactory.cpp
// Implementation of the Class FlyweightFactory
///////////////////////////////////////////////////////////
#include "FlyweightFactory.h"
#include "ConcreteFlyweight.h"
#include <iostream>
using namespace std;
FlyweightFactory::FlyweightFactory()
{
}
FlyweightFactory::~FlyweightFactory()
{
}
Flyweight* FlyweightFactory::getFlyweight(string str)
{
map<string,Flyweight*>::iterator itr = m_mpFlyweight.find(str);
if(itr == m_mpFlyweight.end())
{
Flyweight * fw = new ConcreteFlyweight(str);
m_mpFlyweight.insert(make_pair(str,fw));
return fw;
}
else
{
cout << "Aready in the pool,use the exist one:" << endl;
return itr->second;
}
}
///////////////////////////////////////////////////////////
// ConcreteFlyweight.h
// Definition of the Class ConcreteFlyweight
///////////////////////////////////////////////////////////
#ifndef __CONCRET_FLAYWEIGHT_H__
#define __CONCRET_FLAYWEIGHT_H__
#include "Flyweight.h"
#include <string>
using namespace std;
class ConcreteFlyweight : public Flyweight
{
public:
ConcreteFlyweight(string str);
virtual ~ConcreteFlyweight();
virtual void operation();
private:
string intrinsicState;
};
#endif
///////////////////////////////////////////////////////////
// ConcreteFlyweight.cpp
// Implementation of the Class ConcreteFlyweight
///////////////////////////////////////////////////////////
#include "ConcreteFlyweight.h"
#include <iostream>
using namespace std;
ConcreteFlyweight::ConcreteFlyweight(string str)
{
intrinsicState = str;
}
ConcreteFlyweight::~ConcreteFlyweight()
{
}
void ConcreteFlyweight::operation()
{
cout << "Flyweight[" << intrinsicState << "] do operation." << endl;
}
享元模式中通常會出現工廠模式,從上面代碼就可看出,享元工廠用來維護一個享元池,用於存儲具有相同內部狀態的享元對象。用於需要對象時,首先從享元池中獲取,如果享元池中不存在,則創建一個新的享元對象返回給用戶,並在享元池中保存該新增的對象。
6. 代理模式(Proxy Pattern)
當用戶不想或者不能直接引用一個對象時,可以通過一個稱之爲“代理”的第三者來實現間接應用。通過引入一個新的對象來實現對真實對象的操作,或者將新引入的對象作爲真實對象的一個替身,這種實現機制即爲代理模式。
代理模式包含3個角色:
(1) 抽象角色Subject
(2) 代理角色Proxy
(3) 真實角色 RealSubject
示例代碼:
#include <iostream>
#include "RealSubject.h"
#include "Proxy.h"
using namespace std;
int main(int argc, char *argv[])
{
Proxy proxy;
proxy.request();
return 0;
}
///////////////////////////////////////////////////////////
// Proxy.h
// Definition of the Class Proxy
///////////////////////////////////////////////////////////
#ifndef __PROXY_H__
#define __PROXY_H__
#include "RealSubject.h"
#include "Subject.h"
class Proxy : public Subject
{
public:
Proxy();
virtual ~Proxy();
void request();
private:
void afterRequest();
void preRequest();
RealSubject *m_pRealSubject;
};
#endif
///////////////////////////////////////////////////////////
// Proxy.cpp
// Implementation of the Class Proxy
///////////////////////////////////////////////////////////
#include "Proxy.h"
#include <iostream>
using namespace std;
Proxy::Proxy()
{
m_pRealSubject = new RealSubject();
}
Proxy::~Proxy()
{
delete m_pRealSubject;
}
void Proxy::afterRequest()
{
cout << "Proxy::afterRequest" << endl;
}
void Proxy::preRequest()
{
cout << "Proxy::preRequest" << endl;
}
void Proxy::request()
{
preRequest();
m_pRealSubject->request();
afterRequest();
}
抽象主題角色聲明瞭真實主題和代理主題的共同接口;代理主題角色內部包含對真實主題的引用,從而可以在任何時候操作真實主題對象;真實主題角色中實現了真實的業務操作。用戶可以通過代理主題角色間接調用真實主題角色中定義的方法。代理模式可以在一定程度上降低系統的耦合度。
常見的代理有:遠程代理、虛擬代理、保護代理、防火牆待、智能引用代理等。