Interface Descriptions

Bridge, Adapter, Proxy, and Decrator Patterns



Transframe does not introduce a new language concept to support the abstraction over existing code for reuse purposes. Transframe's class concept is general enough that can support all the patterns that require interface and implementation separation. Transframe's class concept unifies the hybrid concepts such as function, generic class, union, signature, interface presented in other object-oriented languages.

A Transframe class can be constructed in two directions:

Further, the subclass relationship are not necessary to be declared explicitly. The language has a set of implicit subclass inference rules that determines whether a class is a subclass (and hence a subtype) of another class even they are not in an explicit inheritance path.

Bridge and Adapter Patterns

Consider the Bridge pattern (the Adapter is similar). The general idea of the Bridge pattern is to support the construction of an abstraction hierarchy parallel to implementation hierarchy and to avoid permanent binding between the abstractions and the implementations, and hence to reduce dependency among software components.

Transframe allows users to derive subclasses by inheriting a set of abstract interface classes while delegate the implementations to concrete implementations transparently. Therefore, the purpose of the Bridge pattern can be supported by Transframe's class construct directly. This kind of patterns (including the Adapter pattern) are no longer required in Transframe. Consider:


    def object Module
    {
	interface class Abstraction   //  an abstraction class
	{
	    function Method1();	 
	    function Method2();
	}

	class ImplementationA is Abstraction;	 
	class ImplementationB is Abstraction;
    }
	 

Users can use the above definition module in the following way:


    class MyImplemention is Abstraction by ImplementationA
    {
	function Method1()
	{
	     Abstraction::Method1();
	     Abstraction::Method2();
	}
    }
    class YourImplemention is Abstraction by ImplementationB;
	 

In MyImplemention, the implementation of Method1 is re-written and the the implementation of Method2 is automatically delegated to the implementation presented in the class ImplementionA. In YourImplemention, the implementation of all methods are automatically delegated to the implementation presented in the class ImplementionB.

Users do not need to reimplement each method by cluttering the name space with a superfluous set of new classes and methods. There is no need to maintain a bridge redundant dual class hierarchy, nor need to introduce an extra adapter to forwarding messages.

The implementation presented in classes ImplementionA and ImplementionB are hidden in a separated implementation module. They may be wrappers to existing legacy codes in binary format. These implementation classes are not required to be defined in the definition module. Therefore, changing of their implementation-related structures or private methods' interfaces does not affect applications that uses the implementation.

Transframe allows component libraries to be delivered in classes from which applications can derive subclasses without depending on the implementation of these superclasses. The change of implementation related structures and method interfaces in component libraries does not require recompilation which would otherwise require the source code of the component libraries.

Proxy and Decrator Patterns

The Proxy and Decorator patterns are often used in distributed computing where a proxy locating at a local machine may represent an object in a remote site. Application talking with the remote object must go through the proxy, and the proxy will transfer the application request to the remote object and then transfer back the result to the application.

Without a suitable language construct, writing applications that require Proxy and Decorator patterns are not straightforward. For example, using C++ to write CORBA or DCOM applications, developers must keep in mind that the object designed in C++ is not the object in CORBA or DCOM. For each C++ object, they have to create a proxy object within the underlying system and learn how to communicate with the remote object. To establish such communication is usually not easy. Sometimes additional languages and compilers must be used for interface definition.

The available tools such as AppWizard or ClassWizard in OLE Automation may alleviate such difficulty by generating all the related code in C++ automatically. People can follow the the wizard guide to create distributed applications without knowing the how and why. But the automation only simplifies the process, not the concept. They still need to understand the underlying concepts if they want to know what's really going on.

The problem of needing an extra proxy comes from the conflict between the built-in method invocation protocol and the actual required method invocation. For example, the built-in semantics for invoking a method within an object is designed for local objects only in most object-oriented languages. When a different invocation protocol must be used, application developers can no longer use the built-in method calls. Instead, they must write a proxy for each object they developed.

Transframe allows user to define how an object method can be invoked. Though the built-in protocol provides an simple mechanism as most of the other languages do, user can override the method invocation method for a specific application, for example client and server.

The following example gives an comparison of Transframe code with C++ code in distributed applications using DCOM.


Transframe
// module for clients
use DistributedInterface;
class Dictionary is
	   DistributedInterface
{
    method lookup(ItemName): Item;
    method addItem(Item);
    method deleteItem(ItemName);
}


