31
CSCI-4448 Boese Headline Bridge Pattern CSCI-4448 - Boese

Headline - Piazza

Embed Size (px)

Citation preview

CSCI-4448 Boese

Headline

Bridge Pattern

CSCI-4448 - Boese

CSCI-4448 Boese

Objectives

• Definition

• Why

• How

• Bridge Examples

– User Interfaces

– Tic tac toe!

• Design Considerations

• Comparisons with Adapter

CSCI-4448 Boese

Definition

“Decouple an abstraction from its implementation

so that the two can vary independently.”

-Gang of Four

CSCI-4448 Boese

Definition

• Name “Bridge” – A device that is used to connect two pieces of equipment

that were not designed to be connected

Abstraction Implementation

• Intent – Decouple an abstraction from an implementation

– Allows these two perspective of a class to be developed independently

CSCI-4448 Boese

Definition Abstraction

• “What you want your code to do”

– Draw a 3D sphere

– Draw a 3D cone

– Draw a 3D snowman

Implementation

• “The many ways that happen”

• OpenGL

– glVertex

• Direct3D

– Triangles

• Raytracing

– Shperes

CSCI-4448 Boese

Why a Bridge?

• Inheritance is a typical method for providing an

abstract representation of something, and

allowing for varying details using concrete

subclasses

• We can work with our objects in terms of the

abstract class, but specialize to the desired

details of a specific subclass

• So, why introduce yet another class for this

concept? Isn’t it inherent in OO languages?

CSCI-4448 Boese

Inheritance Problem

• Lump all the implementation details into a single

abstraction • Shape3D is the abstraction, Sphere, Code and Snowman

are implementations

Abstract

Concept

Concrete Versions

CSCI-4448 Boese

Inheritance Problem

• But that isn’t the full implementation details! • Need to have Raytaced Snowmen and Direct3D cones…

Abstract Concept More Specific

Abstract Concept

Concrete

Implementations

Concrete

Implementations

CSCI-4448 Boese

Inheritance Problem

• Class explosion! • 3 possible shapes x 3 possible rendering methods =

9 concrete classes and 4 abstract classes

– Add in a TextureMap and Shader options

• RayTraceSnowmanWithTextureMapAndNoShader

• 3 shapes x 3 rendering methods x 2 texture maps x 2

shaders = 36 concrete classes, 28 abstract classes

• Add a single abstract method to the original Shape3D

class, need to update 64 classes!!!

• This is one reason why to Favor Delegation

over Inheritance

CSCI-4448 Boese

Why Use a Bridge?

• Inheritance leads us down a very dangerous

path…

– As features get added, the inheritance hierarchy grows

exponentially in number of implementation details

– Also, the actual class that gets instantiated is bound to

the implementation details

• Can’t dynamically change a RaytraceSnowman to

OpenGLSnowman…

– Violates the Single Responsibility Principle

• A class should have only one reason to change (subclass)

• Our classes change based on shape, rendering method,

shader and texture map option…

CSCI-4448 Boese

Bridge Pattern – Why

• Avoid permanent binding between an

abstraction and its implementation

– Can change from raytraced Snowman to OpenGL

Snowman during run-time

• Otherwise, change instance from RayTracedSnowman to

OpenGLSnowman and recompile

• Extend abstraction and implementation

independently • Add a new shape to the family of Shape3D, don’t need to

change the various rendering classes

• Add a new rendering method, doesn’t affect the shapes

CSCI-4448 Boese

Bridge Pattern - Why

• Cohesion – a class should encapsulate a single

changeable concept

– Shape3D instances should be responsible for

geometry-related tasks only

– Rendering should be encapsulated into a separate

set of classes

• Reduce maintenance costs

– Reduced number of classes

• Pure Inheritance – product of the number of features

• Bridge – sum of the number of features

CSCI-4448 Boese

How

CSCI-4448 Boese

Bridge Pattern - Participants

• Abstraction – Defines the abstraction’s interface

– Maintains reference to an instance of implementor

• Implementor – Defines the interface for implementation classes

– Doesn’t need to correspond to Abstraction’s interface • Usually, consists of more primitive operations

• Refined Abstraction – Extends interface defined by abstraction

• Concrete Implementation – Implements the Implementor interface

CSCI-4448 Boese

Bridge - Structure

CSCI-4448 Boese

Example

Tic-tac-toe and Connect 4

CSCI-4448 Boese

Game Example – Drawing the Board

• Recall the original Tic-tac-toe game

– Created a nice procedural version for Tic-tac-toe

– Then, added different games

• Connect 4, Battleship, etc.

CSCI-4448 Boese

Game Example

• Then, need to draw each of these games on

different platform • Graphics, Swing,

Android, iOS, …

• Interfaces: Graphic vs Swing

• Swing: paintComponent

• Graphic: drawLine

CSCI-4448 Boese

And if we add another feature…

CSCI-4448 Boese

Apply Bridge Pattern

• Obviously, we’re going down the wrong path

– Using inheritance to capture every implementation

detail

– Abstractly, we want to be able to draw the different

types of boards

• Requires a certain set of operations

– On the implementation level, we want to be able to

draw a common set of primitives, regardless of what

we’re drawing on

• Requires a possibly different set of operations

CSCI-4448 Boese

Defining Abstraction

• Abstract operations • displayLine, displayCircle, displayText, displayImage

• Tic-tac-toe operations • drawX, drawO, drawGrid

• Uses displayLine and displayText

• Connect 4 operations • drawBackground and drawToken

• uses displayImage and displayCircle

Specialized operations of

the various abstractions,

NOT implementation

operations!

CSCI-4448 Boese

Defining Implementor

• What are the common drawing operations

desired?

– drawCircle, drawImage, drawText, drawLine…

• Is this the same as the Abstraction’s interface?

– Not really. These are things the Implementors are

capable of doing, not necessarily what the Abstraction

needs them to do

• Add displayRectangle to Abstraction. Abstraction

implements this by calling drawLine four times, not by

adding a drawRectangle in the Implementor

CSCI-4448 Boese

Putting it all together

Bridge

CSCI-4448 Boese

Putting it all together

• The Game Client uses GameBoard – Polymorphic, doesn’t care what the GameBoard is, just that it can drawBoard()

• Specific GameBoard instances use refined Graphics abstraction to do the drawing

– Contains common drawing operations and specific operations for each game

• Graphics doesn’t care what’s actually being drawn on – Bridge decouples the implementation details

CSCI-4448 Boese

Design Considerations

CSCI-4448 Boese

Consequences

• Decouples Interface and Implementation – Implementation details are not bound to a particular

interface

• Can change an object’s implementation at run-time!

– Eliminate compile-time dependencies

• Change Implementor – no need to recompile Abstraction

– Provide a layering between high-level and low-level components

• Improves extensibility – Just refine the abstraction, or add an implementor,

independently

• Hide implementation details from clients – Data Hiding is just one form of encapsulation

CSCI-4448 Boese

Bridge vs. Adapter

CSCI-4448 Boese

Bridge vs. Adapter

• Bridge decouples an abstraction from its

implementation, allowing the abstraction’s

interface to work with an implementation

interface

• Adapter allows some client’s interface to interact

with some implementation’s interface

• What’s the difference…?

CSCI-4448 Boese

Bridge vs. Adapter

• From GoF “The key difference between these patterns lie in their intents. Adapter focuses on resolving incompatibilities between two existing interfaces. It doesn’t focus on how those interfaces are implemented, nor does it consider how they might evolve independently. It’s a way of making two independently designed classes work together without reimplementing one or the other. Bridge, on the other hand, bridges an abstraction and its (potentially numerous) implementations. It provides a stable interface to clients even as it lets you vary the classes that implement it. It also accommodates new implementations as the system evolves.”

CSCI-4448 Boese

Bridge vs. Adapter

• TL;DR

– The purpose of an adapter is to allow two existing

classes work together

• The coupling was unforseen

– The purpose of a bridge is to allow the abstraction of

something to evolve separate from the

implementation

• The decoupling process is performed up-front

– In practice, these occur in different stages of SLC

• Adapter makes things work after they’re designed

• Bridge makes things work before they’re designed

• Adapter may become Bridge in future refactorings

CSCI-4448 Boese

Further Reading

• Design Patterns pp. 151 - 161

• Design Patterns Explained Chapter 10

pp. 159-191