1. 程式人生 > >第2章 包裝外觀(Wrapper Facade):用於在類中封裝函式的結構型模式

第2章 包裝外觀(Wrapper Facade):用於在類中封裝函式的結構型模式

可採取下面的步驟來實現包裝外觀模式: t 9t '9  
lX-i�<0`  
1. 確定現有函式間的內聚的抽象和關係: Win32 POSIX X Windows這樣被實現為獨立的函式和資料結構的傳統 API提供許多內聚的抽象,比如用於網路程式設計、同步和執行緒,以及 GUI管理的機制。但是,由於在像 C這樣的低階語言中缺乏資料抽象支援,開發者常常並不能馬上明瞭這些現有的函式和資料結構是怎樣互相關聯的。因此,應用包裝外觀的第一步就是確定現有 API中的較低階函式之間的內聚的抽象和關係。換句話說,我們通過將現有的低階 API函式和資料結構聚合進一或多個類中來定義一種“物件模型”。
Vo6+|�ztk|  
在我們的日誌例子中,我們從仔細檢查我們原來的日誌伺服器實現開始。該實現使用了許多低階函式,由它們實際提供若干內聚的服務,比如同步和網路通訊。例如, mutex_lock mutex_unlock函式與互斥體同步抽象相關聯。同樣地, socket bind listen accept函式扮演了網路程式設計抽象的多種角色。 +?W4ac1  
2. 將內聚的函式組聚合進包裝外觀類和方法中: 該步驟可劃分為以下子步驟: gkX7,J-0  
在此步驟中,我們為每組相關於特定抽象的函式和資料結構定義一或多個包裝外觀類。 Y]*&/Ex"/  
@ze2'56F }
 
A. 建立內聚的類: 我們從為每組相關於特定抽象的函式和資料結構定義一或多個包裝外觀類開始。用於建立內聚的類的若干常用標準包括: QF npp/K  
?E_;[(Mcr  
  •   確定在底層函式中什麼是通用的 什麼是可變的 ,並把函式分組進類中,從而將變化隔離在統一的介面後面。
將具有高內聚性 cohesion)的函式合併進獨立的類中,同時使類之間不必要的耦合 最小化。 BsA'r+ho?H  
一般而言,如果原來的 API含有廣泛的相關函式,就有可能必須建立若干包裝外觀類來適當地對事務進行分理。 :<s`)  
Y+o/?|q-E  
B. 將多個獨立函式合併進類方法中:
除了將現有函式分組進類中,在每個包裝類中將多個獨立函式組合進數目更少的方法中常常也是有益的。例如,為確保一組低階函式以適當的順序被呼叫,可能必須要採用此設計。 qo$ls/[X  
Kj�"X!-�  
C. 選擇間接層次: 大多數包裝外觀類簡單地將它們的方法呼叫直接轉發給底層的低階函式。如果包裝外觀方法是內聯的,與直接呼叫低階函式相比,可能並沒有額外的間接層次。為增強可擴充套件性,還可以通過動態分派包裝外觀方法實現來增加另外的間接層次。在這種情況下,包裝外觀類扮演橋接( Bridge)模式 [8]中的抽象 Abstraction)角色。 qp)Wt6 k?  
9R2"(.U  
D. 確定在哪裡處理平臺特有的變種: 使平臺特有的應用程式碼最少化是使用包裝外觀模式的重要好處。因而,儘管包裝外觀類方法的實現 在不同的 OS平臺上可以不同,它們應該提供統一的、平臺無關的介面 Bu&9J(J1  
處理平臺特有變種的一種策略是在包裝外觀類方法實現中使用 #ifdef。在聯合使用 #ifdef和自動配置工具(比如 GNU autoconf)時,可以通過單一的原始碼樹建立統一的、不依賴於平臺的包裝外觀。另一種可選策略是將不同的包裝外觀類實現分解進分離的目錄中(例如,每個平臺有一個目錄),並配置語言處理工具,以在編譯時將適當的包裝外觀類包含進應用中。 tHo|8c~[  
選擇特定的策略在很大程度上取決於包裝外觀方法實現變動的頻度。例如,如果它們頻繁變動,為每個平臺正確地更新 #ifdef可能是單調乏味的。同樣地,所有依賴於該檔案的檔案可能都需要重編譯,即使變動僅僅對一個平臺來說是必需的。 /gkhSLq  
>MKj~Ud  
 在我們的日誌例子中,我們將為互斥體、 socket和執行緒定義包裝外觀類,以演示每一子步驟是怎樣被實施的。如下所示: dWQB1Y*N  
:s&dn%5N"  
互斥體包裝外觀: 我們首先定義 Thread_Mutex抽象,在統一和可移植的類介面中封裝 Solaris互斥體函式: QxA( *1  
classThread_Mutex y�3o3�G  
{ c|`$ h  
public: VH~YwO!x  
Thread_Mutex(void) iF Mf[qBg  
{ $Hj;i/zD  
mutex_init (&mutex_, 0, 0); JB].ht  
} s3nO"~tM  
  `Fo/RZOW  
?Thread_Mutex(void) !H) -  
{ p/RT*?<  
mutex_destroy (&mutex_); [` qdpzUp&  
} s2NBYDi$?  
  rD4umWi  
int acquire(void) [:#K_EI5%  
{ }zf!mlk  
return mutex_lock (&mutex_); ~Ck�OiWC0  
} OR!W3 @  
  Xpn/TD<_I  
int release(void) %b<W]HwA  
{ ~=iH*AQR  
return mutex_unlock (&mutex_); Ikf[K%NKn  
} Iq4B%xo6G  
  :&TM0O  
private: [8IO0lul+  
//Solaris-specific Mutex mechanism. qf/W,SM  
mutex_t mutex_; sAqy(oy#M  
  @WS77d~S  
// = Disallowcopying and assignment. } v:YSG  
Thread_Mutex(c*****t Thread_Mutex &); 4jC)"tch  
void operator=(c*****t Thread_Mutex &); 'xj5R=V  
}; 6{]F#ig=  
ll4CF}k  
  通過定義 Thread_Mutex類介面,並隨之編寫使用它、而不是低階本地 OS CAPI的應用,我們可以很容易地將我們的包裝外觀移植到其他平臺。例如,下面的 Thread_Mutex實現在 Win32上工作: cI/[)5&  
`}#rc DK  
classThread_Mutex Fy Ih/  
{ EUuSN| a  
public: ;Go^)bN ;  
Thread_Mutex(void) iLuC_.'u=  
{ [QgP6f]=  
InitializeCriticalSection(&mutex_); |*NZ^6`@  
} Fb]+h)on  
  bc'IoD/  
?Thread_Mutex(void) )8W! |  
{ s<F*kLib  
DeleteCriticalSection(&mutex_); zFExYYd  
} `/>kN%  
  } jJKE  
int acquire(void) Tt)z[^)%  
{ W^L^7  
EnterCriticalSection (&mutex_);return 0; r9_ ON|  
} T /}U{9ELL  
  tjx8UgSi  
int release(void) xjo`u:BH  
{ ?lsK?>uU  
LeaveCriticalSection (&mutex_);return 0; }b(hD|e  
} ZDFq=)0C  
  /Pg)7Zn  
private: '2 r  
//Win32-specific Mutex mechanism. jDY B*Y^F  
CRITICAL_SECTIONmutex_; hI86WP9*  
  Hloe7+5UD  
// = Disallowcopying and assignment. rS BI'op  
Thread_Mutex(c*****t Thread_Mutex &); _Rii19k  
void operator=(c*****t Thread_Mutex &); _ Lh0  
}; cpa" ,8  
03fOm  
  如早先所描述的,我們可以通過在 Thread_Mutex方法實現中使用 #ifdef以及自動配置工具(比如 GUN autoconf)來支援多個 OS平臺,以使用單一原始碼樹提供統一的、平臺無關的互斥體抽象。相反,我們也可以將不同的 Thread_Mutex實現分解進分離的目錄中,並指示我們的語言處理工具在編譯時將適當的版本包含進我們的應用中。 |:R/j0t  
  除了改善可移植性,我們的 Thread_Mutex包裝外觀還提供比直接程式設計低階 Solaris函式和 mutex_t資料結構更不容易出錯的互斥體介面。例如,我們可以使用 C++ private訪問控制指示符來禁止互斥體的拷貝和賦值;這樣的使用是錯誤的,但卻不會被不那麼強型別化的 C程式設計 API所阻止。 })�-V,/  
);.$`0  
socket 包裝外觀: socket API Solaris互斥體 API要大得多,也有表現力得多 [5]。因此,我們必須定義一組相關的包裝外觀類來封裝 socket。我們將從定義下面的處理 UNIX/Win32可移植性差異的 typedef開始: hH1Q:}a  
#if !defined(_WINSOCKAPI_) ]%6%rq%9C  
typedef intSOCKET; m0bxVV^DK!  
#defineINVALID_HANDLE_VALUE -1 qIZ+%ZOu  
#endif /*_WINSOCKAPI_ */ [email protected]  
s9wzN6re  
接下來,我們將定義 INET_Addr類,封裝 Internet域地址結構: cn} CI  
,"`20.Lv  
class INET_Addr ` 7iA?;  
{ b /UXO$_~-  
public: Dr.eos4 ~  
INET_Addr(u_short port, long addr) ejV`W7U  
{ MM32/}Y6  
// Set up the address to become aserver. m-O *t$6  
memset (reinterpret_cast <void*> (&addr_), 0, sizeof addr_); =cl#aS}e8  
addr_.sin_family = AF_INET; ;&j'`t P  
addr_.sin_port = ht***** (port); i/[email protected]/{-v  
addr_.sin_addr.s_addr = htonl(addr); AJ/Hw>>$?m  
} y05!-G:Y/  
  q<E7qY+  
u_shortget_port (void) c*****t U=D;CjAh  
{ 9mDdX  
return addr_.sin_port; !6|_`l>G,  
} {O _X/y~  
  )2).kL>  
longget_ip_addr (void) c*****t 3/v tx9D  
{ `S((F|Ty=;  
return addr_.sin_addr.s_addr; IA0vSF:  
} &pI/VIx ?  
  Kc]cJ`P4.  
sockaddr *addr(void) c*****t k`>qb8,  
{ Ia](CN*;6  
return reinterpret_cast<sockaddr *>(&addr_); GThGV"  
} =Jl/^u%H(x  
  :c]y/lQmV  
size_t size(void) c*****t bP$e1I3`  
{ 1<@lM8&.kO  
return sizeof (addr_); O8hx}dOjA  
} umPn w  
// ... FsUH/Y y  
  %tkqWK:  
private: KD#zsL)3  
sockaddr_inaddr_; wa[J/lW  
}; hlyh8=Z6o  
L ' _%zO  
  注意 INET_Addr構造器是怎樣通過將 sockaddr_in域清零,並確保埠和 IP地址被轉換為網路位元組序,消除若干常見的 socket程式設計錯誤的。 5Zc  
  下一個包裝外觀類, SOCK_Stream,對應用可在已連線 socket控制代碼上呼叫的 I/O操作(比如 recv send)進行封裝: Y]R=z*i%  
2 O([email protected]?  
classSOCK_Stream >*/ |tL  
{ ce4rhtkV  
public: Pi[]k]XA/  
// =C*****tructors. fc:87ZR{K  
// Defaultc*****tructor. )e[q%%ks  
SOCK_Stream(void) i{:?Iw 'ay  
: handle_ (INVALID_HANDLE_VALUE) {} cJT_Qfxx  
  hBZh0xy  
// Initializefrom an existing HANDLE. F9w2+z.  
SOCK_Stream(SOCKET h): handle_ (h) {} mMZ=9 ?m  
  ]�%[email protected]  
//Automatically close the handle on destruction. U2_;  
?SOCK_Stream (void){ close (handle_); } VKXB)-'L  
  K:4G(?w  
void set_handle(SOCKET h) { handle_ = h; } RionKiN  
SOCKETget_handle (void) c*****t { return handle_; } ENYc.$r  
  1:h(8%[email protected]"  
// = I/Ooperati*****. 50S*_4R  
int recv (char*buf, size_t len, int flags = 0); _Axw$oYS  
int send (c*****tchar *buf, size_t len, int flags = 0); ojWf]$^y}  
// ... q<g!bW%  
  $H,9GIivD  
private: } F*=+n  
// Handle forexchanging socket data. CwEb ?  
SOCKET handle_; '~6l 6wi  
}; )]}68}9  
Wu?[1L:x  
注意此類是怎樣確保 socket控制代碼在 SOCK_Stream物件出作用域時被自動關閉的。 %eu_Pr�6X  
  SOCK_Stream物件由連線工廠 SOCK_Acceptor建立,後者封裝被動的 連線建立邏輯 [9] SOCK_Acceptor構造器初始化被動模式接受器 socket,以在 sock_addr地址上進行偵聽。同樣地, accept工廠方法通過新接受的連線來初始化 SOCK_Stream,如下所示: Iak06E  
KC/W6|NtGj  
classSOCK_Acceptor % +$!ctn  
{ xiQd[[(sM  
public: p_sqw~)^%  
SOCK_Acceptor(c*****t INET_Addr &sock_addr) Ac,bf 8C  
{ DVbY  
// Create a local endpoint ofcommunication. x A ZRl  
handle_ = socket (PF_INET,SOCK_STREAM, 0); 6}[email protected]&  
  ^6^A/] v  
// Associate address with endpoint. %t-}dC&  
bind (handle_, sock_addr.addr (), sock_addr.size()); ^W ,x  
  |fWR[/NU  
// Make endpoint listen forconnecti*****. 1b"3 ]?  
listen (handle_, 5); cy_zEJjbD  
}; I#t#%!InH  
  .`N&,& H  
// Accept aconnection and initialize K}Pi"[email protected]  
// the<stream>. yCye3z.  
int accept(SOCK_Stream &stream) [email protected] !~q�  
{ u!VY6y7p  
stream.set_handle (accept (handle_,0, 0)); Z|Xv_Xo|4  
if (stream.get_handle () ==INVALID_HANDLE_VALUE) X=b]Whuv  
return -1; RjQdlr6*  
else return 0; [kg*BaG:  
} J|I&{  
  /wo'XF3:  
private: s av  
// Sockethandle factory. cY/"{o"C  
SOCKET handle_; P&] PJt5  
}; }>u<,  
c1Ta!p{%  
注意 SOCK_Acceptor的構造器是怎樣確保低階的 socket bind listen函式總是以正確的次序被呼叫的。 o.H(&ex|  
  完整的 socket包裝外觀集還包括 SOCK_Connector,封裝主動的 連線建立邏輯 [9] d#G H4+C  
.9;wJ9Bw[  
執行緒外觀: 在不同的 OS平臺上有許多執行緒 API可用,包括 Solaris執行緒、 POSIX Pthreads Win32執行緒。這些 API顯示出微妙的語法和語義差異,例如, Solaris POSIX執行緒可以“分離”( detached)模式被派生,而 Win32執行緒則不行。但是,可以提供 Thread_Manager包裝外觀,在統一的 API中封裝這些差異。如下所示: ['n;e:*  
classThread_Manager g{06d~Y  
{ -{XXU�)Z  
public: X>y6-%@  
int spawn (void*(*entry_point) (void *), Z}'"c9oB  
void *arg, x,SzZ)l-9  
long flags, GT"gB$Mh  
long stack_size = 0, }PDNW  
void *stack_pointer = 0, c,I|O' &k  
thread_t *t_id = 0) K+_$ WT_  
{ hd}"%9p  
thread_t t; 5#U*vGVT  
if (t_id == 0) Eo }mSd  
t_id = &t; 7Q9zEd"d  
  -Fj:^q:@u  
return thr_create (stack_size, isP4*g&%x  
stack_pointer, T;%ceLD  
entry_point, _ADK8a6%)  
arg, #Mz N7  
flags, b}[W[J}`  
t_id); Px)/`'D  
} W%=b|6E  
// ... huau(s0um  
}; @edi6b1W  
IRZ?'Im  
Thread_Manager還提供聯接( join)和取消執行緒的方法。 &r;4$7  
*zy0,{bl  
1. 確定錯誤處理機制: 低階的 C函式 API通常使用返回值和整型程式碼(比如 errno)來將錯誤通知給它們的呼叫者。但是,此技術是容易出錯的,因為呼叫者可能會忘記檢查它們的函式呼叫的返回狀態。 &zF1&J58z  
更為優雅的報告錯誤的方式是使用異常處理。許多程式語言,比如 C++ Java,使用異常處理來作為錯誤報告機制。它也被某些作業系統所使用,比如 Win32 ;TK:D=p4  
使用異常處理作為包裝外觀類的錯誤處理機制有若干好處: 5UwaBPj4  
l/yFx  
它是可擴充套件的: 現代程式語言允許通過對現有介面和使用干擾極少的特性來擴充套件異常處理策略和機制。例如, C++ Java使用繼承來定義異常類的層次。 它使錯誤處理與正常處理得以乾淨地去耦合: 例如,錯誤處理資訊不會顯式地傳遞給操作。而且,應用不會因為沒有檢查函式返回值而偶然地忽略異常。 它可以是型別安全的: 在像 C++ Java這樣的語言中,異常以一種強型別化的方式被扔出和捕捉,以增強錯誤處理程式碼的組織和正確性。相對於顯式地檢查執行緒專有的錯誤值,編譯器會確保對於每種型別的異常,將執行正確的處理器。 vTD`Ja#h  
但是,為包裝外觀類使用異常處理也有若干缺點: ~T')s-,l,:  
Ve&(izI h  
它不是通用的: 不是所有語言都提供異常處理。例如,某些 C++編譯器沒有實現異常。同樣地,當 OS提供異常服務時,它們必須被語言擴充套件所支援,從而降低了程式碼的可移植性。 它使多種語言的使用變得複雜化: 因為語言以不同的方式實現異常,或根本不實現異常,如果以不同語言編寫的元件扔出異常,可能很難把它們整合在一起。相反,使用整型值或結構來報告錯誤資訊提供了更為通用的解決方案。 它使資源管理變得複雜化: 如果在 C++ Java程式碼塊中有多個退出路徑,資源管理可能會變得複雜化 [10]。因而,如果語言或程式設計環境不支援垃圾收集,必須注意確保在有異常扔出時刪除動態分配的物件。 它有著潛在的時間和 / 或空間低效的可能性: 即使沒有異常扔出,異常處理的糟糕實現也會帶來時間和 /或空間的過度開銷 [10]。對於必須具有高效和低記憶體佔用特性的嵌入式系統來說,這樣的開銷可能會特別地成問題。 XY5I5H_U  
  對於封裝核心級裝置驅動程式或低階的本地 OS API(它們必須被移植到許多平臺上)的包裝外觀來說,異常處理的缺點也是特別成問題的。對於這些型別的包裝外觀,更為可移植、高效和執行緒安全的處理錯誤的方式是定義錯誤處理器抽象,顯式地維護關於操作的成功或失敗的資訊。使用執行緒專有儲存( Thread-Specific Storage)模式 [11]是被廣泛用於這些系統級包裝外觀的解決方案。 iaq0/d.[7  
kg$<^:uX  
1. 定義相關助手類(可選): 一旦低階函式和資料結構被封裝在內聚的包裝外觀類中,常常有可能建立其他助手類來進一步簡化應用開發。通常要在包裝外觀模式已被應用於將低階函式和與其關聯的資料聚合進類中之後,這些助手類的效用才變得明顯起來。 =S-'*F  
例如,在我們的日誌例子中,我們可以有效地利用下面的實現 C++ ScopedLocking 習語的 Guard類;該習語確保 Thread_Mutex被適當地釋放,不管程式的控制流是怎樣退出作用域的。 {O6f1LuH  
Xv'M/T}6C+  
template<class LOCK> /HDRr*KO  
class Guard )#r]x1[Kn  
{ oSt-w{!  
public: [email protected]|g )  
Guard (LOCK&lock): lock_ (lock) -/j}le6;c  
{ X;�T(?,,  
lock_.acquire (); ]7 ROCJ;  
} )L`0VTw'M  
  xX  
?Guard (void) s} ,p>8  
{ Hq^s U%  
lock_.release (); pHY~_^B4&  
} 8p7Uvn+m*  
  r}9qK%C G.  