// module for Server
use DistributedSever;
class Dictionary is
           DistributedServer
{
    method lookup(ItemName): Item;
    method addItem(Item);
    method deleteItem(ItemName);
  private:
    item_list: list of Item;
}
			
     
C++
[  uuid
   (E478374848-F23D-2984-9FBB-0457590A),
   ...
]
library Dictionary
{
   [ uuid
     (E478374848-F23D-2984-9FBB-04343435A),
     ...
   ]
   interface _IDictionary: IUnkonwn
   {
     Item lookup(ItemName);
     void addItem(Item);
     void deleteItem(ItemName);
   }
   ...
}
class DictionaryPro:
	public _IDictionary
{
  public:
    STDMETHOD(QueryInterface)
	   (REFIID riid, void **ppv);
    STDMETHOD_(ULONG,AddRef)  (void);
    STDMETHOD_(ULONG,Release) (void);
    STDMETHOD_(Item,lookup) (ItemName);
    STDMETHOD_(void,addItem) (Item);
    STDMETHOD_(void,deleteItem)(ItemName);
  private:
    Dictionary* m_pDictionary;
};	
class Dictionary :
	public IUnknown
{
   private:
    item_list: list<Item>
    IUknown * m_disp_interface;
    DictionaryPro * m_prog_interface;
   public:
    static Create()
    {
	Dictionary* pDic;
	IUnknown* pStdDisp;
	pDic = new Dictionary();
	pStdDisp =
	     CreateDispInterface(...); 
    }
    STDMETHOD(QueryInterface)
	    (REFIID riid, void **ppv);
    STDMETHOD_(ULONG, AddRef)  (void);
    STDMETHOD_(ULONG, Release) (void);
    Dictionary()
    {
	m_prog_interface =
	      new DictionaryPro;
	m_prog_interface->m_pDictionary
		= this;
	...
    }
    ~Dictionary()
    {
	delete DictionaryPro;
	...
    }
};
CLASS DictionaryCF:
	public IClassFactory
{
   public:
    static IClassFactory* Create()
    {
	return new DictionaryCF(); 
    }
    STDMETHOD(QueryInterface)
	    (REFIID riid, void **ppv);
    STDMETHOD_(ULONG, AddRef)  (void);
    STDMETHOD_(ULONG, Release) (void);
}

DistributedInterface and DistributedSever are domain-specific frameworks for client/server applications as shown in the following figure.

Framework for Distributed Interface
class DistributedInterface
{
   class method is functional
   {
       meta function operator create
	 ( owner: DistributedInterface;
	   input: inputType
	 ): outputType;
       meta enter();
   }
}
     
Framework for Distributed Server
class DistributedServer
{
  class method is function
  {
       meta function operator create
       ( owner: DistributedServer;
	 input: inputType
       ): outputType;
       meta enter();
  }
}

The member class method defined in DistributedInterface overrides the meta function create, which provides a specific way to create an activity within the distributed interface when the method is called. This activity is responsible for packaging the input and send them to the server for execution and waiting for the server's output. The member class method defined in DistributedServer provides a specific way to create an activity within the distributed server in response the method invocation request from the client. Both methods have a class constructor for automatic registration of the methods defined in subclasses.

With these domain-specific wrapper, designing a distributed object is no harder than designing an ordinary object. The complicated concepts in the underlying system is wrapped by a very simple high-level concept: developers simply write a distributed server with a number of methods in the same way you write a C++ class with a number of member functions. They focus only on the attributes and the functionalities of their own object. To design a distributed dictionary server, for instance, they just write the methods such as lookup, addItem and deleteItem. They no longer need a different IDL language. All the complexity and difficulty for distributed programming are hidden within the domain-specific frameworks.

Superclass Construct

Transframe's superclass construct provides a reverse step of subclassing: it groups a number of existing classes and provides a supertype with a common interface extracted from the existing classes. For example:


    class Value is super integer, double;

the class Value defines a superclass of integer and double. A name of type Value shall be expected to take a value of one of the two presented types. If integer and double have a common method with interface +(numeric):selfcalss), the type Value will also have this method with the same interface. For example:


    v: Value;
    if (some_condition) v =anInteger; else v =aDouble;
    v := v + 24;

It is not necessary to check the actual type contained in the polymorphic name v. Calling the method "+" will be correctly dispatched at runtime. Compared to union types presented in C++, Transframe's superclass construct is a type-safe mechanism.

By default, a superclass inherits (a reverse inheritance) all common methods from its listed subclasses. Users determine which common methods should be included by an explicit declaration. For example:


    class Value is super integer, double
    {
	function operator + (numeric): selfclass;
	function operator - (numeric): selfclass;
    }

If the subclass list is not presented, instead, a list of method interface is provided, for example:


    class InterfaceI is super
    {
	function f1();
	function f2(int);
    }

The conformance of a class to the superclass shall be inferred automatically at compile time. For example, class D:


    class D
    {
	function f1();
	function f2(int);
	function f3();
    }

is a subclass of InterfaceI, although this is not declared in class D.



- Copyright © 1997 Transframe Technology Corporation <info@transframe.com>
Last update: February 1997