Table of Contents

Object-Oriented Network

 Overview

Programming with C++

{ Recognizing patterns

{ Typ es of patterns

Design Patterns for Initializing

 Passively initiali zi ng network services

Network Services in OO

{ Overview of tactical patterns

Communication Frameworks

{ Overview of strategic patterns

 Implementing the Acceptor pattern

Douglas C. Schmidt

{ First implementation

[email protected] u

{ Revised implementation

Washington University

 Summary

2 1

Identifying Common Behavior and

Structure

Intro duction

 \Pattern recognition" o ccurs by observing

 capture successful solutions

recurring solution strategies

to recurring problems that arise during soft-

ware development

 e.g., network servers often lo ok as follows:

 Useful design patterns and design pattern

void do_server void {

descriptions, like useful software, evolve over

listener_handle.initialize ;

time

for ;; {

{ Likewise, patterns build up on other patterns to

// Create service to handle connection.

form families of related patterns

service = new Service;

// Wait for and accept connection.

client_handle = listener_handle.accept ;

 The following material captures the evolu-

// Activate service.

tion of design patterns that are useful for

service->open client_handle;

managing the lifecycle of services in OO

// Perform service.

communication software

service->run ;

}

}

3 4

Identifying Limitations with the

Strategic and Tactical Patterns

Existing Solution

 Strategic design patterns have an extensive

impact on the software architecture

 The solution ab ove is limit ed by tightly cou-

pling:

{ Typically oriented to solutions in aparticular do-

main

{ Choice of IPC mechanisms

{ Connection and service handling strategies

{ e.g., Reactor and Acceptor pattern in the domain

of event-driven, connection-oriented communica-

{ Choice of demultiplexing strategy

tion software

{ Choice of advertisement strategy

{ Choice of endp oint listening strategy

 Tactical design patterns have a relatively lo-

calized impact on a software architecture

{ Choice of service creation strategy

{ Typically domain-indep endent

{ Choice of connection acceptance strategy

{ e.g.,Wrapp er, Adapter, Bridge, Factory Metho d,

{ Choice of service concurrency strategy

and Strategy

 Strategic and tactical design patterns re-

 It is imp ortant to understand b oth typ es of

move these limitati ons and decrease cou-

patterns

pling

6 5

Decoupling IPC Mechanisms

 Problem

The Wrapp er Pattern

{ IPC mechanisms likesockets, TLI, and STREAM

pip es have non-uniform interfaces, even though

they b ehavior similarly

 Intent

{ Encapsulate lower-level functions within typ e-safe,

mo dular, and p ortable class interfaces

 Key forces

{ Many IPC mechanisms or subsets of mechanisms

provide logically equivalent services even though

 This pattern resolves the following force that

their interfaces are physically di erent

arises when using native C-level OS APIs

. e.g., TLI vs. so ckets

1. How to avoid tedious, error-prone, and non-p ortable

programming of low-level IPC mechanisms

{ Many IPC mechanisms are written in low-level C

co de

2. How to combine multiple related, but indep endent,

functions into a single cohesive abstraction

 Solution

{ Use the Wrapp er and Adapter patterns

7 8

Structure of the Wrapp er Pattern

The Intent 1: request () 

Wrapper Convert the of a class into another inter- client {

request() face client exp ects

. Adapter lets classes work together that couldn't

otherwise b ecause of incompatible interfaces

2: specific_request()

 This pattern resolves the following force that

rises when using conventional OS inter-

Wrappee a faces

specific_request()

1. `How to provide an interface that expresses the

similarities of seemingly di erent OS mechanisms

such as networking or lo cking

9 10

Decoupling Demultip lexi ng

Strategy

Structure of the Adapter Pattern

 Problem

Servers often must handle events from more than 1: request () { Target one source client request() = 0

A

 Key forces

{ It is inecient to rep eatedly \p oll" each event

source and it is incorrect to blo ck inde nitely on

Adapter Adaptee any one source of events specific_request()

request() 2: specific_request()

{ Extensibility is limited if the demultiplexing co de is

coupled with the application event handling co de

 Solution

{ Use the

12 11

Structure

The Reactor Pattern

select (handles) Concrete foreach h in handles loop

APPLICATION Event Intent  h->handle_event (type) APPLICATION

end loop Handler

Decouple event demultiplexing and event handler { - SPECIFIC

- rmed in resp onse

dispatching from the services p erfo INDEPENDENT to events Reactor handle_events() 1 n

register_handler(eh, type)

 This pattern resolves the following forces

r event-driven software: fo remove_handler(eh, type) Event Handler 1

1 handle_event(type)

How to demultiplex multiple typ es of events from 1. 1

multiple sources of events eciently within a single get_handle()

thread of control n Handles A

2. How to extend application b ehavior without requir-

ing changes to the event demultiplexing/dispatching

framework

 Participants in the Reactor pattern

14 13

Collab oratio ns

Decoupling Connection and

Handling Strategies callback : Service main Concrete reactor

program Event_Handler : Reactor Problem Reactor()  INITIALIZE

register_handler(callback)

Coupling initialization strategies with the service REGISTER HANDLER {

MODE

es it hard to change either one with- get_handle() handling mak EXTRACT HANDLE handle_events() out a ecting the other INITIALIZATION START EVENT LOOP select() FOREACH EVENT DO

handle_event()

Key forces EVENT OCCURS 

MODE

EVENT

Services tend to change more often than connec- REMOVE HANDLER handle_close() {

HANDLING

tion establishment strategies

 Solution

 Dynamic interaction among participants in

{ Use the Acceptor pattern

the Reactor pattern

16 15

The Acceptor Pattern

 Intent

Structure of the Acceptor Pattern

{ Decouple the passive initialization of a service from

the tasks p erformed once the service is initialized

Svc Handler

This pattern resolves the following forces

 Svc Handler Acceptor

r network servers using interfaces like so ck-

fo peer_stream_

or TLI: ets peer_acceptor_

open() ACTIVATES handle_event()

1. How to reuse passive connection establishment co de

for each new service

Reactor

2. How to make the connection establishment co de

rtable across platforms that may contain di er-

po handle_event()

ent IPC mechanisms

3. How to ensure that a passive-mo de descriptor is

not accidentally used to read or write data

4. How to enable exible strategies for creation, con-

nection establishment, and concurrency

18 17

Structure of the First Acceptor

Pattern Implementati on

First Implementati on of the

Concrete_Svc_Handler SOCK Stream

n SOCK_Acceptor

r Pattern Accepto 1 Concrete Concrete Svc Handler Acceptor LAYER

LAYER

The following slides illustrate an implemen-  ACTIVATES APPLICATION

APPLICATION

tation of the Acceptor pattern

{ This solution relies up on an implementation of the

PEER_STREAM

r pattern, as well as the Adapter pattern Reacto Svc SVC_HANDLER A PEER_ACCEPTOR Handler Acceptor A open()

handle_event()

 Subsequent slides will describ e limitati ons LAYER LAYER CONNECTION

CONNECTION

with this rst approach sh = new SVC_HANDLER; peer_acceptor.accept(*sh);

reactor->register_handler (sh);

 Other tactical patterns will be used to re-

Event

these limitati ons move Handler 1 Reactor LAYER LAYER handle_event() n REACTIVE

REACTIVE

19 20

Typical Acceptor Use-Case

Acceptor Class Interface

SERVER

 A factory for initial i zi ng network services LOGGING : Logging

passively DAEMON Acceptor

template : Logging

PEER_ACCEPTOR> // Accepts connections

class Handler

class Acceptor : public Event_Handler {

public: : Logging

Initialize and register with Reactor.

// Handler : Reactor

virtual int open const PEER_ACCEPTOR::PEER_ADDR &,

Reactor *;

// Creates, accepts, and activates SVC_HANDLER's.

LOGGING

virtual int handle_event HANDLE;

RECORDS

Demultiplexing hooks.

// LOGGING CONNECTION SERVER

HANDLE get_handle void const; virtual RECORDS REQUEST

CLIENT

private:

// Passive connection mechanism.

EPTOR peer_acceptor_;

PEER_ACC CLIENT CLIENT

// Event demultiplexor.

Reactor *reactor_;

};

22 21

Acceptor Use-case Co de

 Distributed logging server

Acceptor Class Implementation

// Shorthand names.

class Logging_Handler :

define SH SVC_HANDLER

public Svc_Handler

define PR_AC PEER_ACCEPTOR

{

public:

// Initialize the Acceptor

// Obtain HANDLE.

virtual HANDLE get_handle void const;

template int

Acceptor::open const PR_AC::ADDR &l_addr,

// Called back to process logging records.

Reactor *reactor

virtual int handle_event HANDLE;

{

};

this->reactor_ = reactor;

typedef Acceptor

// Forward to PEER_ACCEPTOR to advertise endpoint.

Logging_Acceptor;

this->peer_acceptor_.open l_addr;

int main void

{

// Register with Reactor to listen for connections.

Reactor reactor;

Logging_Acceptor acceptor;

this->reactor_->register_handler

this, READ_MASK;

acceptor.open LOGGER_PORT, &reactor;

}

for ;;

reactor.handle_events ;

}

23 24

Collab orati on in the Acceptor

Pattern

acc : Server sh: reactor :

Acceptor Svc_Handler Reactor

Factory that create, connects, and activates new // open()

INITIALIZE

single-threaded SVC_HANDLER objects. // register_handler(acc) REGISTER HANDLER

get_handle()

int

template EXTRACT HANDLE

::handle_event HANDLE Acceptor handle_events() PHASE START EVENT LOOP

ENDPOINT { FOREACH EVENT DO select()

INITIALIZATION

Create a new SVC_HANDLER. // handle_input() CONNECTION EVENT

sh = new SVC_HANDLER

LER *svc_handler = new SVC_HANDLER; SVC_HAND ALLOCATE AND accept(*sh) ACTIVATE OBJECT

register_handler(sh)

Accept connections from clients // REGISTER HANDLER PHASE FOR CLIENT I/O SERVICE get_handle()

EXTRACT HANDLE

er_acceptor_.accept *svc_handler; this->pe INITIALIZATION handle_input()

DATA EVENT

// Register SVC_HANDLER with Reactor. PROCESS MSG svc()

handle_close()

actor_->register_handler this->re CLIENT SHUTDOWN PHASE

SERVICE

ler, READ_MASK; svc_hand SERVER SHUTDOWN handle_close()

PROCESSING

}

 Acceptor factory creates, connects, and ac-

tivates a Svc Handler

26 25

Limitations with the First

Overcoming Limitations with

Solution

Patterns

 Several problems with the solution ab ove:

 To remove the limit ati ons describ ed ab ove,

1. Advertisement and listener strategies are hardco ded

we'll use various patterns to enhance our

Acceptor

2. Service handler creation strategy is tightly cou-

pled to dynamic allo cation and constructor-based

initialization

 Key patterns are Strategy, Bridge, Factory

Metho d, and Abstract Factory

3. Connection acceptance strategy is hard-co ded

{ These are \tactical" patterns that are widely ap-

plicable across most application domains

4. Service handler activation is not extensible and

concurrency strategy is overly restrictive

 Note that the new solution do es not break

existing co de

 This tight coupling makes it hard to extend

{ This is another imp ortant non-functional \force":::

the Acceptor to work in other contexts

27 28

Structure of the

The Strategy Pattern Intent  STRATEGY

Context Strategy

{ De ne a family of algorithms, encapsulate each

context_interface() algorithm_interface()

one, and make them interchangeable

A

. Strategy lets the algorithm vary indep endently from clients that use it Concrete Concrete Strategy A

Concrete Strategy C

 This pattern resolves the following force

algorithm_interface() Strategy B algorithm_interface()

How to extend the p olicies for adversizing, listen-

1. algorithm_interface()

ing, creating, accepting, and executing a service

hander without mo difying the core Acceptor algo-

rithm

29 30

The

Using the Strategy Pattern

 Intent SVC_HANDLER

PEER_ACCEPTOR SVC_HANDLER

Decouple an abstraction from its implementation

Acceptor {

wo can vary indep endently Concurrency so that the t handle_event() Strategy 2: activate_svc_handler(sh) activate_svc_handler()

A

 This pattern resolves the following force that

...

rises when building extensible software 1: activate_svc_handler(sh) a

... SVC_HANDLER

Howtoprovide a stable, uniform interface that is

SVC_HANDLER SVC_HANDLER Process 1.

, i.e., Reactive Thread Strategy b oth closed and op en

Strategy Strategy

{ Closed to prevent direct co de changes

{ Op en to allow extensibility

32 31

Using the Bridge Pattern

Structure of the Bridge Pattern

3: accept_svc_handler(sh) SVC_HANDLER A Acceptor PEER_ACCEPTOR 1: method_impl() accept_svc_handler() Accept Implementor accept_strategy_ Strategy Abstraction handle_event() method_impl() accept_svc_handler() method() A A A

... 2: accept_strategy_->accept_svc_handler(sh) SVC_HANDLER Concrete ... PEER_ACCEPTOR ImplementorA CLNS Strategy method_impl() Concrete 1: accept_svc_handler() accept_svc_handler() Refined ImplementorB Abstraction SVC_HANDLER method_impl() PEER_ACCEPTOR CONS Strategy

accept_svc_handler()

33 34

The Template Metho d

The Factory Metho d Pattern

 Intent

{ De ne the skeleton of an algorithm in an op era-

tion, deferring some steps to sub classes

 Intent

. Template Metho d lets sub classes rede ne cer-

{ De ne an interface for creating an object, but let

tain steps of an algorithm without changing the

sub classes decide which class to instantiate

algorithm's structure

. Factory Metho d lets a class defer instantiation

to sub classes

 This pattern resolves the following force

1. How to extend the strategies for creating the check sort

 This pattern resolves the following force in

algorithm

the Acceptor pattern:

1. How to extend each initialization strategy in the

Acceptor pattern indep endently and transparently

 Template Metho d is an alternative for the

Strategy/Bridge patterns

36 35

Using the Factory Metho d

Structure of the Factory Metho d

Pattern

Pattern

SVC_HANDLER Creator Creation A factory_method() = 0 Strategy make_product() A Svc make_svc_handler() Product A Handler A Product *product = factory_method() return product Concrete Svc Handler Concrete Demand Creator Strategy factory_method() Concrete Concrete Svc Handler make_svc_handler()

Product CREATES return new Concrete_Product CREATES

return new Concrete_Svc_Handler

37 38

Structure of the Abstract Factory

Pattern

The Intent  Abstract Concrete

Factory Factory_2

Provide an interface for creating families of related { make_product_A()

make_product_A() r dep endent objects without sp ecifying their con- o Abstract make_product_B() make_product_B()

crete classes Product_A A A

Product_A2

This problem resolves the following forces

 Product_A1

in the Acceptor pattern:

Product_B2

1. How to simplify the interface to the Acceptor and

eep it from having a large numb er of individual k Concrete

strategies Factory_1 Product_B1 make_product_A() Abstract

make_product_B() Product_B

2. How to ensure that the selected strategies actually

A

work together without con ict

39 40

Using the Abstract Factory

Pattern

Revised Acceptor Class Mo del

SVC_HANDLER PEER_ACCEPTOR SVC_HANDLER sh = make_svc_handler(); PEER_ACCEPTOR Strategy 2: accept_svc_handler (sh); Factory Logging_Handler activate_svc_handler (sh); Acceptor 1: advertise_svc() SOCK Acceptor open() make_listener() handle_event() make_creation_strategy() SVC_HANDLER Logging advertise_svc() make_concurrency_strategy() Creation make_svc_handler() ... Acceptor A Strategy activate_svc_handler() accept_svc_handler() Advertise make_svc_handler() make_listener() Strategy A ... advertise_svc() SVC_HANDLER A SVC_HANDLER PEER_ACCEPTOR Logging_Handler Dynamic Concurrency Listener SOCK Acceptor Accept Strategy Strategy Strategy Strategy Logging make_listener() activate_svc_handler() accept_svc_handler() A Strategies A A make_creation_strategy() Reactive make_concurrency_strategy() Strategy

...

41 42

Advantages of New Design

Structure of the Revised Acceptor

Pattern

 Decouple advertisement strategy

Concrete_Svc_Handler

e.g., use well-known p orts or name server n SOCK Stream { Concrete 1 SOCK_Acceptor Svc Handler Concrete Acceptor

LAYER

Decouple listening strategy open() 

APPLICATION

{ e.g., using Reactoror some other demultiplexor SVC_HANDLER INITS PEER_ACCEPTOR PEER_STREAM A

Svc Acceptor

Decouple service creation strategy Handler advertise_svc() 

make_listener()

e.g., dynamic allo cation vs singleton or dynamic

open() A make_svc_handler() { static linking accept_svc_handler() linking vs. activate_svc_handler() LAYER open()

CONNECTION handle_event()

 Decouple service connection acceptance strat-

sh = make_svc_handler(); egy

accept_svc_handler (sh);

e.g., connection-oriented vs. connectionless activate_svc_handler (sh); {

Event

Decouple service concurrency strategy Handler  1 Reactor

LAYER handle_event() n

{ e.g., single-threaded reactive vs. multi-threaded

REACTIVE

vs. multi-pro cess

43 44

Revised Acceptor Class Protected

Revised Acceptor Class Public

and Private Interface

protected:

Interface

// = Bridge methods default behavior delegates

template

class PEER_ACCEPTOR> // Accepts connections virtual int advertise_svc const PEER_ACCEPTOR::PEER_ADDR &;

class Acceptor : public Event_Handler { virtual int make_listener Event_Handler *;

public: virtual SVC_HANDLER *make_svc_handler void;

// = Initialization. virtual int accept_svc_handler SVC_HANDLER *;

virtual int open virtual int activate_svc_handler SVC_HANDLER *;

const PEER_ACCEPTOR::PEER_ADDR &,

Strategy_Factory *; private:

// = Strategy objects.

// = Factory that creates, connects, Advertise_Strategy

// and activates SVC_HANDLER's. *advertise_strat_;

virtual int handle_event HANDLE; Listener_Strategy *listen_strat_;

Creation_Strategy *create_strat_;

// = Demultiplexing hooks. Accept_Strategy

virtual HANDLE get_handle void const; *accept_strat_;

virtual int handle_close HANDLE, Reactor_Mask; Concurrency_Strategy

*concurrency_strat_;

};

45 46

Acceptor Class Implementation

// Shorthand names.

define SH SVC_HANDLER

define PA_AC PEER_ACCEPTOR

Strategy Factory Interface

define PA_AD PEER_ACCEPTOR::PEER_ADDR

// Initialize the Acceptor.

template

class PEER_ACCEPTOR> // Accepts connections

template int

class Strategy_Factory {

Acceptor::open

public:

const PR_AC::PEER_ADDR &local_addr,

Strategy_Factory

Strategy_Factory *strat_fact

Advertise_Strategy *,

{

Listener_Strategy *,

// Initialize the strategies.

Creation_Strategy *,

this->create_strat_ =

Accept_Strategy *,

strat_fact->make_creation_strategy ;

Concurrency_Strategy *;

this->accept_strat_ =

strat_fact->make_accept_strategy ;

// Factory methods called by Acceptor::open.

this->concurrency_strat_ =

Advertise_Strategy *make_advertise_strategy void;

strat_fact->make_concurrency_strategy ;

Listener_Strategy *make_listener_strategy void;

this->listen_strat_ =

Creation_Strategy

strat_fact->make_listener_strategy ;

*make_create_strategy void;

this->advertise_strat_ =

Accept_Strategy

strat_fact->make_advertise_strategy ;

*make_accept_strategy void;

Concurrency_Strategy

// Advertise the service.

*make_concurrency_strategy void;

this->advertise_svc local_addr;

// Make a listener.

this->make_listener this;

}

48 47

// Bridge method for creating a service handler.

template SH *

Acceptor::make_svc_handler void

// Implements the strategy for creating, connecting,

{

// and activating new SVC_HANDLER objects.

return this->creation_strategy_->make_svc_handler ;

}

template int

Acceptor::handle_event HANDLE

// Accept connections from clients can be overridden.

{

// Create a new SVC_HANDLER.

template int

Acceptor::accept_svc_handler SH *svc_handler

SH *svc_handler = this->make_svc_handler ;

{

return this->accept_strategy_->

// Accept connection from client.

accept_svc_handler svc_handler;

}

this->accept_svc_handler svc_handler;

// Activate the service handler can be overridden.

// Activate SVC_HANDLER.

template int

this->activate_svc_handler svc_handler;

Acceptor::activate_svc_handler SH *svc_handler

}

{

return this->concurrency_strategy_->

activate_svc_handler svc_handler;

}

49 50

Advertisement Strategies

Advertisement Strategy

ADDR Implementation s

Advertise

A template

wn_Address::advertise_svc

Strategy Well_Kno

const PA_AD &local_addr

advertise_svc() {

// Advertise the IP port and IP address.

vertise_endpoint local_addr;

ADDR ADDR this->ad }

Well-known X.500

Address Strategy template

per::advertise_svc

Strategy ADDR Port_Map

const PA_AD &local_addr

Portmapper {

Advertise using the portmapper

Strategy //

// ...

}

52 51

Listener Strategy

Listener Strategies

Implementation s

// Cache a Reactor.

PEER ACCEPTOR

template

_Listener_Strategy::Listener_Strategy

Listener Reactive *r

A Reactor

reactor_ r

Strategy : {

make_listener() }

// Register with a Reactor

int

Reactive_Listener_Strategy::make_listener

Event_Handler *h

PEER ACCEPTOR PEER ACCEPTOR {

this->handle_ = h->get_handle ;

actor_->register_handler h, READ_MASK; Reactive Threaded this->re Listener Listener }

Strategy

Remove from Reactor

Strategy //

Reactive_Listener_Strategy::~Reactive_Listener_Strategy

void

{

this->reactor_->remove_handler this->handle_;

}

53 54

Creation Strategy

Implementation s

SVC HANDLER Creation

// Make a Singleton SVC_HANDLER.

Strategies

template SH *

Singleton_Strategy::make_svc_handler void {

if this->svc_handler_ == 0

this->svc_handler_ = new SH;

SVC_HANDLER

return this->svc_handler_; // Pre-cached...

A Creation }

Strategy

// Make a new SVC_HANDLER on-demand.

SH *

make_svc_handler() template

Demand_Strategy::make_svc_handler void {

return new SH;

SVC_HANDLER }

Make a SVC_HANDLER by dynamically linking it.

SVC_HANDLER Dynamic //

SH *

Strategy template

Strategy::make_svc_handler void { Demand Dynamic_

SVC_HANDLER

Open the shared library.

Strategy //

*handle = void * dlopen this->shared_library_; Singleton void

Strategy

// Extract the factory function.

SH **factoryvoid = SH **void

dlsym handle, this->factory_function_;

// Call factory function to get new SVC_Handler.

return *factory;

}

55 56

SVC HANDLER Connection

Acceptance Strategies

Connection Acceptance Strategy Implementation SVC_HANDLER

PEER_ACCEPTOR

// Delegate to the accept method of the PEER_ACCEPTOR.

int Accept template

A

trategy::accept_svc_handler

Strategy Accept_S

SH *svc_handler

accept_svc_handler() {

return this->peer_acceptor_.accept *svc_handler;

}

// Implement "stateless connection" strategy

SVC_HANDLER

int

PEER_ACCEPTOR SVC_HANDLER template

ategy::accept_svc_handler

PEER_ACCEPTOR CLNS_Str

*svc_handler CONS SH

Strategy CLNS { ...

Strategy //

}

57 58

Concurrency Strategy

SVC HANDLER Concurrency

Implementation s

Strategies

// Activate the SVC_HANDLER by calling it's open method.

template int

_Strategy::activate_svc_handler

SVC_HANDLER Reactive

SH *svc_handler, void *arg

Concurrency {

Delegate control to the application-specific service

Strategy A //

// handler.

activate_svc_handler()

return svc_handler->open arg;

SVC_HANDLER }

SVC_HANDLER

Activate the SVC_HANDLER by first calling its open

Process //

method and then calling its activate method to turn

Reactive Strategy //

it into an .

Strategy //

template int

Thread_Strategy::activate_svc_handler

SVC_HANDLER SVC_HANDLER

SH *svc_handler, void *arg

Thread Thread {

Initialize SVC_HANDLER.

Strategy Pool //

ler->open arg;

Strategy svc_hand

// Turn the svc_handler into an active object if it isn't

// already one as a result of the first activation...

return svc_handler->activate ;

}

60 59

New Acceptor Use-Case

class Logging_Handler :

public Svc_Handler

Concluding Remarks

{

public:

// Initialize handler.

virtual int open void *;

 Design patterns can alleviate coupling and

unnecessary complexity in communication

// Obtain HANDLE.

virtual HANDLE get_handle void const;

software

// Called back to process logging records.

virtual int handle_event HANDLE;

 Patterns do not exist in isolation

};

typedef Acceptor

{ Instead, they form \families of patterns" that build

Logging_Acceptor;

up on each other

int main void

{

Reactor reactor;

 Both strategic and tactical patterns are nec-

Logging_Acceptor acceptor;

Logging_Strategy strategy /* ... */, &reactor;

essary to build exible and extensible solu-

acceptor.open LOGGER_PORT, &strategy;

tions

for ;;

reactor.handle_events ;

}

62 61