桥接设计模式的例子

转自:http://www.codeproject.com/Articles/890/Bridge-Pattern-Bridging-the-gap-between-Interface

 

Introduction

Development, Marketing and Technical Support teams play a vital role in the success of a software product. Development team is of course the backbone of the product, Marketing team plays a key role in selling the product and Technical support team provides after sales support to the customers. Often, I use to think why development team could not provide support. I found the answer only after a very long time. Technical support team should interact with both customers and computers, whereas the development team spends most of their time in hacking computers. So, the members working in both teams should have different mind set and have totally different roles to play. On top of this, the nature of operation in both the teams is mutually exclusive. For example, we can see products having 24 x 7 technical support, whereas the development team hardly works round the clock. The change in the operational strategy in one of the team will not have a direct impact on the other. In short, the technical support team acts as an interface to the product whereas the development team implements the product.

When I was reading the book DesignPatterns: Elements of Reusable Object-Oriented Software by Erich Gamma et al. (Addison-Wesley, 1995) written by 'Gang of Four (GoF)', I could relate the above-mentioned scenario with theBridge Pattern. Separating the technical support team from the development team is similar to isolating the abstraction from implementation. That's what theBridge Pattern is intended to do. In this article, I will be talking about theBridge Pattern, what, why and when it is needed. Benefits and drawbacks in using theBridge Pattern are also presented. The discussion will not be complete without mentioning about the variants and known uses of theBridge Pattern in popular libraries. Let me start the discussion with a simple programming example.

An Example

Internet is a very good example for proving the fact, "A picture is better than thousand words". When it all started, HTML was just a text based markup language, concentrating mainly on the structure of the document but not its presentation. However, HTML has gone through various revisions to include graphics and images. Now, thousands of web sites, spread all over the Internet, contains GIF and JPEG images. Though GIF and JPEG are popular image formats used on the web, there are hundreds of other image formats such as BMP, PCX, TIFF, TARGA etc., which serve very different purposes and are popular in different operating systems. For example, BMP image format is widely used on Windows operating systems, however its existence is also there on OS/2, Macintosh and UNIX operating systems.

The structure and the representation are two important aspects of an image format. The structure defines the way in which the image is stored and the representation deals with the display of the image. For a given format, the structure remains the same across operating systems, whereas the representation or the way in which the image is displayed may vary between operating systems. For example, the structure of a Windows BMP file remains unchanged in all operating systems, but the mechanism used by the Windows operating system to display a BMP file is different from the one used by Macintosh or OS/2 operating systems to display the same file. On the other hand, for a given operating system, the representation can remain unchanged across different image formats. For example, Windows can display an image represented as a Bitmap object, without having to know about its source format which can be a BMP, a JPEG or a PCX. In short, the representation and the structure of an image format are two different aspects and they should be allowed to vary independently based on the other factors like operating system, hardware etc.

Bridge Pattern classified under the Structuralpattern by 'Gang of Four (GoF)' can be used to abstract and model these variations. According to GoF, theBridge Pattern is intended to"Decouple an abstraction from its implementation so that the two can vary independently". In this article, I will be using the terms used by GoF to explain theBridge Pattern.

This article uses an Image viewer application to explain the concept behind theBridge Pattern. This sample application isdesigned to view BMP files on Windows operating systems. However, it can easily be extended to view other image formats like JPEG on Windows or view BMP images on other operating systems like OS/2.

This example uses two-class hierarchies viz., CImage and CImageImp (see diagram). CImage class hierarchy defines the abstraction for the clients and CImageImp class hierarchy provides implementation for the specified abstraction. CImage and its derived classes are responsible for handling different image formats such as BMP, JPEG, PCX etc., and CImageImp classes are responsible for the representation of the images on different operating systems like Windows, OS/2. The CImage object provides basic services for loading and showing images and it is configured with a CImageImp object. Services that are dependent on a particular implementation (like show) are forwarded to CImageImp class (say to PaintImage). In this way, new image formats can be added to the CImage class hierarchy without affecting the CImageImp and CImageImp can be extended to provide implementation for a new operating system without affecting CImage. In short, the goal of the BridgePattern is achieved, that is, to vary abstraction and implementation independently.

