📄 pat4b-1.htm
字号:
<SCRIPT>
function setFocus() {
if ((navigator.appName != "Netscape") && (parseFloat(navigator.appVersion) == 2)) {
return;
} else {
self.focus();
}
}
</SCRIPT><HTML><HEAD> <TITLE>Bridge</TITLE></HEAD>
<BODY BGCOLOR = #FFFFFF
TEXT = #000000
onLoad="setFocus()";
>
<A NAME="top"></A>
<A NAME="Bridge"></A>
<A NAME="intent"></A>
<H2><A HREF="#alsoknownas"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Also Known As"></A> Intent</H2>
<A NAME="auto1000"></A>
<P>Decouple an abstraction from its implementation so that the two
can vary independently.</P>
<A NAME="alsoknownas"><A>
<H2><A HREF="#motivation"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Motivation"></A> Also Known As</H2>
<A NAME="auto1001"></A>
<P>Handle/Body</P>
<A NAME="motivation"></A>
<H2><A HREF="#applicability"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0
ALT="next: Applicability"></A> Motivation</H2>
<A NAME="auto1002"></A>
<P>When an abstraction can have one of several possible implementations,
the usual way to accommodate them is to use inheritance. An abstract
class defines the interface to the abstraction, and concrete
subclasses implement it in different ways. But this approach isn't
always flexible enough. Inheritance binds an implementation to the
abstraction permanently, which makes it difficult to modify, extend,
and reuse abstractions and implementations independently.</P>
<A NAME="pmwindow"></A>
<A NAME="present-manage"></A>
<A NAME="xwindowsys"></A>
<A NAME="xiconwindow"></A>
<A NAME="xwindow"></A>
<P>Consider the implementation of a portable Window abstraction in a user
interface toolkit. This abstraction should enable us to write
applications that work on both the X Window System and IBM's
Presentation Manager (PM), for example. Using inheritance, we could
define an abstract class Window and subclasses XWindow and PMWindow
that implement the Window interface for the different platforms. But
this approach has two drawbacks:</P>
<OL>
<A NAME="pmiconwindow"></A>
<LI>It's inconvenient to extend the Window abstraction to cover different
kinds of windows or new platforms. Imagine an IconWindow subclass of
Window that specializes the Window abstraction for icons. To support
IconWindows for both platforms, we have to implement <EM>two</EM> new
classes, XIconWindow and PMIconWindow. Worse, we'll have to define two
classes for <EM>every</EM> kind of window. Supporting a third platform
requires yet another new Window subclass for every kind of window.
<A NAME="pmiconwindow-151c"></A>
<A NAME="pmwindow-151c"></A>
<A NAME="151c"></A>
<P ALIGN=CENTER><IMG SRC="bridg098-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/bridg098.gif">
</LI>
<A NAME="auto1003"></A>
<P></P>
<A NAME="auto1004"></A>
<LI>It makes client code platform-dependent. Whenever a client creates a
window, it instantiates a concrete class that has a specific
implementation. For example, creating an XWindow object binds the
Window abstraction to the X Window implementation, which makes the
client code dependent on the X Window implementation. This, in turn,
makes it harder to port the client code to other platforms.
<A NAME="auto1005"></A>
<P>Clients should be able to create a window without committing to a
concrete implementation. Only the window implementation should depend
on the platform on which the application runs. Therefore client code
should instantiate windows without mentioning specific platforms.
</LI>
</OL>
<A NAME="pmwindowimp"></A>
<A NAME="window"></A>
<A NAME="xwindowimp"></A>
<P>The Bridge pattern addresses these problems by putting the Window
abstraction and its implementation in separate class hierarchies.
There is one class hierarchy for window interfaces (Window,
IconWindow, TransientWindow) and a separate hierarchy for
platform-specific window implementations, with WindowImp as its root.
The XWindowImp subclass, for example, provides an implementation based
on the X Window System.</P>
<A NAME="pmwindowimp-152c"></A>
<A NAME="152c"></A>
<P ALIGN=CENTER><IMG SRC="bridg100-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/bridg100.gif"></P>
<A NAME="windowimp"></A>
<P>All operations on Window subclasses are implemented in terms of abstract
operations from the WindowImp interface. This decouples the window
abstractions from the various platform-specific implementations. We
refer to the relationship between Window and WindowImp as a
<STRONG>bridge</STRONG>, because it bridges the abstraction and its
implementation, letting them vary independently.</P>
<A NAME="applicability"></A>
<H2><A HREF="#structure"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Structure"></A> Applicability</H2>
<A NAME="auto1006"></A>
<P>Use the Bridge pattern when</P>
<UL>
<A NAME="auto1007"></A>
<LI>you want to avoid a permanent binding between an abstraction and its
implementation. This might be the case, for example, when the
implementation must be selected or switched at run-time.</LI>
<A NAME="auto1008"></A>
<P></P>
<A NAME="auto1009"></A>
<LI>both the abstractions and their implementations should be extensible
by subclassing. In this case, the Bridge pattern lets you combine the
different abstractions and implementations and extend them
independently.</LI>
<A NAME="auto1010"></A>
<P></P>
<A NAME="auto1011"></A>
<LI>changes in the implementation of an abstraction should have no impact
on clients; that is, their code should not have to be recompiled.</LI>
<A NAME="auto1012"></A>
<P></P>
<A NAME="auto1013"></A>
<LI>(C++) you want to hide the implementation of an abstraction completely
from clients. In C++ the representation of a class is visible in the
class interface.</LI>
<A NAME="auto1014"></A>
<P></P>
<A NAME="auto1015"></A>
<LI>you have a proliferation of classes as shown earlier in the first Motivation
diagram. Such a class hierarchy indicates the need for splitting an
object into two parts. Rumbaugh uses the term "nested
generalizations" [<A HREF="bibfs-1.htm#rumbaugh_omt" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#rumbaugh_omt" TARGET="_mainDisplayFrame">RBP+91</A>] to refer to such class
hierarchies.</LI>
<A NAME="auto1016"></A>
<P></P>
<A NAME="auto1017"></A>
<LI>you want to share an implementation among multiple objects (perhaps
using reference counting), and this fact should be hidden from the
client. A simple example is Coplien's String
class [<A HREF="bibfs-1.htm#coplien_idioms" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#coplien_idioms" TARGET="_mainDisplayFrame">Cop92</A>], in which multiple objects can share the
same string representation (StringRep).</LI>
</UL>
<A NAME="structure"></A>
<H2><A HREF="#participants"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Participants"></A> Structure</H2>
<P ALIGN=CENTER><IMG SRC="bridge-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/bridge.gif"></P>
<A NAME="participants"></A>
<H2><A HREF="#collaborations"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0
ALT="next: Collaborations"></A> Participants</H2>
<UL>
<A NAME="auto1018"></A>
<LI><B>Abstraction</B> (Window)
<A NAME="auto1019"></A>
<P></P>
<UL>
<A NAME="auto1020"></A>
<LI>defines the abstraction's interface.</LI>
<A NAME="auto1021"></A>
<P><!-- extra space --></P>
<A NAME="auto1022"></A>
<LI>maintains a reference to an object of type Implementor.</LI>
</UL>
<A NAME="auto1023"></A>
<P></P>
<A NAME="refinedabs-part-bridge"></A>
<LI><B>RefinedAbstraction</B> (IconWindow)
<A NAME="auto1024"></A>
<P></P>
<UL>
<A NAME="auto1025"></A>
<LI>Extends the interface defined by Abstraction.</LI>
</UL>
<A NAME="auto1026"></A>
<P></P>
<A NAME="auto1027"></A>
<LI><B>Implementor</B> (WindowImp)
<A NAME="auto1028"></A>
<P></P>
<UL>
<A NAME="auto1029"></A>
<LI>defines the interface for implementation classes. This
interface doesn't have to correspond exactly to Abstraction's
interface; in fact the two interfaces can be quite different.
Typically the Implementor interface provides only primitive
operations, and Abstraction defines higher-level
operations based on these primitives.
</UL>
<A NAME="auto1030"></A>
<P></P>
<A NAME="auto1031"></A>
<LI><B>ConcreteImplementor</B> (XWindowImp, PMWindowImp)
<A NAME="auto1032"></A>
<P></P>
<UL>
<A NAME="auto1033"></A>
<LI>implements the Implementor interface and defines its
concrete implementation.
</UL>
</UL>
<A NAME="collaborations"></A>
<H2><A HREF="#consequences"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0
ALT="next: Consequences"></A> Collaborations</H2>
<UL>
<A NAME="auto1034"></A>
<LI>Abstraction forwards client requests to its Implementor object.</LI>
</UL>
<A NAME="consequences"></A>
<H2><A HREF="#implementation"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0
ALT="next: Implementation"></A> Consequences</H2>
<A NAME="auto1035"></A>
<P>The Bridge pattern has the following consequences:</P>
<OL>
<A NAME="decouple-iandi">
<A NAME="auto1036"></A>
<LI><EM>Decoupling interface and implementation.</EM>
An implementation is not bound permanently to an interface. The
implementation of an abstraction can be configured at run-time.
It's even possible for an object to change its implementation
at run-time.
<A NAME="auto1037"></A>
<P>Decoupling Abstraction and Implementor also eliminates compile-time
dependencies on the implementation. Changing an implementation class
doesn't require recompiling the Abstraction class and its
clients. This property is essential when you must ensure binary
compatibility between different versions of a class library.</P>
<A NAME="auto1038"></A>
<P>Furthermore, this decoupling encourages layering that can lead to a
better-structured system. The high-level part of a system only has to
know about Abstraction and Implementor.</P>
</LI>
<A NAME="auto1039"></A>
<P></P>
<A NAME="auto1040"></A>
<LI><EM>Improved extensibility.</EM>
You can extend the Abstraction and Implementor hierarchies independently.</LI>
<A NAME="auto1041"></A>
<P></P>
<A NAME="auto1042"></A>
<LI><EM>Hiding implementation details from clients.</EM>
You can shield clients from implementation details, like the sharing
of implementor objects and the accompanying reference count mechanism
(if any).</LI>
</OL>
<A NAME="implementation"></A>
<H2><A HREF="#samplecode"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Sample Code"></A> Implementation</H2>
<A NAME="auto1043"></A>
<P>Consider the following implementation issues when applying the Bridge
pattern:</P>
<OL>
<A NAME="auto1044"></A>
<LI><EM>Only one Implementor.</EM>
In situations where there's only one implementation, creating an
abstract Implementor class isn't necessary. This is a degenerate case
of the Bridge pattern; there's a one-to-one relationship between
Abstraction and Implementor. Nevertheless, this separation is still
useful when a change in the implementation of a class must not affect
its existing clients—that is, they shouldn't have to be recompiled,
just relinked.
<A NAME="auto1045"></A>
<P>Carolan [<A HREF="bibfs-1.htm#carolan_bullet-proof" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#carolan_bullet-proof" TARGET="_mainDisplayFrame">Car89</A>] uses the term "Cheshire Cat" to
describe this separation. In C++, the class interface of the
Implementor class can be defined in a private header file that isn't
provided to clients. This lets you hide an implementation of a class
completely from its clients.</P>
</LI>
<A NAME="auto1046"></A>
<P></P>
<A NAME="auto1047"></A>
<LI><EM>Creating the right Implementor object.</EM>
How, when, and where do you decide which Implementor class to
instantiate when there's more than one?
<A NAME="auto1048"></A>
<P>If Abstraction knows about all ConcreteImplementor classes, then it
can instantiate one of them in its constructor; it can decide between
them based on parameters passed to its constructor. If, for example,
a collection class supports multiple implementations, the decision can
be based on the size of the collection. A linked list implementation
can be used for small collections and a hash table for larger ones.</P>
<A NAME="auto1049"></A>
<P>Another approach is to choose a default implementation initially and
change it later according to usage. For example, if the collection
grows bigger than a certain threshold, then it switches its
implementation to one that's more appropriate for a large number of
items.</P>
<A NAME="auto1050"></A>
<P>It's also possible to delegate the decision to another object
altogether. In the Window/WindowImp example, we can introduce a
factory object (see <A HREF="pat3afs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A>)
whose sole duty is to encapsulate platform-specifics. The factory
knows what kind of WindowImp object to create for the platform in use;
a Window simply asks it for a WindowImp, and it returns the right
kind. A benefit of this approach is that Abstraction is not coupled
directly to any of the Implementor classes.</P>
</LI>
<A NAME="auto1051"></A>
<P></P>
<A NAME="auto1052"></A>
<LI><EM>Sharing implementors.</EM>
Coplien illustrates how the Handle/Body idiom in C++ can be used to
share implementations among several objects [<A HREF="bibfs-1.htm#coplien_idioms" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#coplien_idioms" TARGET="_mainDisplayFrame">Cop92</A>]. The
Body stores a reference count that the Handle class increments and
decrements. The code for assigning handles with shared bodies has the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -