LasaterConsulting.com

Design Patterns
Type-Object | Properties | Strategy | Entity Relationship | Interpreter | Project Usage | AOM UI

Bridge & Adapter Pattern

 

Introduction

Developers in .NET sometimes come from scripting language environments that are not strong on Object Oriented Methodologies. OO methodologies like refactoring and using design patterns can be intimidating and the value for the developer is hard to see. What developers new to the OO world, and even more seasoned developers need to understand is that good design is not beyond scope in any project. Good habits simply must be learned that will make the developer a better designer.

In that mind set, I am submitting the first of several "how to" real world examples, used in my professional life, on how to use patterns in a simple, easy to follow manner. Many of us have ended up either writing or working on in-line, scripting based code. Taking some ideologies from refactoring methodology, this is not necessarily a bad thing. Of course, when you are first writing an algorithm or a series of logic statements, it seems easier to start with if....then....else. But as you continue to expand this code to more complex forms, this model can quickly become unmanageable. This is where refactoring and design pattern methodology can play an important and useful part in simplifying, enhancing and making code more useable (and understandable) to those who practice good OO design.

This article details a real world example of how to use the bridge and adapter patterns to help you develop a method to allow decoupling of an abstraction class from it's implementation class or to allow a change of implementation from an interface. This is generally used when you have a class that might change, but you don't want those changes to affect a number of clients.

NOTE: This article is for helping developers understand how to convert their inline code to patterns better, not provide strict examples on patterns. There are more than enough strict pattern examples, but to facilitate developers and others out there who are not familiar with patterns, I have written this and other articles like this.

The adaptor and a bridge patterns are very similar. The main difference between an adaptor and a bridge pattern, is that a bridge pattern serves to decouple an abstraction class from it's implementation, and an adaptor pattern converts the interface between classes with less inheritance.

I have reworked this article, from demand from my readers, to illustrate the differences between the adaptor and a bridge patterns. We will start with the bridge pattern, and at the end of the article I will show the difference between the two.

BackGround

Using a bridge or an adapter pattern is a form of encapsulation, which allows a developer to better control the way a client code base deals with the implementation class structure.

How to use the code

We start this example with the class WorkerObject, which we see has one constructor and one method. If this object changes or we want to use another object in our client code instead of WorkerObject, we might have to re-write or re-compile a lot of that code. To decouple this object from the client would seem to make sense.

                
        public class WorkerObject
        {            
            
            public WorkerObject()
            {
                
            }                    
            
            public virtual void DoWork()
            {
                Console.WriteLine("Working...");
            }
        }


        .....client code

        WorkerObject obj = new WorkerObject();
        obj.DoWork();

So, to solve this problem, we need to create a bridge or adapter class that will allow a client to access methods on this object, but also makes sure changes in this object will not to break the client object's calls to the methods, even if we change the WorkerObject for another object. Here we see the Bridge class. This class can be called and the standard implementation of WorkerObject's method DoWork will be executed. The Bridge object is an example of the bridge pattern because of it's access to it's implementation object by inherited objects, and it's inherited relationships.

NOTE: Notice the ImplementationObject accessor, that allows access to the implementation object. Using this accessor in inherited classes, we can change the WorkerObject to an inherited class, with different functionality, without affecting any client code, or changing the base WorkerObject class.

    
        class Bridge

        {
            private WorkerObject _workerObject;

            
            public Bridge
            {
                ImplementationObject = new WorkerObject();

            }
        
            protected WorkerObject ImplementationObject
            {
                get{return _workerObject;}
                set{_workerObject = value;}

            }


            public virtual void DoWork()
            {
                ImplementationObject.DoWork();
            }

        }

Now lets say we wanted to use a different object other than WorkerObject for our implementation class. We still want to provide a standard ImplementationObject accessor, so we create a class inherited from WorkerObject, named LoaferObject, which overrides the DoWork method, in effect changing the way the object functions. We set this object as the implementation object, giving new functionality, while still offering the same method access to the client.

                
        public class LoaferObject : WorkerObject
        {            
            
            public LoaferObject()
            {
                
            }                    
            
            public override void DoWork()
            {
                Console.WriteLine("Loafing...");
            }
        }
        
        
        class AlternateBridge : Bridge
        {
            public AlternateBridge()
            {
                ImplementationObject = new LoaferObject();
            }
        
            public virtual void DoWork()
            {
                ImplementationObject.DoWork();
            }

        }

Now lets say we did not want to use WorkerObject at all, but instead wanted to house some other functionality in the DoWork method, such as another object or different code. We can do this easily, just by inheriting from Bridge and changing the DoWork method's code in any way we like.

    
        class TotallyDifferntBridge : Bridge

        {
            public TotallyDifferntBridge()
            {
                ImplementationObject = null;
            }
        
            public virtual void DoWork()
            {
                TotallyDifferentObject diffObj = new TotallyDifferentObject();

                diffObj.DoWork();
            }

        }
        

Now lets see how this would affect the client code. Well we started with a single class WorkerObject, and a call to it's method DoWork. To make the client code more flexible, and to get the proper object implementation for each client instance, we might call the Bridge object, or it's inherited classes from a factory, which based on some state or parameter or client type gets the correct adapter object.

    
        .....client code calls the factory


        Bridge adapter = BridgeFactory.GetAdapter();
        adapter.DoWork();

Another way of using the Bridge class to keep client code flexible and not change basic class functionality is to simply construct the class directly for each client instance it is needed, constructing different polymorphic inherited instances for different needs.

    
        .....client code


        Bridge adapter = new Bridge();
        adapter.DoWork();

        
        adapter = new AlternateBridge();
        adapter.DoWork();


        adapter = new TotallyDifferntBridge();
        adapter.DoWork();



Now lets take a look at an alternate, less involved way to do the same kind of thing, with less need for inheritance, using the adaptor pattern. Here we see the Adaptor class. Notice that this class has no access to it's implementation object, hence no need for inheritance. We can provide access to the Adaptor class to client code, but have no need to give different implementations of that class outward. We might use the adaptor pattern if we foresaw changes to the implementation object that we did not want to be seen by the client, but had no immediate need to provide a range of different class types to encapsulate these changes. Also to be included in this consideration of pattern implementation is that the implementation class itself would not likely become another class altogether, where we might need to retain the old class's functionality. Basically we might use the adaptor pattern if changes to the implementation object could be accomplished with out the need for backward compatibility, or polymorphic changes to the DoWork method.

    
        class Adaptor

        {
            private WorkerObject _workerObject;

            
            public Adaptor
            {
                _workerObject = new WorkerObject();

            }
        
           

            public virtual void DoWork()
            {
                _workerObject.DoWork();
            }

        }
        

 

Points of Interest

The adapter & bridge patterns offer us another way to make our code more flexible, and gives us another option for code design. Developers new to OO may have already found they have used a version of one of these patterns in their code before, or can find a myriad of uses immediately. Like many of the more commonly used patterns, these two patterns are useful in a wide variety of code situations.

This is the fifth installment in the series I am writing on real world design patterns. All examples and the bulk of this article are taken from my professional experience as an architect. The examples given are templates only, and the designer must keep in mind that they are the ones who must decide where different patterns, if any, may be best used in their code.

Deciding to perform a refactoring effort from existing code to a pattern must be weighed on the necessity and need of the code itself. Patterns are only design templates, helpers to accommodate better overall design. I might stress that making the effort to use patterns will strengthen your overall design ability, but like your basic coding skills, it is something learned and cultivated.

If this or any other in this series on design patterns is helpful or you have questions or comments please e-mail me at chris.lasater@gmail.com.

History

This is the second revision and is the fifth installment in a series.

Related Articles

Other articles in this series include:

  1. Bridge & Adapter Pattern
  2. Chain Of Responsibility Pattern
  3. Facade Pattern
  4. Factory & Strategy Pattern
  5. Mediator Pattern
| Make a Donation to Help Build/Maintain our site! |©2004 Lasater Consulting

1