Wrapper Facade: Type mode for package functions in the class Douglas C. Schmidt
1 Describe this paper describes the packaging model. The intent of this mode is to encapsulate low-level functions and data structures by the object-oriented (OO) interface. Examples of common packaging appearance mode are class libraries such as MFC, ACE, and AWT, which encapsulates local OS C APIs, such as Socket, PThreads, or GUI functions. Program directly for local OS C APIs makes network applications, unrestful, unmistable, and difficult to maintain, because application developers need to know many low-level, error-free details. This paper explains how packaging appearance mode makes these types of applications more simple, robust, portable, and maintenance. This thesis is organized as follows: 2 Detailed description Use the package appearance mode of Siemens format [1], 3 gives an end language. 2 Packaging Exterior Mode 2.1 intends to encapsulate low-level functions and data structures in a more concise, robust, portable and maintainable higher-level object-oriented interface interface. 2.2 Examples To explain the package appearance mode, consider the server of the distributed log service shown in Figure 2-1. The client application uses the log service to record information about their execution status in the distributed environment. These status information typically include error notifications, debugging tracking, and performance diagnostics. The logging is sent to the central log server, which is written to various output devices, such as the network management console, printer, or database.
Figure 1 Distributed Log Service
The log server shown in Figure 1 handles the connection request and log record sent by the customer. Logging and connection requests can be reached on multiple Socket handles. Each handle identifies network communication resources managed in the OS. Customer uses a connection protocol like TCP [2] to communicate with the log server. Thus, when the customer wants to log data, it must first send a connection request to the log server. The server uses the handle factory to accept the connection request, and the handle factory listens on the network address known by the customer. When the connection request arrives, the OS handle factory accepts the client's connection and creates the Socket handle indicating the connection endpoint of the client. The handle is returned to the log server, the latter waits for the client log request on this handle and other handles. Once the customer is connected, they can send logs to the server. The server receives these records, processes records, and writes them to their output devices through a connected Socket handle. The common way to develop concurrent log servers for multiple customers is to use low C language functions and data structures to complete operations such as thread, synchronization, and network communication. For example, Figure 2 demonstrates how to use Solaris Threads [3] and Socket [4] Network Programming API to develop multi-threaded log servers.
Figure 2 Multi-threaded log server
In this design, the handle factory of the log server accepts the customer network connection in its main thread. It is then derived a new thread and runs the logging_handler function in a separate connection to handle logging from each customer. The following two C functions demonstrate how to use the local Solaris OS API of Socket, muters, and threads to implement this log server design.
// At file scope.// Keep track of number of logging requests.static int request_count; // Lock to protect request_count.static mutex_t lock; // Forward declaration.static void * logging_handler (void *); // Port number to listen on for requests.static const int logging_port = 10000;. // main driver function for the multi-threaded // logging server Some error handling has been // omitted to save space in the example.int main (int argc, char * argv []) {struct sockaddr_in sock_addr;. // Handle UNIX / Win32 portability differences # if defined (_WINSOCKAPI_) SOCKET acceptor; #elseint acceptor; #endif / * _WINSOCKAPI_ * / // Create a local endpoint of communication.acceptor = socket (PF_INET, SOCK_STREAM, 0); // Set up the address to become a server.memset (reinterpret_cast
It receives and processs logging on each connection, as shown below: // entry points processes logging records for // one client connection.void * logging_handler (void * arg) {// handle unix / win32 portability Differences. # IF Defined (_winsockapi_) socket h = reinterpret_cast Generally speaking, the code to write and maintain is very boring, often contains subtle and harmful errors. For example, the code created and initialized in the main function of 2.2 is easy to erroneously erroneously erroneous, such as clearing the SOCK_ADDR, or does not use Htons [5] for the Logging_Port number. Mutex_lock and mutex_unlock are also easily misused. For example, if the Write call returns -1, the logging_handler code will not release the mutex lock and jump out of the loop. Similarly, if the nesting for loop is returned when an error is encountered, the Socket handle h will not be turned off. Lack of portability: Software written using low-level functions and data structures can often be transplanted between different OS platforms and compilers. Moreover, they often do not transplant in different versions of the same OS or compiler. Unavailability stems from hidden information in a function of low-level API and data structures. For example, the log server in 2.2 has hardly encoded the dependence on several non-portable local OS threads and network programming C APIs. In particular, the use of THR_CREATE, MUTEX_LOCK and MUTEX_UTEX_LOCK and MUTEX_UNLOCK cannot be ported to the non-Solaris OS platform. Similarly, specific Socket features, such as using Int represents the Socket handle, cannot be ported to non-Unix platforms like Win32 WINSOCK; Winsock represents the Socket handle as a pointer. High maintenance overhead: C and C developers are usually obtained by explicitly increase the conditional compilation instructions in their application source code using #ifdef. However, the use of conditional compilation to handle platform-specific variants increase the physical design complexity of application source code [6]. Developers are difficult to maintain and extend such software because the platform unique implementation details are dispersed in all parts of the application source file. For example, WIN32 and UNIX portability of the Socket data type (that is, socket vs. int) hinder the readability of the code. Developers who make programming such as low-level C APIs must be familiar with many OS platform features to write and maintain the code. Due to these disadvantages, it is often not a valid design choice by programming the low-level functions and data structures. 2.5 Solution To ensure that an effective way to access low-level functions and data structures is to use packaging appearance mode. For each set of related functions and data structures, one or more packaging appearance classes are created, and the low-level functions and data structures are encapsulated in the packaging appearance interface. 2.6 Structure of Participants in Structure Packaging Models Demo in the following UML class diagram: Key Participants in the packaging model include: Function: Function is an existing low-level function and data structure, which provides consolidation Cohesive service. Packaging appearance (Wrapper Fa? ADE): The appearance of the packaging is a package function and one or a set of classes of the data structure associated therewith. The method provided by the packaging is forwarded to one or more lower functions. 2.7 Dynamic Characteristics The following figure shows various collaboration in the packaging appearance mode: As mentioned below, these collaboration is very simple: 1. CLIENT INVOCATION: Customer calls the method through the instance of the packaging appearance. 2. Forwarding: The packaging look forward to forward the request to the one or more underlying functions of it package, and transfer any internal data structure required for the function. 2.8 Implementation This section explains the steps involved in the components and applications through the packaging appearance mode. We will explain how these packaging appearances have overcome the cumbersome, unachable procedures, lack of portability, and high maintenance overhead; these problems have torture solutions using low-level functions and data structures. The examples described herein are based on the log servers described in 2.2, and Figures 2-3 demonstrate the structure and participants in this example. The example in this section applies a reusable component from the ACE framework [7]. ACE provides a rich set of rich C packaging and framework components to complete common communication software tasks across extensive OS platforms. Figure 3 Multi-threaded log servers The following steps can be taken to implement packaging model: 1. Determine the abstraction and relationship between existing functions: a traditional API that is implemented as a stand-alone function and data structure like Win32, POSIX or X Windows. Abstract, such as network programming, synchronization, and threads, and GUI management mechanisms. However, due to lack of data abstract support in low-level languages like C, developers often do not immediately understand how these existing functions and data structures are associated with each other. Therefore, the first step in applying the packaging appearance is to determine the abstraction and relationship between the lower level functions in the existing API. In other words, we define an "object model" by aggregating existing low-level API functions and data structures into one or more classes. In our log case, we started a carefully checking our original log server. This implementation uses many low-level functions, which are actually provided with several consolidated services, such as synchronous and network communication. For example, Mutex_lock and Mutex_unlock functions are associated with mutex synchronization abstraction. Similarly, Socket, Bind, Listen and Accept functions play a variety of roles of network programming abstraction. 2. Polymerize the inner function group into the package appearance class and method: This step can be divided into the following sub-step: In this step, we define one or more packages for each set of compact abstraction functions and data structures. Appearance. A. Create a category: We start from each set of confineable functions and data structures to define one or more packaging appearances. Several common standards for creating categories of consolidation include: • Merging a function of high cohesion (cohesion) into an independent class while minimizing unnecessary coupling between the class. • Determine what is generic in the underlying function is a variable and puts the function packet into class, thereby isolating the change in a unified interface. In general, if the original API contains a wide variety of related functions, it is possible to create several packaging appearances to properly divide the transaction. B. Multiple independent functions are incorporated in: In addition to the existing function packet into classes, it is often beneficial in the method of less in combination in each package class. For example, to ensure that a set of low-level functions is called in an appropriate order, this design may be used. C. Select an indirect level: Most packaging appearances simply call their method to directly forward the low-level functions of the underlying. If the package appearance method is inline, there may be no additional indirect hierarchy compared to directly calling low-level functions. In order to enhance scalability, it is also possible to increase additional indirect hierarchy by dynamically assigning packaging appearance methods. In this case, the fabrication of the abstract (Abstract) role in the bridge mode [8] is placed. D. Determine where to deal with platform-specific variants: Make the platform-specific application code to minimize the importance of using packaging appearance mode. Thus, although the implementation of the packaging appearance method can be different on different OS platforms, they should provide a unified, platform-independent interface. A strategy for processing platform unique variants is to use #ifdef in the implementation of the packaging appearance method. When using #ifdef and automatic configuration tools (such as GNU AutoConf), you can create a unified, not dependent on the platform's packaging appearance. Another optional policy is to decompose different packaging appearances in a directory (for example, there is a directory for each platform), and configure language processing tools to include appropriate packaging appearance classes when compiling. in. Choosing a specific policy depends to a large extent on the frequency of change in the package appearance method. For example, if they frequently change, the #ifdef can be properly updated for each platform may be monotonous. Similarly, all files that depend on the file may need to be recompiled, even if there is a change only for one platform. In our log example, we will define packaging appearances for muters, sockets, and threads to demonstrate how each step is implemented. As shown below: · Mutex packaging: We first define thread_mutex abstraction, package Solaris mutex function in unified and portable class interface: Class thread_mutex {public: thread_mutex (void) {mutex_init (& Mutex_, 0, 0 ?);} Thread_Mutex (void) {mutex_destroy (& mutex_);} int acquire (void) {return mutex_lock (& mutex_);} int release (void) {return mutex_unlock (& mutex_);} private: // Solaris-specific Mutex mechanism .mutex_t mutex_; // = Disallow copying and assignment.Thread_Mutex (const Thread_Mutex &); void operator = (const Thread_Mutex &);}; by defining the Thread Mutex class interface, and subsequently written to use it, rather than lower the local OS C API Applications, we can easily transplant our packaging to other platforms. For example, following the Thread Mutex implemented work on Win32: class Thread_Mutex {public:? Thread_Mutex (void) {InitializeCriticalSection (& mutex_);} Thread_Mutex (void) {DeleteCriticalSection (& mutex_);} int acquire (void) {EnterCriticalSection (& mutex_); return 0;} int release (void) {LeaveCriticalSection (& mutex_); return 0;} private: // Win32-specific Mutex mechanism.CRITICAL_SECTION mutex_; // = Disallow copying and assignment.Thread_Mutex (const Thread_Mutex &); void operator = (const thread_mutex &);}; If we described earlier, we can use a single source tree to provide a single source tree by using #ifdef and automatic configuration tools (such as Gun AutoConf) in the Thread_Mutex method implementation. The platform-independent mutex abstract. Instead, we can also achieve different THREAD_MUTEX to break down into the separated directory, and indicate that our language processing tool contains appropriate versions to enter our app when compiling. In addition to improving portability, our Thread_Mutex packaging appearance also provides a mutex interface than the direct programming low Solaris function and Mutex_t data structure is more inclusive. For example, we can use the C Private access control indicator to disable the copy and assignment of the mutex; such use is wrong, but it will not be blocked by less than a strong type C program API. · Socket packaging appearance: Socket API is much larger than Solaris mutex API, and there is also a lot of performance [5]. Therefore, we must define a set of related packaging appearances to encapsulate the socket. We will start from the Typedef that the following processing UNIX / WIN32 portability: #if! Defined (_winsockapi_) typef int design; #define invalid_handle_value -1 # endif / * _winsockapi_ * / Next, we will define the inet_addr class, Package Internet Domain Address Structure: Class INET_ADDR {public: inet_addr (u_short port, long addr) {// set up the address to become a server.memset (reinterpret_cast Likewise, accept the new factory methods accepted connections initialized SOCK_Stream, as follows: class SOCK_Acceptor {public: SOCK_Acceptor (const INET_Addr & sock_addr) {// Create a local endpoint of communication.handle_ = socket (PF_INET, SOCK_STREAM, 0) ; // Associate Address with endpoint.bind (Handle_, Sock_addr.addr (), SOCK_ADDR.SIZE ()); // Make Endpoint Listen for Connections.Listen (Handle_, 5);}; // Accept A connection and initialize / / The · It makes the error handle to cleanly decouple the error: For example, the error handling information does not explicitly pass to the operation. Moreover, the application does not ignore the exception because there is no check function to return values. · It can be type secure: In languages like C and Java, it is thrown out and captured in a strongly type, to enhance the organization and correctness of the error handling code. The compiler will ensure the correct processor for each type of exception is performed relative to the explicitly checking the thread. However, there are several disadvantages for the use of abnormalities for packaging appearance: • It is not common: not all languages provide exception handling. For example, some C compilers do not have an exception. Similarly, when the OS provides an abnormal service, they must be supported by the language extension, thereby reducing the transplantability of the code. · It makes it complicated in multiple languages: Instead, use an integer or structure to report error messages to provide a more common solution. · It makes resource management complicates: If there are multiple exit paths in the C or Java code block, resource management may become complex [10]. Thus, if language or programming environments do not support garbage collection, you must take care of ensuring that the dynamically assigned object is removed when there is abnormally thrown out. · It has potential time and / or space inefficient possibility: Even if there is no abnormality thrown out, the bad implementation of abnormal processing will also bring overhead overhead of time and / or space [10]. Such overhead may be particularly problematic for embedded systems that must have efficient and low memory occupancy characteristics. The disadvantage of abnormal processing is also particularly problematic for packaging appearances for encapsulated internal nuclear-level device drivers or low-level OS APIs (they must be ported to many platforms). For these types of packaging appearance, more portable, efficient, and thread safety processing errors are information that define an error processor abstraction, explicitly maintains success or failure of operations. Use thread-specific storage (Thread-Specific Storage) mode [11] is a solution that is widely used for these system-level packaging appearances. 1. Define the associated assistant class (optional): Once the low-level function and the data structure are encapsulated in the widget, it is often possible to create other helper classes to further simplify application development. These assistants have become significant after the package appearance mode has been applied to the category of the low-level function and the data associated with it. For example, in our log example, we can effectively utilize the KUARD class that implements C ScopeD Locking idios; this idiom ensures that thread_mutex is properly released, regardless of how the program's control flow exits the action. Template As shown below: // ... {// Constructor of It receives and processs logging on each connection, as shown below: // entry point what processes logging records for // one client connection.void * logging_handler (void * arg) {socket h = reinterpret_cast 2.10 It is known to apply the example in this paper to focus on concurrent network programming. However, the packaging model has been applied to many other areas, such as the GUI framework and database class library. Below is some widely known applications of packaging appearance mode: Microsoft Foundation Class (MFC): MFC provides a set of packaging appearances of a set of packages, mainly focusing on providing a GUI component that implements Microsoft document / template architecture. ACE Frame: 2.8 Described muters, threads and socket packages are based on components in the ACE framework, respectively [7]: ACE_THREAD_MUTEX, ACE_THREAD_MANAGER, and ACE_SOCK * class. Rogue Wave Category Library: Rogue Wave Net.h and Threads.h Class libraries implements the packaging appearance of Socket, threads, and synchronization mechanisms on many OS platforms. Objectspace System 2.12 See the exterior mode and appearance mode is similar [8]. The intent of the appearance mode is to simplify the interface of the subsystem. The intent of the packaging model is more specific: it provides simple, robust, portable and maintainable type interface, low-level functions and data structures, such as local OS muters, sockets, threads, and GUI C language APIs. In general, the appearance hides complex class relationships behind the simpler API, while the packaging appearance hides complex functions and data structure relationships behind the richest class API. If dynamic assignments are used to implement packaging appearance methods, packaging appearance mode can be implemented using bridge mode [8]; packaging appearance method plays an abstraction role in bridge mode. 3 Conclusion This paper describes the packaging model and gives a detailed example demonstration how to use it. The implementation of the ACE packaging appearance assembly described in this paper can be freely acquired in the ACE [7] software release (URL: http://www.cs.wustl.edu/~ Schmidt/ace.html). This release contains a complete C source code, documentation, and test example driver developed in the University of St. Louis Washington. At present, ACE is using many companies (like Bellcore, Boeing, Dec, Ericsson, Kodak, Lucent, Motorola, SAIC and Siemens) communication software projects. Thank you for thanks to Hans Rohnert, Regine Meunier, Michael Stal, Christa Schwanninger, Frank Buschmann and Brad Applet, which greatly improves the form and content of packaging appearance mode description. Reference [1] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, And M. Stal, Pattern-Oriented Software Architecture - a System of patterns. Wiley and Sons, 1996. [2] Wrstevens, UNIX NetWork Programming, First et Edition. Englewood Cliffs, NJ: Prentice Hall, 1990. [3] J. Eykholt, S. Kleiman, S. Barton, R. Faulkner, A. ShiValin-Giah, M. Smith, D. Stein, J Volliams, "Beyond Multiprocessing ... Multithreading The Sunos Ker-Nel," in Proceedings of The Summer Usenix Conference, (San Antonio, Texas), June 1992. [4] Wrstevens, UNIX Network Programming, Second Edition Englewood Cliffs, NJ:. Prentice Hall, 1997. [5] DC Schmidt, "IPC SAP: An Object-Oriented Interface to Interprocess Communication Services," C Report, vol.4, November / December 1992. [6] J. Lakos, Large-scale Software Development with C Reading, MA:. Addison-Wesley, 1995. [7] DC Schmidt, "ACE: an Object-Oriented Framework for Developing Distributed Applications," in Proceedings of the 6th Usenix C Technical Con Ference, (Cambridge, Mas-Sachusetts, Usenix Association, April 1994. [8] E. Gamma, R. Helm, R. Johnson, And J. Vlissides, Design Pat-Terns: Elements of Reusable Object-Oriented Software. Read -ing, MA: Addison-Wesley, 1995. [9] DC Schmidt, "Acceptor and Connector: Design Patterns for Initializing Communication Services," in Pattern Languages of Program "(R. Martin, F. Buschmann, And D. Riehle, EDS.), Reading, MA: Addison-Wesley, 1997. [10] H. Mueller, "Patterns for Handling Exception Handling Suc-CessFully," C Report, Vol. 8, Jan. 1996. [11] DC Schmidt, T Harrison, "Thread-Specific Storage - An Object Behavioral Pattern for Accessing Per-Thread State EfficIntly," C