⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap1.htm

📁 设计模式英文版 作者:Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides 四人帮的书。 学设计模式的必读的书籍!经典中的经典
💻 HTM
📖 第 1 页 / 共 5 页
字号:

<A NAME="auto1091"></A>
<P>Although most programming languages don't support the distinction
between interface and implementation inheritance, people make the
distinction in practice.  Smalltalk programmers usually act as if
subclasses were subtypes (though there are some well-known
exceptions&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=cook92" TARGET="_mainDisplayFrame">Coo92</A>]); C++ programmers manipulate objects through
types defined by abstract classes.</P>

<A NAME="auto1092"></A>
<P>Many of the design patterns depend on this distinction.  For example,
objects in a <A HREF="pat5afs.htm" TARGET="_mainDisplayFrame">Chain of Responsibility&nbsp;(223)</A> must have a
common type, but usually they don't share a common implementation.  In
the <A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite&nbsp;(163)</A> pattern, Component defines a
common interface, but Composite often defines a common implementation.
<A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command&nbsp;(233)</A>, <A HREF="pat5gfs.htm" TARGET="_mainDisplayFrame">Observer&nbsp;(293)</A>,
<A HREF="pat5hfs.htm" TARGET="_mainDisplayFrame">State&nbsp;(305)</A>, and <A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy&nbsp;(315)</A> are often
implemented with abstract classes that are pure interfaces.</P>

<H4>Programming to an Interface, not an Implementation</H4>

<A NAME="auto1093"></A>
<P>Class inheritance is basically just a mechanism for extending an
application's functionality by reusing functionality in parent
classes. It lets you define a new kind of object rapidly in terms of
an old one.  It lets you get new implementations almost for free,
inheriting most of what you need from existing classes.</P>

<A NAME="poly-w-inherit"></A>
<A NAME="implement-reuse"></A>
<P>However, implementation reuse is only half the story. Inheritance's
ability to define families of objects with <EM>identical</EM> interfaces
(usually by inheriting from an abstract class) is also important. Why?
Because polymorphism depends on it.</P>

<A NAME="auto1094"></A>
<P>When inheritance is used carefully (some will say <EM>properly</EM>), all
classes derived from an abstract class will share its interface. This
implies that a subclass merely adds or overrides operations and
does not hide operations of the parent class.  <EM>All</EM> subclasses
can then respond to the requests in the interface of this abstract
class, making them all subtypes of the abstract class.</P>  

<A NAME="auto1095"></A>
<P>There are two benefits to manipulating objects solely in terms of the
interface defined by abstract classes:</P>

<OL>

<A NAME="auto1096"></A>
<LI>Clients remain unaware of the specific types of objects they use,
as long as the objects adhere to the interface that clients expect.</LI>

<A NAME="auto1097"></A>
<P></P>

<A NAME="auto1098"></A>
<LI>Clients remain unaware of the classes that implement these objects.
Clients only know about the abstract class(es) defining the interface.</LI>

</OL>

<A NAME="auto1099"></A>
<P>This so greatly reduces implementation dependencies between subsystems
that it leads to the following principle of reusable object-oriented
design:</P>

<BLOCKQUOTE>
<I>Program to an interface, not an implementation.</I>
</BLOCKQUOTE>

<A NAME="auto1100"></A>
<P>Don't declare variables to be instances of particular concrete
classes.  Instead, commit only to an interface defined by an abstract
class. You will find this to be a common theme of the design patterns in
this book.</P>

<A NAME="auto1101"></A>
<P>You have to instantiate concrete classes (that is, specify a
particular implementation) somewhere in your system, of course, and
the creational patterns (<A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory&nbsp;(87)</A>,
<A HREF="pat3bfs.htm" TARGET="_mainDisplayFrame">Builder&nbsp;(97)</A>, <A HREF="pat3cfs.htm" TARGET="_mainDisplayFrame">Factory Method&nbsp;(107)</A>,
<A HREF="pat3dfs.htm" TARGET="_mainDisplayFrame">Prototype&nbsp;(117)</A>, and <A HREF="pat3efs.htm" TARGET="_mainDisplayFrame">Singleton&nbsp;(127)</A>
let you do just that.  By abstracting the process of object creation,
these patterns give you different ways to associate an interface with
its implementation transparently at instantiation.  Creational
patterns ensure that your system is written in terms of interfaces,
not implementations.</P>

<H3>Putting Reuse Mechanisms to Work</H3>

<A NAME="auto1102"></A>
<P>Most people can understand concepts like objects, interfaces, classes,
and inheritance.  The challenge lies in applying them to build
flexible, reusable software, and design patterns can show you how.</P>

<A NAME="intro_inheritance_versus_composition"></A>
<A NAME="reuse-via-subclass"></A>
<A NAME="whitebox-reuse"></A>
<H4>Inheritance versus Composition</H4>

<A NAME="auto1103"></A>
<P>The two most common techniques for reusing functionality in
object-oriented systems are class inheritance and
<STRONG>object composition</STRONG>.  As we've explained, class inheritance
lets you define the implementation of one class in terms of another's.
Reuse by subclassing is often referred to as <STRONG>white-box
reuse</STRONG>. The term "white-box" refers to visibility: With inheritance,
the internals of parent classes are often visible to subclasses.</P>

<A NAME="blackbox"></A>
<A NAME="composition"></A>
<P>Object composition is an alternative to class inheritance.  Here, new
functionality is obtained by assembling or <EM>composing</EM> objects to
get more complex functionality.  Object composition requires that the
objects being composed have well-defined interfaces.  This style of
reuse is called <STRONG>black-box reuse</STRONG>, because no internal
details of objects are visible. Objects appear only as "black
boxes."</P>

<A NAME="auto1104"></A>
<P>Inheritance and composition each have their advantages and disadvantages.
Class inheritance is defined statically at compile-time and is
straightforward to use, since it's supported directly by the
programming language.  Class inheritance also makes it easier to
modify the implementation being reused.  When a subclass overrides
some but not all operations, it can affect the operations it inherits
as well, assuming they call the overridden operations.</P>  

<A NAME="inherit-bk-encap"></A>
<P>But class inheritance has some disadvantages, too.  First, you can't
change the implementations inherited from parent classes at run-time,
because inheritance is defined at compile-time.  Second, and generally
worse, parent classes often define at least part of their subclasses'
physical representation. Because inheritance exposes a subclass to
details of its parent's implementation, it's often said that
"inheritance breaks encapsulation"&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=snyder86" TARGET="_mainDisplayFrame">Sny86</A>]. The
implementation of a subclass becomes so bound up with the
implementation of its parent class that any change in the parent's
implementation will force the subclass to change.</P>  

<A NAME="auto1105"></A>
<P>Implementation dependencies can cause problems when you're trying to
reuse a subclass. Should any aspect of the inherited implementation
not be appropriate for new problem domains, the parent class must be
rewritten or replaced by something more appropriate.  This dependency
limits flexibility and ultimately reusability. One cure for this is to
inherit only from abstract classes, since they usually provide little
or no implementation.</P>

<A NAME="auto1106"></A>
<P>Object composition is defined dynamically at run-time through objects
acquiring references to other objects.  Composition requires objects
to respect each others' interfaces, which in turn requires carefully
designed interfaces that don't stop you from using one object with
many others. But there is a payoff.  Because objects are accessed
solely through their interfaces, we don't break encapsulation. Any
object can be replaced at run-time by another as long as it has the
same type.  Moreover, because an object's implementation will be
written in terms of object interfaces, there are substantially fewer
implementation dependencies.</P>

<A NAME="auto1107"></A>
<P>Object composition has another effect on system design.  Favoring
object composition over class inheritance helps you keep each class
encapsulated and focused on one task.  Your classes and class
hierarchies will remain small and will be less likely to grow into
unmanageable monsters.  On the other
hand, a design based on object composition will have more objects (if
fewer classes), and the system's behavior will depend on their
interrelationships instead of being defined in one class.</P>

<A NAME="auto1108"></A>
<P>That leads us to our second principle of object-oriented design:</P>

<BLOCKQUOTE>
<I>Favor object composition over class inheritance.</I>
</BLOCKQUOTE>

<A NAME="auto1109"></A>
<P>Ideally, you shouldn't have to create new components to achieve reuse.
You should be able to get all the functionality you need just by
assembling existing components through object composition. But this is
rarely the case, because the set of available components is never
quite rich enough in practice.  Reuse by inheritance makes it easier
to make new components that can be composed with old ones.
Inheritance and object composition thus work together.</P>

<A NAME="auto1110"></A>
<P>Nevertheless, our experience is that designers overuse inheritance as
a reuse technique, and designs are often made more reusable (and
simpler) by depending more on object composition.  You'll see object
composition applied again and again in the design patterns.</P>

<A NAME="delegation"></A>
<H4>Delegation</H4>

<A NAME="auto1111"></A>
<P><STRONG>Delegation</STRONG> is a way of making composition as
powerful for reuse as inheritance&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=Lieb86a" TARGET="_mainDisplayFrame">Lie86</A>,
<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=johnson-zweig_delegation" TARGET="_mainDisplayFrame">JZ91</A>].  In delegation,
<EM>two</EM> objects are involved in handling a request: a receiving
object delegates operations to its <STRONG>delegate</STRONG>.  This
is analogous to subclasses deferring requests to parent classes.
But with inheritance, an inherited operation can always refer to
the receiving object through the <CODE>this</CODE> member variable in
C++ and <CODE>self</CODE> in Smalltalk.  To achieve the same effect
with delegation, the receiver passes itself to the delegate to let
the delegated operation refer to the receiver.</P>

<A NAME="rectangle"></A>
<P>For example, instead of making class Window a subclass of Rectangle
(because windows happen to be rectangular), the Window class might
reuse the behavior of Rectangle by keeping a Rectangle instance
variable and <EM>delegating</EM> Rectangle-specific behavior to it.  In
other words, instead of a Window <EM>being</EM> a Rectangle, it would
<EM>have</EM> a Rectangle.  Window must now forward requests to its
Rectangle instance explicitly, whereas before it would have inherited
those operations.</P>

<A NAME="auto1112"></A>
<P>The following diagram depicts the Window class delegating its Area
operation to a Rectangle instance.</P>  

<A NAME="rectangle-20c"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/deleg063.gif"></P>

<A NAME="auto1113"></A>
<P>A plain arrowhead line indicates that a class keeps a reference to
an instance of another class.  The reference has an optional name,
"rectangle" in this case.</P>

<A NAME="auto1114"></A>
<P>The main advantage of delegation is that it makes it easy to compose
behaviors at run-time and to change the way they're composed.  Our
window can become circular at run-time simply by replacing its
Rectangle instance with a Circle instance, assuming Rectangle and
Circle have the same type.</P>

<A NAME="auto1115"></A>
<P>Delegation has a disadvantage it shares with other techniques that
make software more flexible through object composition: Dynamic,
highly parameterized software is harder to understand than more static
software.  There are also run-time inefficiencies, but the human
inefficiencies are more important in the long run.  Delegation is a
good design choice only when it simplifies more than it complicates.
It isn't easy to give rules that tell you exactly when to use
delegation, because how effective it will be depends on the context
and on how much experience you have with it.  Delegation works best
when it's used in highly stylized ways&#151;that is, in standard
patterns.</P>

<A NAME="deleg-patrely">
<A NAME="deleg-state"></A>
<A NAME="deleg-strat"></A>
<A NAME="deleg-visit"></A>
<P>Several design patterns use delegation.  The
<A HREF="pat5hfs.htm" TARGET="_mainDisplayFrame">State&nbsp;(305)</A>, <A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy&nbsp;(315)</A>, and
<A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor&nbsp;(331)</A> patterns depend on it.  In the State
pattern, an object delegates requests to a State object that
represents its current state.  In the Strategy pattern, an object
delegates a specific request to an object that represents a strategy
for carrying out the request.  An object will only have one state, but
it can have many strategies for different requests.  The purpose of
both patterns is to change the behavior of an object by changing the
objects to which it delegates requests.  In Visitor, the operation
that gets performed on each element of an object structure is always
delegated to the Visitor object.</P>

<A NAME="media-use-deleg"></A>
<P>Other patterns use delegation less heavily.
<A HREF="pat5efs.htm" TARGET="_mainDisplayFrame">Mediator&nbsp;(273)</A> introduces an object to mediate
communication between other objects.  Sometimes the Mediator object
implements operations simply by forwarding them to the other objects;
other times it passes along a reference to itself and thus uses true
delegation.  <A HREF="pa

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -