The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998
Abstract Well-designed software separates the domain model from peripheral subsystems (like GUI, persistence or communication infrastructure). However, these subsystems still have to communicate with the domain model. Nevertheless, the domain model should not depend on peripheral subsystems that are susceptible to change. The most common way of having the peripheral subsystems communicate with the domain model is to write dozens of public accessors and mutators for each domain class. We expose a Design Pattern named Transfer that avoids these "bloated" interfaces and isolates the set of specific basic types used in peripheral subsystems of the domain model.
Name Transfer
Example Suppose you have a well-designed model. Now it's time for your application to have an attractive graphic user interface (GUI), with smart dialog boxes. You are strongly committed to follow the well-known principle : the model must not depend on the GUI. To save time, money and sweat, you decide to use a widespread GUI framework. The only remaining thing to do is to connect both sides. At this very moment, you learn two pieces of bad news: First, to show the data, the dialog boxes need to take information out of the model. Since you don't want your model be dependent of the user interface, you feel forced to use accessors and mutators. Doing so, you break down your design by allowing other model classes to use these public accessors instead of creating true services with high level responsibilities. Second, the framework doesn't come only with it's architecture, it also comes with its own set of elementary types. In order to deal with the framework, you should use this set of elementary types. Your model becomes GUI dependent. Now, you know you must find out a solution to this real problem of connection.
Problem The core question answered by Transfer Pattern is "how do you connect peripheral subsystems (for instance the GUI) with the model to ensure data transfer ?".
Context You are designing a software with a user interface using a framework.
Forces • We need to transfer data from the model to the GUI or to other peripheral subsystem ... But we want to avoid creation of public accessors and mutators on model classes. Resisting use of public accessors is a key to force the designers to create a high level responsibilities services. The domain model is the system core and must be preserved. • The framework incites us to use its elementary types ... But we don't want these types to invade our domain model because we want to be able to change of GUI framework or to redesign the GUI with no consequences on the domain model. More over, we want to create our own elementary classes, in order to handle business concepts easily. These classes are used as simple types, such a attributes types or local variables types. In so far as these concrete data types are very specific to a domain, GUI frameworks can't deal with them. • We want an efficient mechanism to transfer data between both sides, especially regarding the number of copy involved ... But we want to limit the coupling between both sides.
Solution Create a set of Transfer classes between the domain model and the subsystem. Each domain class to be represented in the subsystem owns its Transfer class to store and transmit selected data, like the Memento pattern does. The external interfaces of these Memento-like classes are in conformity with basic types used by the domain model. Derive the concerned domain classes from an Editable interface which defines two methods:
1 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 • Get : To put the data from the domain class inside the related Transfer class. • Set : To write data from this Transfer class into the domain class. The initialization / update protocol handle only the Editable interface, no regarding of the concrete domain class involved, but each one must implement the correct job to write and read data to and from the related Transfer class. Make the connection between each concerned subsystem class and it's related Transfer class. The underlying structure of this connection is close to the Adapter pattern, with the Transfer class as Adaptee, the subsystem class as Adapter, and the base class of this subsystem class (probably a framework class) as Target. However, the subsystem class aggregates the Transfer class as it's own data stor. Like the Adapter Pattern, the Transfer Pattern allows two variants : • The composition variant can be used by languages that doesn't allow multiple inheritances, like Java or Smalltalk. • Multiple inheritances variant can be used with languages allowing this feature, like C++ or Eiffel. Define a specific interface on each Transfer class, to be used by the subsystem classes. This interface is in conformity with the subsystem's basic types. Create a Translator class, with a set of methods for domain model basi types to subsystem basic types translation and reverse. Make this Translator class a base class of all the Transfer classes, because these Translation methods are only used by the Transfer classes and becuse the Editable interface needs to handle Transfer classes through a common base class.
Structure
Participants • Editable : Abstract interface to be implemented by the Source class. Declare two abstract operations, a Get method and a Set method with a Translator as parameter. • Source (ConcreteModelClassA on the diagram): Concrete domain model class to be transfered. Implemente the Editable interface. Check the Translator object passed to be a TransferA, for instance with dynamic casting. • Translator: Base class of all Transfer classes. Provides converter services from basic types used by the model to basic types used by the framework, and reverse. This class deals with the Editable interface for data initialization and update. • Transfer (TransferA on the diagram): Is a kind of data store. This class is also an abstract base class for the Target class. • Target (SubsystemClass on the diagram): Peripheral subsystem concrete class. This class used Transfer's data as its own.
2 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998
Collaboration
Implementation 1. Define the Transfer classes with their external interfaces. For each domain class, define the data which must be read or may be updated (or both) by the subsystem class. Reduce the external interface at the strict minimum to ensure the needed read: write capabilities. You can give a special relationship between each Transfer class and the related domain class through the external interface by using the Private interface Pattern. A simpler alternative in C++ is to declare the domain class as friend of the Transfer class as the Memento Pattern does. 2. Define the translation rules between domain model basic types an the subsystem basic types. The set of data to transfer defines the set of types involved. These types need to be translated in their corresponding subsystem basic types, or broken in several parts if a complex type doesn't match a counterpart in the subsystem. Doing so, you can define Transfer classes attributes. These attributes will act as a working area for the subsystem classes, so efficiency is improved if both sides used the same basic types. 3. Define the translator class. Using the Translation rules, you can define the set of methods for domain model basic types to subsystem basic types and reverse. Because the Translator class is a base class for all the
3 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 Transfer classes, give a protect visibility to the Translation methods, in order to restrict their usage for Transfer classes. In C++, use the inlining feature to improve efficiency of the simplest translations. 4. Implement the Transfer classes external interfaces. Use the required translation methods to covert the incoming and outgoing data. 5. Define the Editable interface. It can be a Java interface or a C++ abstract class. With both "Get" and "Set" method as virtual pure. An alternative is to respect the Template Method Pattern design for these two methods. Doing so, you can insert at the Editable level some application specific code like calls to log event or to persistence. Following this alternative, Editable is no longer an interface, but a true base class. Because the domain model classes may already have their own base class, this alternative ill probably be restricted to the languages supporting multiple inheritances. The application may deal with several subsystems, using separate set of Transfer classes. Each set of Transfer classes derive from its own Translator class. Define as many Editable interfaces as needed to handle these sets of Transfer classes. A C++ variant shown in Annex allows creation of multiple sets of Transfer classes from only one set. 6. Define the subsystem specific interface on each Transfer class. The methods of these interfaces can be as simpler as accessors and mutators, but you can also take advantage of mutators to keep track of modifications. As in (1), reduce the set of methods at the strict minimum needed. 7. Make the connection between subsystem class and Transfer class. a.
Following the composition variant: as in (1) you can give a special relationship between the Transfer class and the related subsystem class using Private Interface Pattern. A drawback of this variant is an extra copy between the domain and the subsystem. So, we advise to restrict this variant to languages which doesn't allow multiple inheritances. Elsewhere, prefer the variant 7b.
b.
Make the connection between subsystem class and Transfer class following the multiple inheritances variant. Give a protected visibility to the specific interface on the Transfer class, in order to restrict its usage to the related subsystem class. You can also expose Transfer's data as protected attributes and avoid the specific interface. Doing so, the subsystem directly handle these attributes and efficiency is improved, but you can't keep track of changes any more as stated in (6), neither you can give readonly feature to some attributes. In both case (a) and (b), keep in mind that a subsystem class may handle several Transfer classes. For example, a single dialog box may show data from two domain model classes. There is no restriction in the composition variant to have several Transfer classes as component, nor in the multiple inheritances variant to have as many Transfer base clases as needed.
Rationale The first force is resolved: Transfer Pattern is able to move data between the domain model and a subsystem, while avoiding the need for a bundle of public accessors and mutators on the domain model classes. Instead, the Transfer class fulfills this role. Domain model classes handle Transfer classes for communication purpose only. The second force, efficiency, is resolved with the multiple inheritances variant, especially when direct handling of Transfer classes attributes by subsystem classes is allowed. Doing so, there is only one data copy during initialization and same during update. The composition variant perform an extra copy and doesn't achieve the efficiency goal. The key point to resolve the third force is the Transfer class's external interface avoid exposure of subsystem basic types. This external interface is consistent with the two kind of elementary types used by the domain model : language basic types and domain specific elementary classes. The translation is encapsulated in the Translator class. Changing the framework used to implement a subsystem has no effect on the domain model, only on Translator and Transfer classes.
Consequences
• We finally obtain an architecture where subsystems are clearly identified. There is no more fear to use "as is" framework's basic types or special features, because we know that Transfer classes ensure independence. • The shifting of data access to the Transfer classes can really limit internal data access between team development members working together on different domain model classes. For example, with C++, a developer who is not responsible for a domain model class only needs the corresponding object and headers
4 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 files. There is no reason to give him the Transfer class's header. Without this header, there is no way to get the domain model class's internal data. There is also unexpected positive side effects : • Transfer classes become the link between the model and the subsystem. Parallel development of the domain model and the subsystem is improved because subsystem classes no longer depend on domain model class's interfaces but on the Transfer classes. Changes in the domain model doesn't impact the subsystem. Instead, both sides may consider the Transfer classes as a "contract". • Keep track of object's value evolution : Transfer classes are the single channel to data modification. Using the Transfer classes as Memento when data are modified is a smart way to achieve this goal. We only need to apply the Prototype Pattern on the Transfer classes to take a snapshot of the data. • Consistency control of modifiable objects. All operations for reading and writing the object's data are enclosed in only one Get and one Set methods. Consistency of domain model classes can be checked with preconditions and postconditions on these two methods.
There are several drawbacks. • Design complexity and code size increases significantly. Instead of adding a bundle of accessors and mutators on domain model classes, we introduce a new class. Even more, the Translator class and the Editable interface must be written independently of the number of Transfer classes. We advise to apply Transfer Pattern only when several Transfer classes are created. • In the composition variant, you perform an extra copy. The efficiency goal is not achieved in this case. • Use of multiple inheritance may occur side effects, especially in C++. If you deal with a single inheritance only framework, implicit features of this single inheritance may be used. There is no silver bullet against this problem. In C++, we advise to specify the Shape as the first base class of the subsystem class.
Related Pattern • Adapter [Gamma+95, p. 139]. An Adapter-like structure is used on the subsystem side to adapt Transfer class to subsystem class. Both forms of this pattern, class adapter or object adapter are applicable, leading to the multiple inheritances or composition variant of Transfer Pattern. • Command [Gamma+95, p. 233]. The unification aspect of Get and Set methods leads to generic Command for creation and modification of domain model objects. The Transfer Pattern can store an object's state between two operations and can be used to restore previous state. Using templates in C++, we only need two generic classes for creation and modification of domain model objects, then avoiding Command classes proliferation. • Franca Lingua[Foote98]. This pattern is about conversions between different formats. In this perspective, the Translator class, in the Transfer Pattern, achieves for a part the same goal and the domain model types act as the Franca Lingua. • Informational Role [Kerth95, p.310] achieves for a part the same goal, but Kerth's pattern is oriented to abstract specific information delivered by classes. The main force is avoiding switch-case statements. • Memento [Gamma+95, p. 283] is used to externalize, store and restore an object's state, without violating encapsulation. A Transfer class could nearly act as a Memento, but which is not create by the domain model object. The goal of a Transfer class is data transmission instead of storage. • Object recovery [Silva+98, pp. 261-276] shares some similarities with Transfer Pattern, like encapsulation and modularity, but its fist intent is the recovery after failure. So, this pattern separates the object recovery policies from object-specific functionality. • Observer [Gamma+95, p. 293] and Transfer Pattern are complementary.. In the Observer Pattern, the concrete subject is read and writen by the concrete Observer through two methods : SetState () and GetState(). An Observer can se a Transfer class to handle the Subject's state. In short, the Observer Pattern gives an answer to : "When data must be updated ?". The Transfer Pattern answer to : "How data can be updated ?". • Private interface [Newkir97] provides a private channel between two classes. We can take advantage of this pattern to give a non sharable interface between the participants of the Transfer Pattern, especially between the Source class and the Transfer Class. • Prototype [Gamma+95, p. 117] is needed on the Transfer class, to take a snapshot of the Source class, if we want to take advantage of the "Transfer layer" for an undo / redo mechanism. • Template Method [Gamma+95, p. 325]. As previously stated, this pattern can be used on the Editable class, for the Get and Set methods. • Three subsystems [Cockburn95, p. 325] describes well-designed application's structure. When applying this pattern, you enter the context of Transfer Pattern. • Visitor [Gamma+95, p. 331] shares some similarities with Transfer Pattern. The Get and Set methods on the Source class look like the Accept method on Element class. Both intent and structure are different. Visitor is 5 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 algorithm oriented through its double-dispatching mechanism. Transfer pattern don't have such a mechanism, because it is data transmission oriented.
Known uses Transfer Pattern has been successfully used in a network supervision project at EDF-GDF, the French national provider of electricity and gas. In this application, Transfer Pattern has been applied two times: • For connecting the GUI framework to the domain model. • For connecting the persistence subsystem to the domain model.
Acknowledgments We would like to thanks Kyle Brown for shepherding our work and Neil Harrison, our mentor in Pattern writing workshop at EuroPLOP. We also thanks EuroPLOP 98 stream A participants for their generous acceptance of this paper in writters workshop and their useful contribution to improve this paper. We acknowledge Jens Coldewey for his constructive suggestions and Philippe Foucard for his useful contribution on reading and reviewing this pattern.
6 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998
Annexes Sample code Our example concerns the communication between a domain model represented by a User class and a GUI subsystem based on MFC represented by a dialog box class. We choose MFC because it's a widely used framework and because this framework comes with its own basic types. On the other side, the domain model uses native C++ and standard library basic types. We want to edit a User class via a dialog box. 1. Consider the User class : class User { public: User(const std::string& name); void SetPassword(const std::string& password); ... private: std::string _name; std::string _password; int _age; EmployeeNumber _empNum; ... }; We can define an external interface for the User's Transfer class. It looks like this : class UserTransfer { public: std::string GetName() const; void SetName(const std::string& name); int GetAge() const; void SetAge(int age); void SetEmpNum(EmployeeNumber empNum); }; The EmployeeNumber attribute is read only while the password will be not accessible at all from the dialog box. All others attributes will be accessible for read ans modifications. 2. We can now define the translations rules : Model int std::string EmployeeNumber
Subsystem int CString int
3. Using the translation rules, we can write the Translator class : class Translator { protected: inline void StringToCString(const std::string& val_I, Cstring& val_o) const; inline void CstringToString(const Cstring& val_I, std::string& val_o) const; void EmployeeNumberToInt(EmployeeNumber num_I, int& num_o) const; void IntToEmployeeNumber(int num_I, EmployeeNumber& num_o) const; }; The Translator class is not complete right now. Nevertheless, you can notice the following points: The type int from the domain model don't need translation methods, because this type is used as it on the subsystem side. If possible, the translation methods can be tagged inline. In our example, string to CString translation is trivial and EmployeeNumber to int is not. Last but not least, Translator is a base class for UserTransfer :
7 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 class UserTransfer : public Translator { ... }; 4. Now, we can implement UserTransfer's attributes : class UserTransfer : public Translator { public: ...// External interface written in (1) private: CString t_name; int t_age; int t_number; }; We can also implement UserTransfer's external interface : std::string UserTransfer::GetName() const { std::string name; CStringToString(t_name, name); return name; } void UserTransfer::SetName(const std::string& name) { StringToCString(name, t_name); } int UserTransfer::GetAge() const { return t_age; } void UserTransfer::SetAge(int age) { t_age = age; } void UserTransfer::SetEmpNum(EmployeeNumber empNum) { EmployeeNumberToInt(empNum, t_number); } 5. We need an Editable interface. We can define this interface as follow : class Editable { public: virtual ~Editable() {} protected: Editable() {} virtual bool Get(Translator&) const = 0; virtual bool Set(const Translator&) = 0; }; To take it in account, the User class must inherit from this Editable interface and redefine both Get and Set methods. class User : public Editable { public: ...// Already written protected: virtual bool Get(Translator& t) const; virtual bool Set(const Translator& t); private: ...// Already written 8 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 }; The Translator class must be slightly modified too. This class deals with the Editable interface for initialization and update. class Translator { public: virtual ~Translator() {} protected: Translator(Editable *pEditable); bool Update(); ... private: Editable *_pSource; }; Initialization and update can be implemented as follow: Translator::Translator(Editable *pEditable) { _pSource = pEditable; _pSource->Get(*this); } bool Translator::Update() { return _pSource->Set(*this); } 6. We can write UserTransfer specific interface, now. class UserTransfer : public Translator { public: UserTransfer(Editable *pEditable); ...// External interface is already written protected: inline void set_name(const CString& name); inline CString& get_name() const; inline void set_age(int age); inline int get_age() const; inline int get_empNumber() const; private: ...// Attributes are already described }; UserTransfer's constructor is modified to take in account the former modification of Translator's constructor. As already stated, the specific interface is rather simple. So, we specify all these methods as "inline" in order to improve efficiency. The proposed implementation is trivial, but you can make them more complex, if you want to keep track of modifications, for instance. inline void UserTransfer::set_name(const CString& name){t_name = name;} inline CString& UserTransfer::get_name() const {return t_name;} inline void UserTransfer::set_age(int age){t_age = age;} inline int UserTransfer::get_age() const {return t_age;} inline int UserTransfer::get_empNumber() const {return t_number;} In fact, in the final code, we avoid the specific interface and expose UserTransfer's data as protected attributes. The reason is a MFC's special mechanism named "Dynamic Data Exchange / Validation(DDX/DDV)". It's not the place to discuss of this mechanism, just notice that this DDX/DDV directly handle attribute's references. 7. Because our example is written in C++, we'll take advantage of multiple inheritance. class UserPropertyDlg : public CDialog, private UserTransfer { public: UserPropertyDlg(User *pUser); virtual void OnInitDialog(); ...
9 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 virtual void OnOk() { ... Translator::update(); ... }
}; Most of the MFC glue is omitted. We just describe two framework's methods : OnInitDialog : is called by the framework to initialize the dialog's widgets. We can take advantage of this method to initialize all dialog fields with UserTransfer's values. OnOk : is called by the framework when you pushed the OK button. It's a good place to update the model class. So, we insert a call to Translator::Update at this place.
C++ specific variants The implementation section describes some possible variants. We also show here two C++ idiomatic variants. 1. Generic implementation of subsystem classes in C++. Using the generic paradigm, we can write a base class for all dialog based on Transfer. Such a class uses a C++ idiomatic form : template<class T> class TransferDlg : public CDialog, protected T { public: virtual void OnOk(); ... protected: TransferDlg(Editable *pEditable) : T(pEditable){} ... }; Now, the concrete user property dialog box is rather simplified : class UserPropertyDlg : public TransferDlg< UserTransfer > { public: UserPropertyDlg(User *pUser) : TransferDlg<UserTransfer>(pUser){} virtual void OnInitDialog(); ... }; 2. Multiple sets of Transfer classes, using "traits". This C++ specific technique [Myers95] allows the definition of various implementation or type definitions using generic programming. Using this technique, it is possible to write only one set of Transfer classes, even if several sets are necessary to deal with several subsystems. The variable elements like framework's type or translation methods are designed using the trait class. Here is an example : template<class T> class trait_translator{}; template<class T> class UserTransfer : public trait_translator<T> { public: void SetName(const std::string& name) { StringToTString(name, t_name); } ... protected: TString t_name; int t_age; TEmpNumber t_empNumb; }; Each time a new familly of Transfer class is needed, only one Tranlator class is necessary to define all symbolic types and translator methods. Such a trait translator is defined as follows : class trait_translator<CString> { protected: typedef CString TString; typedef BOOL TBool;
10 / 11
The Transfer Pattern, by Christophe Addinquy & Frédéric Paulin – EuroPLOP 1998 typedef int TEmpNumber; ... StringToTString(const std::string& val_I, TString& val_o); ...
}; For each concrete trait class, a "selector" type is required. In the example above, we choose the framework's string type as selector, because it's a typical basic type that changes from framework to framework. Now, as the generic set of Transfer class and each specific trait Translator are created, each concrete set of translator needs only some typedefs to be declared : // First set typedef UserTransfer<CString> MFC_UserTransfer; ... // Second set typedef UserTransfer<std::string> STD_UserTransfer; Of course, an Editable interface for each Transfer classe's set is needed.
11 / 11