private: S 1|[}nYP  
// Hold thelock by reference to avoid &@A(8(%  
// the use ofthe copy c*****tructor... JcZs/ fl9  
LOCK&lock_; O�GrVy=rd  
} )oa6;=go  
8(D>ws$  
Guard類應用了 [12]中描述的 C++習語,藉此,在一定作用域中“構造器獲取資源而析構器釋放它們”。如下所示: &D uvy#J  
~d7!)c`z  
// ... /A _g  
{ waW2$9O  
// C*****tructorof <mon> automatically vj^vzFb�K  
// acquires the<mutex> lock. $ W(m  
Guard<Thread_Mutex>mon (mutex); '6fMF#X4F  
  +%8c8]2  
// ...operati***** that must be serialized ... mTW0_!.  
  S?*v p=  
// Destructorof <mon> automatically *8fnxWR  
// releases the<mutex> lock. Txfu%'2)e  
} v$w!hYsQ  
// ... @X"p"3V  
qn6Y(@<[  
  因為我們使用了像 Thread_Mutex包裝外觀這樣的 ,我們可以很容易地替換不同型別的鎖定機制,與此同時仍然複用 Guard的自動鎖定 /解鎖協議。例如,我們可以用 Process_Mutex類來取代 Thread_Mutex類,如下所示: 1-1x,U7w  
s6rdQI]  
// Acquire aprocess-wide mutex. E:f0NV3"1  
Guard<Process_Mutex>mon (mutex); Lc�f =)GL  
<jQ?l%/  
  如果使用 C函式和資料結構、而不是 C++類,獲得這種程度的“可插性”( pluggability)要困難得多。 +L!-JrYHS4  
qXkc~{W_  
2.2.9 例子解答 .:*V CDOM  
  下面的程式碼演示日誌伺服器的 main函式,它已使用 2.2.8描述的互斥體、 socket和執行緒的包裝外觀重寫。 ^vLHs=<  
f:G�Zb?Wyd  
// At filescope. xpKD 'O=T  
// Keep trackof number of logging requests. :O{`!&[>L  
static intrequest_count; +n�%uIv  
  =GTltFqI 1  
// Managethreads in this process. DF-`nD  
staticThread_Manager thr_mgr; (n05MwKu/  
  V]t ucs  
// Lock to protectrequest_count. t>.�[email protected]|  
staticThread_Mutex lock; ZWQrG'$?o8  
  Wjn1W;m&g  
// Forwarddeclaration. //nR=Dy{  
static void*logging_handler (void *); 5�e~/o}]  
  $J>GCY  
