TheBridgePattern

From Pattern Repository Wiki
Jump to: navigation, search

The Bridge Pattern

Webinar Link

Contextual Forces

Motivation

Separate a varying entity from a varying behavior (aka seperate an "abstraction" from its "implementation" (1)) , so that these issues can vary independently. Sometimes we say it this way: separate what something is from what it does, where both of these things vary for different reasons.

Encapsulation

The entity variation, the behavior variation, and the relationship between these variations are encapsulated. Also, we may wish to encapsulate that the Bridge is being used at all.

Procedural Analog

Similar to a Strategy, a Bridge is analogous to the use of branching, conditional logic. However, in this case the logic is nested:

if(entityConditionA) {
     if(behaviorCondition1){
          // Algorithm A1
     } else {
          // Algorithm A2
     }
} else {
     if(behaviorCondition1){
          // Algorithm B1
     } else {
          // Algorithm B2
     }
}

Non-Software Analog

My friends and I like to go to together dinner a lot. From the waitress's point of view, we are all the same – she considers us to be patrons, and she wants the same question answered no matter which of us she is speaking with: "whatlyahave?"

We are actually all different from each other, of course, but that is not her concern. One difference that influences the outcome of this interaction is our ordering preference.

I usually get what's on special for the day. I like adventure. Christopher (also my younger brother) says I'm crazy, because he says everyone knows "the special" is the thing that's just about to go bad. He says "get the specialty of the house; they take pride in doing that right." Sure, I say, but you always get the same thing if you do that. It's an old argument.

Andrea (also my wife), likes salads, and usually orders one. However, some places don't have them, so then she needs to get something meatless, whatever that might be.

Brenner always gets the most expensive thing on the menu. He's noticed that we always divide the check equally… and well, that's Brenner.

That's one variation, or really, two. The variation of who we are, and the variation of how we order food. These do not vary independently, however. The variation in our ordering preferences is what makes us different, in the context of being restaurant patrons.

The independent variation has to do with the restaurant we're currently visiting on a given evening. For example:

Ocean Breeze: Seafood, primarily, and very experimental cuisine.

Redrocks: Barbecue, mostly meat, very large portions: not Andrea's favorite place.

So, who we are varies, and the food available varies, but each of us can use the menu in our own way to get what we want. From the waitress's point of view, she just wants an answer.

Implementation Forces

Example

Bridge 1.jpg

The Bridge can be tricky, so here is the same example using my non-software analogy above:

Bridge 2.jpg

Note that Andrea has to take some extra actions, because getSalad() is not reliable across all restaurants. Because she is isolated from everyone else, only she has to be so high-maintenance. :)

Pseudo Code

public class AbstractEntity {
     protected AbstractBehavior myBehavior;
     public AbstractEntity(AbstractBehavior aBehavior) {
           myBehavior = aBehavior;
     }
     public abstract void request();
}

public class ConcreteEntityA extends AbstractEntity {
     public ConcreteEntityA(AbstractBehavior aBehavior) {
           superclassConstructor(aBehavior);
     }
     public void request() {
           myBehavior.operation1();
     }
}

public class ConcreteEntityB extends AbstractEntity {
     public ConcreteEntityB(AbstractBehavior aBehavior) {
           superclassConstructor(aBehavior);
     }
     public void request() {
           myBehavior.operation2();
     }
}

public abstract class AbstractBehavior {
     public abstract void operation1();
     public abstract void operation2();
}

public class ConcreteBehaviorA extends AbstractBehavior {
     public void operation1() {
           // Behavior 1A
     }
     public void operation2() {
           // Behavior 2A
     }
}

public class ConcreteBehaviorB extends AbstractBehavior {
     public void operation1() {
          // Behavior 1B
     }
     public void operation2() {
          // Behavior 2B
     }
}

Questions, concerns, credibility checks

  • How likely is it that all the entities can share the same interface, without giving up any key capabilities?
  • How likely is it that behaviors can share the same interface, without giving up any key capabilities?
  • How consistently do the entities use the behaviors? If they are very orthogonal, then the interface of the AbstractBehavior will tend to be broad.
  • How likely is it that new entities that may be added later will be able to use the existing AbstractBehavior interface? If this is unlikely, then the interface will tend to grow, and perhaps bloat over time.
  • Are the behaviors likely to be stateful or stateless? If they are stateless, then they can be shared to increase efficiency and performance.

Options in implementation

Because the Bridge requires that all behaviors must share the same interface, and because it is quite common for these behaviors to come from foreign classes (device drivers, API's from other systems, etc…) then Bridges often use Adapters and Façades to bring the behaviors to a common interface. Bridge 3.jpg

Consequent Forces

Testing issues

The behavior classes will probably be testable on their own (unless they are Adapters, Proxies and/or Façades, in which case see the testing forces accompanying those patterns). However the entity classes are dependant upon behaviors, and so a Mock object can be used to control the returns from these dependencies, and also to check on the action taken upon the behavior by the entity, if this is deemed an appropriate thing to test.

Bridge 4.jpg

Cost-Benefit (gain-loss)

  1. The Bridge creates flexibility because the entities and behaviors can each vary without necessarily affecting the other.
  2. Both the Entities and Behaviors are open-closed, if we build the bridge in an object factory, which is recommended.
  3. If the Entities are highly orthoganal from one another, the Behavior interface will tend to be broad.
  4. The interface of the Behavior can require changes over time, which can cause maintenance problems. Specifically, if new Entities that may be added to the system in the future are unlikely to be satisfied with the current Behavior interface, then this interface may bloat, requiring potentially extensive maintenance.
  5. The delegation from the Entities to the Behaviors can degrade performance.