橋接模式案例

橋接模式的定義:將抽象部分與它的實現部分分離,是他們都可以獨立地變化。

這樣的定義,對於初學者的我,簡直就是一頭霧水。什麼叫做抽象與實現分離?

其實上網查閱相關資料,再加上自己工程實踐,才慢慢明白這句話的意思。

所謂的抽象就是某個類暴露給外界的接口(說白了就是可調用的函數);

實現就是這些接口(函數)如何實現。

通常當一個抽象有多個實現時,使用繼承就可以了。但是繼承機制將抽象部分與實現部分固定在一起,使得難以對抽象部分和實現部分獨立地進行修改、擴充和重用。(書中原話)

其實也就是說,如果你修改基類,例如增加了一個接口,那麼所有的派生類都可能對這個函數加以實現。這樣抽象部分就影響了實現部分。而橋接模式就是將這種耦合解耦。

下面說一下我的應用場景,當然這並不是一個非常經典的案例,我只是爲了聯繫橋接模式而這樣設計的:

    現在需要一個通用網絡通信類庫,但通信大致分爲UDP和TCP通信,不管哪種通信機制,這個類庫並不關心,他只想知道實現通信需要哪些接口。然後我們草擬了這份接口

class  CSimpleSocket
{
protected:
 CSimpleSocket(void);
public:
 virtual ~CSimpleSocket(void){};
public:
 virtual BOOL Initialize() = 0;
 virtual BOOL IsInitialize()= 0;
 virtual VOID UnInitialize()= 0;
 virtual BOOL Connect(const char * pAddr,UINT uPort/*,int nSocketType/ *=SOCK_STREAM* /*/)= 0;
 virtual int Send(const char * pSendData,int nLen)= 0;
 //如果pRecvData是NULL則返回剩餘可讀取的大小
 virtual int Recv(char * pRecvData,int nLen)= 0;

 virtual INT ShutDown()= 0;
 virtual BOOL IsContected()= 0;
 virtual BOOL SetSendTimeOut(int nTimeOutSec)= 0;
 virtual BOOL SetRecvTimeOut(int nTimeOutSec)= 0;

 virtual BOOL SetReceiveBufferSize(int nRecvBufferSize)= 0;
 virtual int GetReceiveBufferSize()= 0;
 virtual BOOL SetSendBufferSize(int nSendBufferSize)= 0;
 virtual int GetSendBufferSize()= 0;
 virtual BOOL SetReuseAddress(BOOL bReuse)= 0;
 virtual BOOL GetReuseAddress()= 0;
 virtual BOOL SetKeepAlive(BOOL  bKeepAlive)= 0;
 virtual BOOL GetKeepAlive()= 0;
 INT GetLastErrorCode() {return WSAGetLastError();} ;
 VOID SetLastErrorCode(INT nErrCode) {WSASetLastError(nErrCode);} ;
protected:
 virtual CSimpleSocketImp * GetSocketImp();
 //設置新的CSimpleSocketImp類,並返回設置之前的CSimpleSocketImp類
 CSimpleSocketImp *SetSocketImp(CSimpleSocketImp * newSockImp);
private:
 CSimpleSocketImp * m_socketImp;
};

 

當客戶端調用函數的時候只需調用上面的函數即可,而無需關心到底是TCP還是UDP。

但光有接口是不行的,還必須有實現,實現也要有一定的規範,例如必須有startup、cleanup等函數。實現部分的規範和預定義的接口不必完全一致,只要實現的規範可以完成預定義接口的功能即可。

 

class CSimpleSocketImp
{
protected:
 CSimpleSocketImp(){};
public:
 virtual ~CSimpleSocketImp(){};
public:
 int StartUp();
 int CleanUp();
 SOCKET CreateSocket(int nAf,int nType,int nProtocol);
 int CloseSocket(SOCKET s);
 int BindToSocket(SOCKET sock,const struct sockaddr * sAddr,int nNameLen);
 int ConnectToSocket(SOCKET s,LPSOCKADDR lpName,int nNameLen);

 int SetSocketOption(SOCKET s,int nLevel,int nOptName,const char * pOptValue,int nOptLen);
 int GetSocketOption(SOCKET s,int nLevel,int nOptName,char FAR * pOptValue,int FAR *nOptLen);
 int GetErrorCode();
 VOID SetErrorCode(int nErrorCode);
// int getsockopt(  SOCKET s,  int level,  int optname,  char FAR* optval,
//  int FAR* optlen);
 virtual int ListenSocket(SOCKET s,int nQueueSize)=0;
 virtual SOCKET AcceptFromSocket(SOCKET s,LPSOCKADDR lpAddr,LPINT lpAddrLen)=0;
 virtual int ShutDown(SOCKET s)=0;
 virtual int SendData(SOCKET s,const char * lpBuffer,int nBufferLen,const sockaddr * sockAddr,int sockAddrLen)=0;
 virtual int RecvData(SOCKET s,char *lpBuffer,int nBufferLen,sockaddr * sockAddr,int* pSockAddrLen)=0;
 virtual int PeekData(SOCKET s,char *lpBuffer,int nBufferLen,sockaddr * sockAddr,int* pSockAddrLen)=0;

};

這個類的實例來實現具體的通信,這個類有兩個子類,TCPSocket,UDPSocket。

抽象接口將client的請求轉發給他的Implementor,Implementor將請求轉發給他的子類,子類完成具體的功能。

這樣做有什麼好處呢?

其實可以很容易發現,接口和實現可以同時進行開發。開發通信類庫的接口,由A組來實現;

接口的實現有B組來實現,實現的規範由B組統一之後,分兩撥人開發具體的TCPSocket,UDPSocket子類。上層調用通信類庫接口的應用程序只需按照接口部分就可以了。這樣是不是很不錯的選擇呢?

 

其實說白了,就是將可改變的部分延後實現,這個延後可以使時間的延後也可以是空間的延後。時間的延後就是運行的時候可以選擇具體實現,只需要調用TCPSocket,UDPSocket子類就可以了,具體怎麼選擇對於上層就是透明的了。空間的延後就是繼承,由上層決定是用哪個子類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章