Bridge Pattern has four participants that includeAbstraction, Refined Abstraction, Implementor and Concrete Implementor. In this example, the abstract image classCImage, is referred as the Abstraction, the concrete image classCBmpImage (for handling Windows Bitmaps) is referred as Refined Abstraction,the abstract image implementation class CImageImp is referred as Implementor and the concrete class CWinImp that implements the interfaces of the Implementor is referred as theConcrete Implementor. The application using the CImage Abstraction is the client. Depending on the operating system, the client can configure the CImage subclass (Refined Abstraction) with a concreteCImageImp class object (Concrete Implementor).

CImage maintains a reference to the CImageImp object. When the client calls Load or Show method in CImage, it does some preprocessing and forwards the request to CImageImp object by calling InitImageInfo or PaintImage method that provides the actual implementation. Isolating the image and image implementation in separate class hierarchies entitles them to vary independently. UML diagram showing the relationship between the participants of theBridge Pattern is presented below.Listing 1 contains the class declarations and Listing 2 contains sample method implementations.

Benefits in using Bridge Pattern

  1. Decoupling abstraction from implementation - Inheritance tightly couples an abstraction with an implementation at compile time.Bridge pattern can be used to avoid the binding between abstraction and implementation and to select the implementation at run time.

  2. Reduction in the number of sub classes - Sometimes, using pure inheritance will increase the number of sub classes. Let us assume that the full-blown version of our Image Viewer supports 6 image formats in 3 different operating systems. Pure inheritance would have resulted in 18 sub classes whereas applying Bridge Pattern reduces the sub class requirement only to 9.

  3. Cleaner code and Reduction in executable size - In the above example, operating system specific code is encapsulated in CImageImp sub classes. This results in a cleaner code without much preprocessor statements like #ifdefs, #ifndefs. Also, it is easy to conditionally compile CImageImp sub classes for a given operating system to reduce the size of the executable.

  4. Interface and implementation can be varied independently - Maintaining two different class hierarchies for interface and implementation entitles to vary one independent of the other.

  5. Improved Extensibility - Abstraction and implementation can be extended independently. As mentioned earlier, the above example can easily be extended to view other image formats on Windows or view BMP images on other operating systems.

  6. Loosely coupled client code - Abstraction separates the client code from the implementation. So, the implementation can be changed without affecting the client code and the client code need not be compiled when the implementation changes. (NOTE : In the above mentioned example, for the sake of simplicity, the application configures the CImage object with the right CImageImp object. However, alternate methods like Abstract Factory can be adopted to choose the CImageImp object.)

Drawbacks in using Bridge Pattern

1. Double indirection - In the above example, operating system specific methods are implemented by subclasses of CImageImp class. CImage class must delegate the message to a CImageImp subclass which implements the appropriate method. This will have a slight impact on performance.

Variants

Handle/Body

Reference counting is a technique to allow multiple objects with the same value to share a single representation of that value. The advantage of sharing representation reduces the memory overhead in case of large objects. A simple example is a String class, in which multiple objects can share the same String representation. String class is referred to as a Handle class and String representation is referred to as a Body class. Handle class specifies the interface and the Body class maintains a reference count and implements the actual representation. Clients interact with the Body class through the interface specified by the Handle class. Handle/Body separation abstracts the client from implementation changes. This structure is very similar to theBridge Pattern, however the intent is different.

Degenerate Bridge

Sometimes, there may be only one Implementor class for a given Abstraction. Therefore, an Abstract Implementor class is not needed. This leads to a one to one relationship between Abstraction and Implementor classes. The separation may still be useful to change the Implementor without affecting any of its clients. GoF refers this as DegenerateBridge Pattern, where there is a one-to-one relationship between Abstraction and Implementor. GoF explains DegenerateBridge with an example from libg++. Libg++ defines classes that implement common data structures, such as Set, LinkedSet, HashSet, LinkedList, and HashTable. Set is an abstract class that defines a set abstraction, while LinkedList and HashTable are concrete implementors for a linked list and a hash table, respectively. LinkedSet and HashSet are Set implementors thatbridge between Set and their concrete counterparts LinkedList and HashTable.

Known Uses

This section presents known uses of BridgePattern. Some of the known uses presented in this section are taken from the GoF book onDesign Patterns.

MFC and Bridge Pattern

