Back Home Up Next


1 Purpose

2 Problem Description

3 Domain Definitions

4 Elisa Components

1 Purpose

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

2 Problem Description

Sometimes a toolkit class that's designed for reuse isn't reusable only because its interface doesn't match the domain-specific interface an application requires.

Consider for example a drawing editor that lets users draw and arrange graphical elements (lines, polygons, text, etc.) into pictures and diagrams. The drawing editor's key abstraction is the graphical object, which has an editable shape and can draw itself. The interface for graphical objects is defined by an abstract class called Shape. The editor defines a subclass of Shape for each kind of graphical object: a LineShape class for lines, a PolygonShape class for polygons, and so forth.

Classes for elementary geometric shapes like LineShape and PolygonShape are rather easy to implement, because their drawing and editing capabilities are inherently limited. But a TextShape subclass that can display and edit text is considerably more difficult to implement, since even basic text editing involves complicated screen update and buffer management. Meanwhile, an off-the-shelf user interface toolkit might already provide a sophisticated TextView class for displaying and editing text. Ideally we'd like to reuse TextView to implement TextShape, but the toolkit wasn't designed with Shape classes in mind. So we can't use TextView and Shape objects interchangeably.

How can existing and unrelated classes like TextView work in an application that expects classes with a different and incompatible interface? We could change the TextView class so that it conforms to the Shape interface, but that isn't an option unless we have the toolkit's source code. Even if we did, it wouldn't make sense to change TextView; the toolkit shouldn't have to adopt domain-specific interfaces just to make one application work.

Instead, we could define TextShape so that it adapts the TextView interface to Shape's. We can do this in one of two ways: (1) by inheriting Shape's interface and TextView's implementation or (2) by composing a TextView instance within a TextShape and implementing TextShape in terms of TextView's interface. These two approaches correspond to the class and object versions of the Adapter pattern. We call TextShape an adapter.

This diagram illustrates the object adapter case. It shows how BoundingBox requests, declared in class Shape, are converted to GetExtent requests defined in TextView. Since TextShape adapts TextView to the Shape interface, the drawing editor can reuse the otherwise incompatible TextView class.

Often the adapter is responsible for functionality the adapted class doesn't provide. The diagram shows how an adapter can fulfill such responsibilities. The user should be able to "drag" every Shape object to a new location interactively, but TextView isn't designed to do that. TextShape can add this missing functionality by implementing Shape's CreateManipulator operation, which returns an instance of the appropriate Manipulator subclass.

Manipulator is an abstract class for objects that know how to animate a Shape in response to user input, like dragging the shape to a new location. There are subclasses of Manipulator for different shapes; TextManipulator, for example, is the corresponding subclass for TextShape. By returning a TextManipulator instance, TextShape adds the functionality that TextView lacks but Shape requires.

3 Domain Definitions

We will transform the DrawingEditor case into a set of corresponding domain definitions:

 Shape = LineShape | TextShape | ...

 TextShape = ... + TextView + ... 

The next step is to translate the domain definitions into a number of Elisa components.

4 Elisa components

Based on the domain definitions a set of related components can be derived.

The first domain definition is a selection domain. It specifies a number of alternative domains. The implementation of selection domains is based on the concepts of categories.  The following component implements the

component Shapes;
type Shape = category(LineShape, TextShape);
     BoundingBox(Shape)       -> optional(BoundingBox);
     CreateManipulator(Shape) -> optional(Manipulator);
     BoundingBox(LineShape:lineshape) = BoundingBox(lineshape);
     BoundingBox(TextShape:textshape) = BoundingBox(textshape);

     CreateManipulator(LineShape:lineshape) = CreateManipulator(lineshape); 
     CreateManipulator(TextShape:textshape) = CreateManipulator(textshape); 
end component Shapes;

The definitions in this component use  patterns for dynamic type matching to select the definition rule which corresponds to the matching type. For example, the first definition rule of the BoundingBox definition test first if the type of the Shape argument is of the LineShape type. If that is the case, the corresponding LineShape function will be executed. Otherwise the following definition rule will be tried.  

The LineShape functions are defined in the following component:

component LineShapes;
type LineShape;
     CreateLineShape( )           -> LineShape;
     BoundingBox(LineShape)       -> BoundingBox;
     CreateManipulator(LineShape) -> Manipulator;
       << implementations of: >>
     CreateLineShape( )           = ...;
     BoundingBox(lineshape)       = ...;
     CreateManipulator(lineshape) = ...;
end component LineShapes;

The TextShape functions are defined in the following component:

component TextShapes;
type TextShape;
     CreateTextShape(TextView)    -> TextShape;
     BoundingBox(TextShape)       -> BoundingBox;
     CreateManipulator(TextShape) -> Manipulator;
       << implementations of: >>
     CreateTextShape(textview)    = TextShape:[..textview..];
     BoundingBox(textshape)       = [... GetExtent(textshape.textview).. ];
     CreateManipulator(textshape) = ...;
end component TextShapes;

The TextView functions are defined in the following component:

component TextViews;
type TextView;
     CreateTextView( ) -> TextView;
     GetExtend(TextView) -> Extent;
        << implementations of: >>
     CreateTextView( ) = ...;
     GetExtent(textview) = ...;
end component TextViews;


The connection between TextShape and TextView can be established in the following way. First, we assume that a call of the CreateTextView( ) definition will return a descriptor of TextView. That descriptor will be used as input for the CreateTextShape definition.  So,


    TextViewDescriptor = CreateTextView( );

    TextShapeDescriptor = CreateTextShape(TextViewDescriptor);


With these two operations TextShape and TextView are connected.


Back Home Up Next

  Part 5: Design Patterns   Adapter


Home | Highlights of Elisa | Integrating Different Paradigms | Getting Started with Elisa | Demo's  | What is Domain Orientation | Bibliography | Copyright | News | Contact | Contents

Language Description:

Lexical Elements | Basic Data Types and Expressions | Definitions | Streams | Backtracking | Statements and Special Expressions | Arrays | Lists | Descriptors | Components | Collections | Generic Components | Terms | Categories | Types | Built-in Definitions | Higher-order Definitions | External Interfaces | Index 

Data Structures: Sequences | Examples involving Lists | Trees | Graphs | Searching State Spaces | Language Processing | Knowledge Representations
Domain Modeling:

Domain Modeling | Concepts | Domain Definitions | Domain Operations | Domain Implementations | Systems | Case study: an Order processing system | Case study: an Airport Support system | Domain Orientation versus Object Orientation

Design Patterns:

Introduction | Abstract Factory | Builder | Factory Method | Prototype | Singleton | Adapter | Bridge | Composite | Decorator | Facade | Flyweight | Proxy | Chain of Responsibility | Command | Interpreter | Iterator | Mediator | Memento | Observer | State | Strategy | Template Method | Visitor 


click tracking


Send me your comments


This page was last modified on 14-11-2012 18:07:46   

free hit counter