// Port numberto listen on for requests. O6NgI2[O  
static c*****tint logging_port = 10000; [email protected]  
  +VkhM;'"C  
// Main driverfunction for the multi-threaded t8DySFT  
// loggingserver. Some error handling has been eEePK~%c  
// omitted tosave space in the example. x+x6F  
int main (intargc, char *argv[]) 9E4H`[EQ  
{ 3`S|I_$(T"  
// Internetaddress of server. ?2zVW Z  
INET_Addr addr(port); uo;aC$US  
  CpNnywDRwU  
// Passive-modeacceptor object. Veo:G{  
SOCK_Acceptorserver (addr); _fx0-S*$  
  OvqCuX  
SOCK_Streamnew_stream; l2QO/O I9m  
  ||a 5)D  
// Wait for aconnection from a client. D's'LspQ  
for (;;) e6f:@ O?  
{ wDswK "T  
// Accept a connection from aclient. GO{o # }  
server.accept (new_stream); 2/h}6DGx2  
  <`,pyvR Kv  
// Get the underlying handle. H=Rqr  
SOCKET h = new_stream.get_handle(); J0CEZ  
  b�fy `UZr  
// Spawn off athread-per-connection. }6ObQa43  
thr_mgr.spawn (logging_handler, y`O !,kW  
reinterpret_cast <void *>(h), g"v�g {Q  
THR_DETACHED); :+,>0%  
} ^Y�z.,!B[  
} M:x?I_JG8  
]vj4E"2;  
logging_handler函式執行在單獨的執行緒控制中,也就是,每個相連客戶有一個執行緒。它在各個連線上接收並處理日誌記錄,如下所示: y57]q#k  
i( P/=B  
// Entry pointthat processes logging records for kXimJL_<g  
// one clientconnection. "Y0:Y?Vz"  
void*logging_handler (void *arg) td!WgL,m�  
{ "8MG[$Y  
SOCKET h =reinterpret_cast <SOCKET> (arg); (qd�$wv^h  
  Lt ;!q b.  
// Create a<SOCK_Stream> object from SOCKET <h>. G{$(t/>8  
SOCK_Streamstream (h); x�xxM  
for (;;) fFP>$  
{ 5sJi- ^�  
UINT_32 len; // Ensure a 32-bitquantity. RDU,yTHq  
char log_record[LOG_RECORD_MAX]; +�E8/g  
  '[email protected]  
// The first <recv_n> readsthe length W+k`^A|@  
// (stored as a 32-bit integer) of <[email protected][7!  
// adjacent logging record. Thiscode P|}~=2J  
// handles"short-<recv>s". 86Q/G.h7  
ssize_t n = stream.recv_n bg.f';C  
(reinterpret_cast <char *>(&len), r,}U -S.w  
sizeof len); P(?i>F7s  
  ;0*T7l  
// Bail out if we’re shutdown or [email protected] KZ`  
// errors occur unexpectedly. !(/OT  
if (n <= 0) break; 4k' 2FkDA  
len = ntohl (len); // Convertbyte-ordering. }'HJV��B_  
if (len > LOG_RECORD_MAX) break; WK7=z3mu  
  :x e/7�-  
// The second <recv_n> thenreads <len> fsc^8  
// bytes to obtain the actualrecord. 7{=<_  
// This code handles"short-<recv>s". [email protected]>Vb  
n = stream.recv_n (log_record,len); Y9F78 =Q�  
  v=Ep  
// Bail out if we’re shutdown or EoLF7j<W  
// errors occur unexpectedly. 0:-i  
if (n <= 0) break; 3UeG>5R  
  Bvx%|:R  
{ a&YD4DQ05  
// C*****tructor of Guardautomatically 2^ 'X  
// acquires the lock. :b+C<Bp64r  
Guard<Thread_Mutex> mon(lock); c_b^t09  
  75vd ]45as  
// Execute following two statementsin a �f:L%th  
// critical section to avoid raceconditi***** Nx4_Oc^hY  
// and scrambled output,respectively. ?! dp0<  
++request_count; // Count # ofrequests "Yw-1h`fR  
  =!3G�,qV  
if (write (STDOUT, log_record, len)== -1) T1m097  
break; DmAMr=p  
  PQAN�,d  
// Destructor of Guardautomatically ^!�^8]u<Q  
// releases the lock, regardless of w2y{3O"p=  
// how we exit this block! $5nOi�aQL  
} jTk !wm=  
} .(Q3M0.D  
  :pQZ) bF  
