Back Home Up Next

BRIDGE

1 Purpose

2 Problem Description

3 Domain Definitions

4 Elisa Components

1 Purpose

Decouple an abstraction from its implementation so that the two can vary independently.

2 Problem Description

When an abstraction can have one of several possible implementations, the usual way to accommodate them is to use inheritance. An abstract class defines the interface to the abstraction, and concrete subclasses implement it in different ways. But this approach isn't always flexible enough. Inheritance binds an implementation to the abstraction permanently, which makes it difficult to modify, extend, and reuse abstractions and implementations independently.

Consider the implementation of a portable Window abstraction in a user interface toolkit. This abstraction should enable us to write applications that work on both the X Window System and IBM's Presentation Manager (PM), for example. Using inheritance, we could define an abstract class Window and subclasses XWindow and PMWindow that implement the Window interface for the different platforms. But this approach has two drawbacks:

  1. It's inconvenient to extend the Window abstraction to cover different kinds of windows or new platforms. Imagine an IconWindow subclass of Window that specializes the Window abstraction for icons. To support IconWindows for both platforms, we have to implement two new classes, XIconWindow and PMIconWindow. Worse, we'll have to define two classes for every kind of window. Supporting a third platform requires yet another new Window subclass for every kind of window. 

     

     

  2. It makes client code platform-dependent. Whenever a client creates a window, it instantiates a concrete class that has a specific implementation. For example, creating an XWindow object binds the Window abstraction to the X Window implementation, which makes the client code dependent on the X Window implementation. This, in turn, makes it harder to port the client code to other platforms. 

    Clients should be able to create a window without committing to a concrete implementation. Only the window implementation should depend on the platform on which the application runs. Therefore client code should instantiate windows without mentioning specific platforms.

The Bridge pattern addresses these problems by putting the Window abstraction and its implementation in separate class hierarchies. There is one class hierarchy for window interfaces (Window, IconWindow, TransientWindow) and a separate hierarchy for platform-specific window implementations, with WindowImp as its root. The XWindowImp subclass, for example, provides an implementation based on the X Window System.

All operations on Window subclasses are implemented in terms of abstract operations from the WindowImp interface. This decouples the window abstractions from the various platform-specific implementations. We refer to the relationship between Window and WindowImp as a bridge, because it bridges the abstraction and its implementation, letting them vary independently.

3 Domain Definitions

We will transform the top-level relations into a set of corresponding domain definitions:

 Window = IconWindow | TransientWindow 

 WindowImp = XWindowImp | PMWindowImp. 

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 Window = IconWindow | TransientWindow :

component Windows;
type Window = category(IconWindow, TransientWindow);
     DrawText(Window) -> Status;
     DrawRect(Window) -> Status;
        . . .
begin

        << implementations of: >>
     DrawText(window) = DevDrawText(Windowimp);
     DrawRect(window) = DevDrawLine(Windowimp); << 4 times >>
       . . .
end component Windows;

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 IconWindow functions are defined in the following component:

component IconWindows;
type IconWindow;
     CreateIconWindow( ) -> IconWindow;
     DrawBorder(IconWindow) -> Status;

       . . .
begin
        << implementations of: >>
     CreateIconWindow() = IconWindow:[ .. icon = "icon.jpg" .. ];
     DrawBorder(window) = [ DrawRect(Window:window)... DrawText(Window:window) ];

        . . .
end component IconWindows;

 

The TransientWindow functions are defined in the following component:

component TransientWindows;
type TransientWindow;
     CreateTransientWindow( ) -> TransientWindow;
     DrawCloseBox(TransientWindow) -> Status;
        . . .
begin
        << implementations of: >>
     CreateTransientWindow( ) = TransientWindow: [Textview = "TextView"] ;
     DrawCloseBox(window) = [ DrawRect(Window:window) ];
        . . . 
end component TransientWindows;

 

The following component implements the  WindowImp = XWindowImp | PMWindowImp :

component WindowImps;
type WindowImp = category(XWindowImp, PMWindowImp);
     SelectWindowImp(text) -> single(WindowImp);
     DevDrawText(WindowImp) -> optional(Status);
     DevDrawLine(WindowImp) -> optional(Status);
begin
     SelectWindowImp ("XWindowImp" ) = WindowImp:CreateXWindowImp();
     SelectWindowImp ("PMWindowImp") = WindowImp:CreatePMWindowImp();

     DevDrawText(XWindowImp:Xwindow) = DevDrawText(Xwindow);
     DevDrawText(PMWindowImp:Pwindow) = DevDrawText(Pwindow);

     DevDrawLine(XWindowImp:Xwindow) = DevDrawLine(Xwindow);
     DevDrawLine(PMWindowImp:Pwindow) = DevDrawLine(Pwindow);
end component WindowImps;

The XWindowImps functions are defined in the following component:

component XWindowImps;
type XWindowImp;
     CreateXWindowImp( ) -> XWindowImp;
     DevDrawText(XWindowImp) -> Status;
     DevDrawLine(XWindowImp) -> Status;

        . . .
begin
            << implementations of: >>
     CreateXWindowImp() = XWindowImp:[ ... ] ; << constructor >>
     DevDrawText(Xwindow) = XDrawString( ) ;
     DevDrawLine(Xwindow) = XDrawLine( );
       . . .
end component XWindowImps;

The PMWindowImps functions are defined in the following component:



component PMWindowImps;
type PMWindowImp;
     CreatePMWindowImp( ) -> PMWindowImp;
     DevDrawText(PMWindowImp) -> Status;
     DevDrawLine(PMWindowImp) -> Status;

        . . .
begin
            << implementations of: >>
     CreatePMWindowImp() = PMWindowImp:[ ... ] ; << constructor >>
     DevDrawText(PMwindow) = PMDrawString( );
     DevDrawLine(PMwindow) = PMDrawLine( );
         . . .
end component PMWindowImps;

The connection between Window and WindowImp can be established in the following way. Before we call any function of the Windows component we have to call the SelectWindowImp function of the WindowImp component. For example, if we want to select the PMWindowImp implementation we may do it in the following way:

 

    Windowimp = SelectWindowImp("PMWindowImp");

 

With this operation  Window and WindowImp are connected.

 

Back Home Up Next

  Part 5: Design Patterns   Bridge

            
Introduction

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 27-11-2012 17:07:02   

free hit counter