In MFC, the process of storing/retrieving an object to/from a persistence mechanism (like a file) is called Serialization. MFC uses theBridge Pattern to implement Serialization. CArchive and CFile classes implement object Serialization. CArchive class provides the interface for writing/reading an object to/from a persistence mechanism whereas the CFile and its sub classes provides implementation for different persistence mechanisms such as memory, disk file, sockets etc.

A CArchive object is configured with an object of class CFile (or a derived class) during its construction, from which it obtains the necessary information for serialization, including the filename and type of the requested operation (a read or write). Client performing the Serialization operation can use CArchive object without regarding the persistence mechanism implemented by CFile classes.

Java and Bridge Pattern

Java uses the Bridge Pattern to separate Components and Component Peers. Java applications can run on different platforms, so the client code should be able to create a Component without committing to a concrete implementation. The Components and Component Peers are represented as two different class/interface hierarchies. Every AWT Component sub class has a corresponding Component Peer sub interface with which it can communicate. Platform specific classes implement these Component Peer interfaces.

Other known uses (from GoF book on DesignPatterns)

The ET++ Window/WindowPort design extends theBridge Pattern in that the WindowPort also keeps a reference back to the Window. The WindowPort implementor class uses this reference to notify Window about WindowPort-specific events : the arrival of input events, window resizes, etc.

NeXT's AppKit uses the Bridge Pattern in the implementation and display of graphic images.

Bridge and Strategy

Often, the Strategy Pattern is confused with theBridge Pattern. Even though, these twopatterns are similar in structure, they are trying to solve two differentdesign problems. Strategy is mainly concerned in encapsulating algorithms, whereasBridge decouples the abstraction from the implementation, to provide different implementation for the same abstraction.

The article Applying Strategy Pattern in C++ Applications talks about the StrategyPattern in detail.

Bridge and Adapter

The structure of the Adapter Pattern (object adapter) may look similar to theBridge Pattern. However, the adapter is meant to change the interface of an existing object and is mainly intended to make unrelated classes work together.

Summary

This article not only presented what a BridgePattern is but also went into why and when it is really needed. To summarize, pure inheritance hardwires the abstraction and the implementation.Bridge Pattern can be used when an abstraction can have different implementations and when both of them can vary independently.

Acknowledgments

Special thanks to my friend Sree Meenakshi for her helpful suggestions in improving the clarity and presentation of this article.

Listing 1 - CImage and CImageImp class declarations containing important class members

class CImage
{
    // Method declarations
    public :
        virtual INT Load( LPCSTR, CRuntimeClass * )    = 0;
        virtual INT Show( CWnd *, WPARAM );

    // Data members
    protected :
        CImageImp    * m_pImageImp;
};

class CBmpImage : public CImage
{
    // Method declarations
    public :
        virtual INT Load( LPCSTR, CRuntimeClass * );
};

class CImageImp : public CObject
{
    // Method declarations
    public :
        virtual INT    InitImageInfo( LPSTR )           = 0;
        virtual BOOL    PaintImage( CWnd *, CRect * )   = 0;

    // Attributes
    public :
        LPBYTE      m_pImage;
        LONG        m_lNormalWidth;
        LONG        m_lNormalHeight;
};

class CWinImp : public CImageImp
{
    // Method declarations
    public :
        INT        InitImageInfo( LPSTR );
        BOOL       PaintImage( CWnd *, CRect * );

    // Attributes
    protected :
        BYTE        * m_pBmi;
        CPalette    * m_pPalette;
};

Listing 2 - Implementation of Show and Load methods of CImage and CBmpImage classes

INT CImage::Show( CWnd * pWnd, WPARAM wParam )
{
    // Step 1 - Check and delegate this method to m_pImageImp
    ASSERT( m_pImageImp != NULL );
    return m_pImageImp->PaintImage( pWnd, ( CRect * ) wParam );
}

INT CBmpImage::Load( LPCSTR lpszFileName, CRuntimeClass * pRuntimeClass )
{
    // Some initialization code before creating image implementation object
    …
    …
    //  Initialize image information, after creating image implementation object
    m_pImageImp = ( CImageImp * ) pRuntimeClass->CreateObject();
    if( m_pImageImp == NULL )
    {
        …
        …
        return FAILURE;
    }
    m_pImageImp->InitImageInfo(..);
    …
    …
    return SUCCESS;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值