// Destructorof <stream> automatically OI�B~W  
// closes down<h>. w C]yE/P1  
return 0; [email protected](k�  
} M$A"<5  
~$w-I/Q!  
注意上面的程式碼是怎樣解決 2.2.2所示程式碼的各種問題的。例如, SOCK_Stream Guard的析構器會分別關閉 socket控制代碼和釋放 Thread_Mutex,而不管程式碼塊是怎樣退出的。同樣地,此程式碼要容易移植和維護得多,因為它沒有使用平臺特有的 API Bn*D<<{T  
2.2.10 已知應用 sJtz{'  
  本論文中的例子聚焦於併發網路程式設計。但是,包裝外觀模式已被應用到其他的許多領域,比如 GUI構架和資料庫類庫。下面是包裝外觀模式的一些廣為人知的應用: &O5%6Sv3d  
mJ0nyjX^  
Microsoft Foundation Class MFC ): MFC提供一組封裝大多數低階 C Win32 API的包裝外觀,主要集中於提供實現 Microsoft文件 /模板體系結構的 GUI元件。 2J�rr;"r  
2;.7c+r0  
ACE 構架: 2.2.8描述的互斥體、執行緒和 socket的包裝外觀分別基於 ACE構架中的元件 [7] ACE_Thread_Mutex ACE_Thread_Manager ACE_SOCK*類。 csT_!sII  
nab:y(]$/  
Rogue Wave 類庫: Rogue Wave Net.h++ Threads.h++類庫在許多 OS平臺上實現了 socket、執行緒和同步機制的包裝外觀。 (.7_`T6QG  
/^SL Zhe  
ObjectSpace System<Toolkit> 該工具包也提供了 socket、執行緒和同步機制的包裝外觀。 P"W$ZX  
D:'|poH  
Java 虛擬機器和 Java 基礎類庫: Java虛擬機器( JVM)和各種 Java基礎類庫,比如 AWT Swing,提供了一組封裝大多數低階的本地 OS系統呼叫和 GUI API的包裝外觀。 ^ #e:q  
T=:&W 3  
2.2.11 效果 z.oDH<1  
  包裝外觀模式提供以下好處: 3WS`,}  
h>$,97EU  
更為簡潔和健壯的程式設計介面: 包裝外觀模式在一組更為簡潔的 OO類方法中封裝許多低階函式。這減少了使用低階函式和資料結構開發應用的枯燥性,從而降低了發生程式設計錯誤的潛在可能性。 ULMG"."IH  
)ruC_)  
改善應用可移植性和可維護性: 包裝外觀類的實現可用以使應用開發者與低階函式和資料結構的不可移植的方面遮蔽開來。而且,通過用基於邏輯設計 實體(比如基類、子類,以及它們的關係)的應用配置策略取代基於物理設計 實體(比如檔案和 #ifdef)的策略 [6],包裝外觀模式改善了軟體結構。一般而言,根據應用的邏輯設計、而不是物理設計來理解和維護它們要更為容易一些。 j2deb`GD�  
-vwkvNn8  
改善應用的模組性、可複用性和可配置性: 通過使用像繼承和引數化型別這樣的 OO語言特性,包裝外觀模式建立的可複用類元件可以一種整體方式被“插入”其他元件,或從中“拔出”。相反,不求助於粗粒度的 OS工具,比如連結器或檔案系統,替換成組的函式要難得多。 Y'S�xehx  
A{/7HV�5  
包裝外觀模式有以下缺點: W&}YMb  
=|�S8.|r+  
額外的間接性( Indirection ): 與直接使用低階的函式和資料結構相比,包裝外觀模式可能帶來額外的間接。但是,支援內聯的語言,比如 C++,可以無需顯著的開銷而實現該模式,因為編譯器可以內聯用於實現包裝外觀的方法呼叫。 `23&vGk}  
2.2.12 參見 *aS|4M-  
  包裝外觀模式與外觀模式是類似的 [8]。外觀模式的意圖是簡化子系統的介面。包裝外觀模式的意圖則更為具體:它提供簡潔、健壯、可移植和可維護的類介面,封裝低階的函式和資料結構,比如本地 OS互斥體、 socket、執行緒和 GUI C語言 API。一般而言,外觀將複雜的類關係隱藏在更簡單的 API後面,而包裝外觀將複雜的函式和資料結構關係隱藏在更豐富的類 API後面。 4?g~GI3  
  如果動態分派被用於實現包裝外觀方法,包裝外觀模式可使用橋接模式 [8]來實現;包裝外觀方法在橋接模式中扮演抽象 Abstraction)角色。 b*qkox;j  
n'7�3DApW  
2.3 結束語 &1893#V  
  本論文描述包裝外觀模式,並給出了詳細的例子演示怎樣使用它。在本論文中描述的 ACE包裝外觀元件的實現可在 ACE[7]軟體釋出中自由獲取( URL http://www.cs.wustl.edu/~schmidt/ACE.html )。該釋出含有在聖路易斯華盛頓大學開發的完整的 C++原始碼、文件和測試例子驅動程式。目前 ACE正在用於許多公司(像 Bellcore、波音、 DEC、愛立信、柯達、朗訊、摩托羅拉、 SAIC和西門子)的通訊軟體專案中。 <<LmO-92  
感謝 f( �hK>H  
  感謝 Hans Rohnert Regine Meunier Michael Stal Christa Schwanninger Frank Buschmann Brad Appleton,他們的大量意見極大地改善了包裝外觀模式描述的形式和內容。 rw8O<No4.o  
gWqmK/.U.0  
參考文獻 9z/q_0&i  
[1] F.Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and M. Stal, Pattern-OrientedSoftware Architecture - A System of Pattern s. Wiley and S*****, 1996. *pw:oTO  
[2]W.R.Stevens,UNIX Network Programming, First Editio n. Englewood Cliffs,NJ: Prentice Hall, 1990. a+)Yk8%KY  
[3] J.Eykholt, S. Kleiman, S. Barton, R. Faulkner, A. Shivalin-giah, M. Smith, D.Stein, J. Voll, M. Weeks, and D. Williams, “Beyond Multiprocessing...Multithreading the SunOS Ker-nel,” in Proceedings of the Summer USENIXConferenc e,(San Antonio, Texas), June 1992. 03J,NXs  
[4]W.R.Stevens,UNIX Network Programming, Second Editio n. Englewood Cliffs,NJ: Prentice Hall, 1997. (' /S~  
[5] D. C.Schmidt, “IPC SAP: An Object-Oriented Interface to Interprocess Communicati*****ervices,” C++ Repor t,vol.4, November/December 1992. Run)E*sf  
[6] J.Lakos, Large-scale Software Development with C+ +. Reading, MA:Addison-Wesley, 1995. /#20`;~F)  
[7] D. C.Schmidt, “ACE: an Object-Oriented Framework for Developing DistributedApplicati*****,” in Proceedings of the 6th USENIX C++ TechnicalConferenc e, (Cambridge, Mas-sachusetts), USENIX Association, April 1994. 6#7f^uIK  
[8] E.Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Pat-terns: Elements ofReusable Object-Oriented Softwar e. Read-ing, MA: Addison-Wesley, 1995. )9##mUt'}  
[9] D. C.Schmidt, “Acceptor and Connector: Design Patterns for InitializingCommunication Services,” in Pattern Languages of Program Design (R.Martin, F. Buschmann, and D. Riehle, eds.), Reading, MA: Addison-Wesley, 1997. c~ l$_A  
[10] H.Mueller, “Patterns for Handling Exception Handling Suc-cessfully,” C++ Repor t,vol. 8, Jan. 1996. #l2wF> 0  
[11] D. C.Schmidt, T. Harrison, and N. Pryce, “Thread-Specific Storage – An ObjectBehavioral Pattern for Accessing per-Thread State Efficiently,” C++ Repor t,vol.9,Novem-ber/December1997. UY*Hc  
[12]Bjarne Stroustrup, The C++ Programming Language, 3rd Editio n.Addison-Wesley, 1998. n 0 _:!]k^  
s!nFc{  
:RzcK>Gub=  
This file is decompiled by an unregistered version of ChmDecompiler. PkF B.  
Regsitered version does not show this message. v+znKpE  
You can download ChmDecompiler at :     [url]http://www.zipghost.com/ [/url] 

相關推薦

2 包裝外觀Wrapper Facade用於封裝函式結構模式

可採取下面的步驟來實現包裝外觀模式: t 9t '9   lX-i�<0`  1. 確定現有函式間的內聚的抽象和關係: 像 Win32、 POSIX或 X Windows這樣被實現為獨立的函式和資料結構的傳統 API提供許多內聚的抽象,比如用於網路程式設計、同步和執行緒,以及 GUI管理的機制

三部分 進階篇-2 CC2530 BasicRF無線點燈

1 理論分析 1.1 Light_Switch工作流程 無線點燈的工作過程:啟動、發射、接收。【本實驗的重點就在Application層】  啟動 (1)板載外設、射頻IO、系統時鐘、中斷等初始化;確保外圍器件沒有問題;(halBoardInit(

設計模式C++實現包裝外觀模式Wrapper Facade

包裝器外觀模式(Wrapper Facade)把現有的非面向物件的API所提供的函式和資料,封裝在更加簡潔的、健壯的、可移植的、可維護的和內聚的面向物件的類介面中。 一般通過兩種方式實現跨平臺: 1

《演算法導論》-2節_練習參考答案

演算法導論(第三版)參考答案:練習3.2-1,練習3.2-2,練習3.2-3,練習3.2-4,練習3.2-5,練習3.2-6,練習3.2-7,練習3.2-8 Exercise 3.2-1 Show that if f(n) and g(n) are m

2017.12.8 軟件工程----- 總體設計復習

中一 計劃 整體 推薦 滿足 集中 用戶 重要 文檔 軟件工程-----第五章 總體設計(復習) (1)概要 經過需求分析階段的工作,系統必須“做什麽”已經很清楚了,現在是決定“怎樣做”的時候了。總體設計的基本目的是系統應該如何實現。他最重要的一項工作是設計軟件結構。因此,

hiho 2周 Trie樹字典樹

oid syn one ++ tac col splay str gif 裸字典樹。AC自動機前綴技能 1 #include <set> 2 #include <map> 3 #include <queue> 4

概論論與數理統計嚴繼高版習題答案含過程

com 概論 img 9.png ima mage bubuko 技術 image 第八題在下一頁 概論論與數理統計嚴繼高版第六章習題答案(含過程)

概率論與數理統計嚴繼高版習題答案含過程

src mage 習題答案 .com 概率 技術分享 統計 http com 無7.3(不考)總習題我只有草稿,忘記帶了,想起來就更 概率論與數理統計嚴繼高版第七章習題答案(含過程)

JavaSE習題 執行緒未完成

問答題 1.執行緒和程序是什麼關係?   程序是程式的一次動態執行,對應了從程式碼載入,執行至執行完畢的一個完整的過程   執行緒是比程序更小的執行單位,一個程序在其執行過程中可以產生多個執行緒,形成多條執行線索 2.執行緒有幾種狀態?   4種,新建,執行,中斷,死亡 3.引起執行緒中斷的常見原

【練習題】--互動設計Think Python

1.寫一個函式叫做square(譯者注:就是正方形的意思),有一個名叫t的引數,這個t是一個turtle。用這個turtle來畫一個正方形。寫一個函式呼叫,把bob作為引數傳遞給square,然後再執行這個程式。 code: import turtle def square(t): &n

【練習題】--迭代Think Python

相比之下,與其對比x和y是否精確相等,倒不如以下方法更安全:用內建的絕對值函式來計算一下差值的絕對值,也叫做數量級。 if abs(y-x) < epsilon: break 這裡可以讓epsilon的值為like 0.0000001,差值比這個小就說明已經足夠接近了。

【練習題】--條件迴圈Think Python

//--地板除。例:5//4=1 %--求模。例:5//3=2 如果你用Python2的話,除法是不一樣的。在兩邊都是整形的時候,常規除法運算子/就會進行地板除法,而兩邊只要有一側是浮點數就會進行浮點除法。 複合語句中語句體內的語句數量是不限制的,但至少要有一個。有的時候會遇到一個語句體

《機器學習》 周志華學習筆記 決策樹課後習題python 實現

一、基本內容 1.基本流程 決策樹的生成過程是一個遞迴過程,有三種情形會導致遞迴返回 (1)當前節點包含的yangben全屬於同一類別,無需劃分; (2)當前屬性集為空,或是所有yangben在所有屬性上的取值相同,無法劃分; (3)當前結點包含的yangben集合為空,不能

《機器學習》 周志華學習筆記 線性模型課後習題python 實現

線性模型 一、內容 1.基本形式 2.線性迴歸:均方誤差是迴歸任務中最常用的效能度量 3.對數機率迴歸:對數機率函式(logistic function)對率函式是任意階可導的凸函式,這是非常重要的性質。 4.線性判別分析(LDA 是一種降維的方法) 5.多分類學習:

《機器學習》 周志華學習筆記 整合學習課後習題python實現

  1.個體與整合 1.1同質整合 1.2異質整合 2.boosting:代表AdaBoost演算法 3.Bagging與隨機森林 3.1Bagging 是並行式整合學習方法最著名的代表(基於自主取樣法bootstrap sampling) 自己學習時編寫了

《機器學習》 周志華學習筆記 神經網路課後習題 python實現

1.神經元模型 2.感知機與多層網路 3.誤差逆傳播演算法 (A)BP演算法:最小化訓練集D上的累積誤差 標準BP演算法:更新規則基於單個Ek推導而得 兩種策略防止過擬合:(1)早停(通過驗證集來判斷,訓練集誤差降低,驗證集誤差升高)(2) 正則化:在誤差目標函式中引入描述網

周志華 西瓜書 16 強化學習習題答案

  原文轉自: https://blog.csdn.net/icefire_tyh/article/details/53691569                  

Essential c++ 異常處理exception handling課後練習

練習7.1 請找出以下函式中所有可能發生錯誤的地方。 int *alloc_and_init(string file_name) { ifstream infile(file_name.c_str()); int elem_cnt; infile >> elem_c

讀書筆記 ---- 《計算機網路—謝希仁7版》---- 4 網路層上篇

上一篇:資料鏈路層:https://blog.csdn.net/pcwl1206/article/details/83863677 下一篇:網路層下篇:https://blog.csdn.net/pcwl1206/article/details/84098381 本章節目錄: 4.1&n

《Java多執行緒程式設計實戰》—— 8 Active Object主動物件模式

Active Object模式是一種非同步程式設計模式。(跟Promise模式有什麼區別呢?) 通過對方法的呼叫與方法的執行進行解耦來提高併發性。 類圖 當Active Object模式對外暴露的非同步方法被呼叫時,與該方法呼叫相關的上下文資訊,包括被呼叫的非同步方法名、引數等,會被