Difference between revisions of "Bridge"
(→Related Patterns) |
(→Consequences) |
||
(43 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | The '''Bridge pattern''' allows you to decouple an abstraction from its implementation so both can vary independently. The Bridge pattern is also know as Handle/Body.<br/><br/> | ||
+ | |||
__TOC__ | __TOC__ | ||
− | |||
− | |||
== UML Structure == | == UML Structure == | ||
Line 16: | Line 16: | ||
== Applicability == | == Applicability == | ||
The Bridge pattern is applicable when: | The Bridge pattern is applicable when: | ||
− | <ul> | + | <ul><ul> |
<li>we want to avoid a permanent binding between an abstraction and its implementation</li> | <li>we want to avoid a permanent binding between an abstraction and its implementation</li> | ||
<li>both the abstractions and their implementations should be extensible by subclassing</li> | <li>both the abstractions and their implementations should be extensible by subclassing</li> | ||
Line 22: | Line 22: | ||
<li>we want to hide the implementation of an abstraction completely from clients</li> | <li>we want to hide the implementation of an abstraction completely from clients</li> | ||
<li>we want to share an implementation among multiple objects, and this fact should be hiddent from the client</li> | <li>we want to share an implementation among multiple objects, and this fact should be hiddent from the client</li> | ||
− | </ul> | + | <li>useful in graphic and windowing systems that need to run over multiple platforms</li> |
+ | <li>useful any time we need to vary an interface and an implementation in different ways</li> | ||
+ | </ul></ul> | ||
== Participants == | == Participants == | ||
− | The classes and/or objects participating in this pattern are: | + | The classes and/or objects participating in this pattern are: |
<ul> | <ul> | ||
<li>'''Abstraction'''</li> | <li>'''Abstraction'''</li> | ||
Line 32: | Line 34: | ||
<li>maintains a reference to an object of type Implementor</li> | <li>maintains a reference to an object of type Implementor</li> | ||
</ul> | </ul> | ||
− | <li>'''Refined Abstraction</li> | + | <li>'''Refined Abstraction'''</li> |
<ul> | <ul> | ||
<li>extends the interface defined by Abstraction.</li> | <li>extends the interface defined by Abstraction.</li> | ||
Line 38: | Line 40: | ||
<li>'''Implementor'''</li> | <li>'''Implementor'''</li> | ||
<ul> | <ul> | ||
− | <li>defines the interface for implementation classes. This interface does not have to correspond exactly to Abstraction's interface; in fact the two interfaces can be quite different. The Implementation interface provides only primitive operations, and Abstraction defines high-level operations based on these primitives. | + | <li>defines the interface for implementation classes. This interface does not have to correspond exactly to Abstraction's interface; in fact the two interfaces can be quite different. The Implementation interface provides only primitive operations, and Abstraction defines high-level operations based on these primitives.</li> |
</ul> | </ul> | ||
<li>'''Concrete Implementor'''</li> | <li>'''Concrete Implementor'''</li> | ||
Line 48: | Line 50: | ||
− | == | + | == Code Examples == |
− | The Bridge pattern | + | |
− | < | + | ====PHP Java Bridge in Java Code==== |
− | < | + | The following code is found on "Oregon State University Open Source Lab" which illustrate the Bridge pattern. The following is a part of the code which allows user to vary the class loader. <br/> |
− | + | Source: http://gentoo.osuosl.org/distfiles/php-java-bridge_2.0.8.tar.bz2<br/> | |
− | + | File: JavaBridge.java<br/> | |
− | + | <pre> | |
− | + | package php.java.bridge; | |
− | < | + | |
− | < | + | |
− | </ | + | /** |
+ | * A bridge pattern which allows us to vary the class loader as run-time. | ||
+ | * The decision is based on whether we are allowed to use a dynamic | ||
+ | * classloader or not (loader==null). | ||
+ | * @see DynamicJavaBridgeClassLoader | ||
+ | * @see java.lang.ClassLoader | ||
+ | */ | ||
+ | public class JavaBridgeClassLoader { | ||
+ | |||
+ | DynamicJavaBridgeClassLoader cl = null; | ||
+ | ClassLoader scl = null; | ||
+ | private JavaBridge bridge; | ||
+ | |||
+ | public JavaBridgeClassLoader(JavaBridge bridge, DynamicJavaBridgeClassLoader loader) { | ||
+ | this.bridge = bridge; | ||
+ | this.cl = loader; | ||
+ | |||
+ | if(this.cl==null) | ||
+ | this.scl = bridge.getClass().getClassLoader(); | ||
+ | else | ||
+ | cl.clear(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Append the path to the current library path | ||
+ | * @param path A file or url list, separated by ';' | ||
+ | * @param extensionDir Usually ini_get("extension_dir"); | ||
+ | */ | ||
+ | public void updateJarLibraryPath(String path, String extensionDir) { | ||
+ | if(cl==null) { | ||
+ | bridge.logMessage("You don't have permission to call java_set_library_path() | ||
+ | or java_require(). Please store your libraries in the lib | ||
+ | folder within JavaBridge.war"); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | cl.updateJarLibraryPath(path, extensionDir); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Only for internal use | ||
+ | * @return the classloader | ||
+ | */ | ||
+ | public ClassLoader getClassLoader() { | ||
+ | if(cl!=null) return (ClassLoader)cl; | ||
+ | return scl; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * reset loader to the initial state | ||
+ | */ | ||
+ | public void reset() { | ||
+ | if (cl!=null) cl.reset(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * clear all loader caches but | ||
+ | * not the input vectors | ||
+ | */ | ||
+ | public void clearCaches() { | ||
+ | if (cl!=null) cl.clearCaches(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Load a class. | ||
+ | * @param name The class, for example java.lang.String | ||
+ | * @return the class | ||
+ | * @throws ClassNotFoundException | ||
+ | */ | ||
+ | public Class forName(String name) throws ClassNotFoundException { | ||
+ | if(cl==null) return Class.forName(name, false, scl); | ||
+ | return cl.loadClass(name); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ====Real-world Sample code in C#==== | ||
+ | This real-world code demonstrates the Bridge pattern in which a BusinessObject abstraction is decoupled from the implementation in DataObject. The DataObject implementations can evolve dynamically without changing any clients. <br/> | ||
+ | Source: http://www.dofactory.com/Patterns/PatternBridge.aspx | ||
+ | <pre> | ||
+ | // Bridge pattern -- Real World example | ||
+ | |||
+ | using System; | ||
+ | using System.Collections; | ||
+ | |||
+ | namespace DoFactory.GangOfFour.Bridge.RealWorld | ||
+ | { | ||
+ | |||
+ | // MainApp test application | ||
+ | |||
+ | class MainApp | ||
+ | { | ||
+ | static void Main() | ||
+ | { | ||
+ | // Create RefinedAbstraction | ||
+ | Customers customers = | ||
+ | new Customers("Chicago"); | ||
+ | |||
+ | // Set ConcreteImplementor | ||
+ | customers.Data = new CustomersData(); | ||
+ | |||
+ | // Exercise the bridge | ||
+ | customers.Show(); | ||
+ | customers.Next(); | ||
+ | customers.Show(); | ||
+ | customers.Next(); | ||
+ | customers.Show(); | ||
+ | customers.New("Henry Velasquez"); | ||
+ | |||
+ | customers.ShowAll(); | ||
+ | |||
+ | // Wait for user | ||
+ | Console.Read(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // "Abstraction" | ||
+ | |||
+ | class CustomersBase | ||
+ | { | ||
+ | private DataObject dataObject; | ||
+ | protected string group; | ||
+ | |||
+ | public CustomersBase(string group) | ||
+ | { | ||
+ | this.group = group; | ||
+ | } | ||
+ | |||
+ | // Property | ||
+ | public DataObject Data | ||
+ | { | ||
+ | set{ dataObject = value; } | ||
+ | get{ return dataObject; } | ||
+ | } | ||
+ | |||
+ | public virtual void Next() | ||
+ | { | ||
+ | dataObject.NextRecord(); | ||
+ | } | ||
+ | |||
+ | public virtual void Prior() | ||
+ | { | ||
+ | dataObject.PriorRecord(); | ||
+ | } | ||
+ | |||
+ | public virtual void New(string name) | ||
+ | { | ||
+ | dataObject.NewRecord(name); | ||
+ | } | ||
+ | |||
+ | public virtual void Delete(string name) | ||
+ | { | ||
+ | dataObject.DeleteRecord(name); | ||
+ | } | ||
+ | |||
+ | public virtual void Show() | ||
+ | { | ||
+ | dataObject.ShowRecord(); | ||
+ | } | ||
+ | |||
+ | public virtual void ShowAll() | ||
+ | { | ||
+ | Console.WriteLine("Customer Group: " + group); | ||
+ | dataObject.ShowAllRecords(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // "RefinedAbstraction" | ||
+ | |||
+ | class Customers : CustomersBase | ||
+ | { | ||
+ | // Constructor | ||
+ | public Customers(string group) : base(group) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public override void ShowAll() | ||
+ | { | ||
+ | // Add separator lines | ||
+ | Console.WriteLine(); | ||
+ | Console.WriteLine ("------------------------"); | ||
+ | base.ShowAll(); | ||
+ | Console.WriteLine ("------------------------"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // "Implementor" | ||
+ | |||
+ | abstract class DataObject | ||
+ | { | ||
+ | public abstract void NextRecord(); | ||
+ | public abstract void PriorRecord(); | ||
+ | public abstract void NewRecord(string name); | ||
+ | public abstract void DeleteRecord(string name); | ||
+ | public abstract void ShowRecord(); | ||
+ | public abstract void ShowAllRecords(); | ||
+ | } | ||
+ | |||
+ | // "ConcreteImplementor" | ||
+ | |||
+ | class CustomersData : DataObject | ||
+ | { | ||
+ | private ArrayList customers = new ArrayList(); | ||
+ | private int current = 0; | ||
+ | |||
+ | public CustomersData() | ||
+ | { | ||
+ | // Loaded from a database | ||
+ | customers.Add("Jim Jones"); | ||
+ | customers.Add("Samual Jackson"); | ||
+ | customers.Add("Allen Good"); | ||
+ | customers.Add("Ann Stills"); | ||
+ | customers.Add("Lisa Giolani"); | ||
+ | } | ||
+ | |||
+ | public override void NextRecord() | ||
+ | { | ||
+ | if (current <= customers.Count - 1) | ||
+ | { | ||
+ | current++; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public override void PriorRecord() | ||
+ | { | ||
+ | if (current > 0) | ||
+ | { | ||
+ | current--; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public override void NewRecord(string name) | ||
+ | { | ||
+ | customers.Add(name); | ||
+ | } | ||
+ | |||
+ | public override void DeleteRecord(string name) | ||
+ | { | ||
+ | customers.Remove(name); | ||
+ | } | ||
+ | |||
+ | public override void ShowRecord() | ||
+ | { | ||
+ | Console.WriteLine(customers[current]); | ||
+ | } | ||
+ | |||
+ | public override void ShowAllRecords() | ||
+ | { | ||
+ | foreach (string name in customers) | ||
+ | { | ||
+ | Console.WriteLine(" " + name); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ====Sample Code in Java==== | ||
+ | The following is an easier to follow example of the Bridge pattern:<br/> | ||
+ | Source: http://en.wikipedia.org/wiki/Bridge_pattern | ||
+ | <pre> | ||
+ | import java.util.*; | ||
+ | /** "Implementor" */ | ||
+ | interface DrawingAPI { | ||
+ | public void drawCircle(double x, double y, double radius); | ||
+ | } | ||
+ | |||
+ | /** "ConcreteImplementor" 1/2 */ | ||
+ | class DrawingAPI1 implements DrawingAPI { | ||
+ | public void drawCircle(double x, double y, double radius) { | ||
+ | System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius); | ||
+ | } | ||
+ | } | ||
− | + | /** "ConcreteImplementor" 2/2 */ | |
− | + | class DrawingAPI2 implements DrawingAPI { | |
− | + | public void drawCircle(double x, double y, double radius) | |
− | + | { System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius); } | |
− | + | } | |
− | |||
− | |||
− | |||
− | + | /** "Abstraction" */ | |
+ | interface Shape { | ||
+ | public void draw(); // low-level | ||
+ | public void resizeByPercentage(double pct); // high-level | ||
+ | } | ||
+ | /** "Refined Abstraction" */ | ||
+ | class CircleShape implements Shape { | ||
+ | private double x, y, radius; | ||
+ | private DrawingAPI drawingAPI; | ||
+ | public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) { | ||
+ | this.x = x; this.y = y; this.radius = radius; | ||
+ | this.drawingAPI = drawingAPI; | ||
+ | } | ||
+ | // low-level i.e. Implementation specific | ||
+ | public void draw() { drawingAPI.drawCircle(x, y, radius); } | ||
+ | // high-level i.e. Abstraction specific | ||
+ | public void resizeByPercentage(double pct) { radius *= pct; } | ||
+ | } | ||
− | == | + | /** "Client" */ |
+ | class BridgePattern { | ||
+ | public static void main(String[] args) { | ||
+ | Shape[] shapes = new Shape[2]; | ||
+ | shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1()); | ||
+ | shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2()); | ||
+ | for (Shape shape : shapes) { | ||
+ | shape.resizeByPercentage(2.5); | ||
+ | shape.draw(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
− | == | + | == References == |
+ | <ul> | ||
+ | <li>[http://en.wikipedia.org/wiki/Bridge_pattern Wikipedia.com - Bridge pattern]</li> | ||
+ | <li>[http://www.developer.com/design/article.php/1502691 A Survey of Common Design Patterns]</li> | ||
+ | <li>[http://www.dofactory.com/Patterns/PatternBridge.aspx DoFactory.com - Bridge Design Pattern]</li> | ||
+ | <li>[http://home.earthlink.net/~huston2/dp/all_uml.html Design Class Diagrams - Bridge]</li> | ||
+ | <li>[http://gentoo.osuosl.org/distfiles/php-java-bridge_2.0.8.tar.bz2 Oregon State University Open Source Lab - JavaBridge.java]</li> | ||
+ | <li>Gamma, E., Helm, R., Johnson, R., Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2 </li> | ||
+ | </ul> |
Latest revision as of 08:52, 11 June 2007
The Bridge pattern allows you to decouple an abstraction from its implementation so both can vary independently. The Bridge pattern is also know as Handle/Body.
Contents
UML Structure
Here is an example of the Bridge pattern.
Source: Downloaded from http://www.dofactory.com/Patterns/PatternBridge.aspx
Here is another example of the Bridge pattern where it decouples the abstraction from its implementation so that the two can vary independently
Source: Downloaded from http://home.earthlink.net/~huston2/dp/all_uml.html
Applicability
The Bridge pattern is applicable when:
- we want to avoid a permanent binding between an abstraction and its implementation
- both the abstractions and their implementations should be extensible by subclassing
- changes in the implementation of an abstraction should have no impact on clients
- we want to hide the implementation of an abstraction completely from clients
- we want to share an implementation among multiple objects, and this fact should be hiddent from the client
- useful in graphic and windowing systems that need to run over multiple platforms
- useful any time we need to vary an interface and an implementation in different ways
Participants
The classes and/or objects participating in this pattern are:
- Abstraction
- defines the abstraction's interface
- maintains a reference to an object of type Implementor
- Refined Abstraction
- extends the interface defined by Abstraction.
- Implementor
- defines the interface for implementation classes. This interface does not have to correspond exactly to Abstraction's interface; in fact the two interfaces can be quite different. The Implementation interface provides only primitive operations, and Abstraction defines high-level operations based on these primitives.
- Concrete Implementor
- implements the Implementor interface and defines its concrete implementation.
Code Examples
PHP Java Bridge in Java Code
The following code is found on "Oregon State University Open Source Lab" which illustrate the Bridge pattern. The following is a part of the code which allows user to vary the class loader.
Source: http://gentoo.osuosl.org/distfiles/php-java-bridge_2.0.8.tar.bz2
File: JavaBridge.java
package php.java.bridge; /** * A bridge pattern which allows us to vary the class loader as run-time. * The decision is based on whether we are allowed to use a dynamic * classloader or not (loader==null). * @see DynamicJavaBridgeClassLoader * @see java.lang.ClassLoader */ public class JavaBridgeClassLoader { DynamicJavaBridgeClassLoader cl = null; ClassLoader scl = null; private JavaBridge bridge; public JavaBridgeClassLoader(JavaBridge bridge, DynamicJavaBridgeClassLoader loader) { this.bridge = bridge; this.cl = loader; if(this.cl==null) this.scl = bridge.getClass().getClassLoader(); else cl.clear(); } /** * Append the path to the current library path * @param path A file or url list, separated by ';' * @param extensionDir Usually ini_get("extension_dir"); */ public void updateJarLibraryPath(String path, String extensionDir) { if(cl==null) { bridge.logMessage("You don't have permission to call java_set_library_path() or java_require(). Please store your libraries in the lib folder within JavaBridge.war"); return; } cl.updateJarLibraryPath(path, extensionDir); } /** * Only for internal use * @return the classloader */ public ClassLoader getClassLoader() { if(cl!=null) return (ClassLoader)cl; return scl; } /** * reset loader to the initial state */ public void reset() { if (cl!=null) cl.reset(); } /** * clear all loader caches but * not the input vectors */ public void clearCaches() { if (cl!=null) cl.clearCaches(); } /** * Load a class. * @param name The class, for example java.lang.String * @return the class * @throws ClassNotFoundException */ public Class forName(String name) throws ClassNotFoundException { if(cl==null) return Class.forName(name, false, scl); return cl.loadClass(name); } }
Real-world Sample code in C#
This real-world code demonstrates the Bridge pattern in which a BusinessObject abstraction is decoupled from the implementation in DataObject. The DataObject implementations can evolve dynamically without changing any clients.
Source: http://www.dofactory.com/Patterns/PatternBridge.aspx
// Bridge pattern -- Real World example using System; using System.Collections; namespace DoFactory.GangOfFour.Bridge.RealWorld { // MainApp test application class MainApp { static void Main() { // Create RefinedAbstraction Customers customers = new Customers("Chicago"); // Set ConcreteImplementor customers.Data = new CustomersData(); // Exercise the bridge customers.Show(); customers.Next(); customers.Show(); customers.Next(); customers.Show(); customers.New("Henry Velasquez"); customers.ShowAll(); // Wait for user Console.Read(); } } // "Abstraction" class CustomersBase { private DataObject dataObject; protected string group; public CustomersBase(string group) { this.group = group; } // Property public DataObject Data { set{ dataObject = value; } get{ return dataObject; } } public virtual void Next() { dataObject.NextRecord(); } public virtual void Prior() { dataObject.PriorRecord(); } public virtual void New(string name) { dataObject.NewRecord(name); } public virtual void Delete(string name) { dataObject.DeleteRecord(name); } public virtual void Show() { dataObject.ShowRecord(); } public virtual void ShowAll() { Console.WriteLine("Customer Group: " + group); dataObject.ShowAllRecords(); } } // "RefinedAbstraction" class Customers : CustomersBase { // Constructor public Customers(string group) : base(group) { } public override void ShowAll() { // Add separator lines Console.WriteLine(); Console.WriteLine ("------------------------"); base.ShowAll(); Console.WriteLine ("------------------------"); } } // "Implementor" abstract class DataObject { public abstract void NextRecord(); public abstract void PriorRecord(); public abstract void NewRecord(string name); public abstract void DeleteRecord(string name); public abstract void ShowRecord(); public abstract void ShowAllRecords(); } // "ConcreteImplementor" class CustomersData : DataObject { private ArrayList customers = new ArrayList(); private int current = 0; public CustomersData() { // Loaded from a database customers.Add("Jim Jones"); customers.Add("Samual Jackson"); customers.Add("Allen Good"); customers.Add("Ann Stills"); customers.Add("Lisa Giolani"); } public override void NextRecord() { if (current <= customers.Count - 1) { current++; } } public override void PriorRecord() { if (current > 0) { current--; } } public override void NewRecord(string name) { customers.Add(name); } public override void DeleteRecord(string name) { customers.Remove(name); } public override void ShowRecord() { Console.WriteLine(customers[current]); } public override void ShowAllRecords() { foreach (string name in customers) { Console.WriteLine(" " + name); } } } }
Sample Code in Java
The following is an easier to follow example of the Bridge pattern:
Source: http://en.wikipedia.org/wiki/Bridge_pattern
import java.util.*; /** "Implementor" */ interface DrawingAPI { public void drawCircle(double x, double y, double radius); } /** "ConcreteImplementor" 1/2 */ class DrawingAPI1 implements DrawingAPI { public void drawCircle(double x, double y, double radius) { System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius); } } /** "ConcreteImplementor" 2/2 */ class DrawingAPI2 implements DrawingAPI { public void drawCircle(double x, double y, double radius) { System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius); } } /** "Abstraction" */ interface Shape { public void draw(); // low-level public void resizeByPercentage(double pct); // high-level } /** "Refined Abstraction" */ class CircleShape implements Shape { private double x, y, radius; private DrawingAPI drawingAPI; public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) { this.x = x; this.y = y; this.radius = radius; this.drawingAPI = drawingAPI; } // low-level i.e. Implementation specific public void draw() { drawingAPI.drawCircle(x, y, radius); } // high-level i.e. Abstraction specific public void resizeByPercentage(double pct) { radius *= pct; } } /** "Client" */ class BridgePattern { public static void main(String[] args) { Shape[] shapes = new Shape[2]; shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1()); shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2()); for (Shape shape : shapes) { shape.resizeByPercentage(2.5); shape.draw(); } } }
References
- Wikipedia.com - Bridge pattern
- A Survey of Common Design Patterns
- DoFactory.com - Bridge Design Pattern
- Design Class Diagrams - Bridge
- Oregon State University Open Source Lab - JavaBridge.java
- Gamma, E., Helm, R., Johnson, R., Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2