📄 tij0083.html
字号:
<html><body>
<table width="100%"><tr>
<td>
<a href="http://www.bruceeckel.com/javabook.html">Bruce Eckel's Thinking in Java</a>
</td>
<td align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0082.html">Prev</a> | <a href="tij0084.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
Designing
with inheritance
<P><A NAME="Index697"></A><A NAME="Index698"></A></H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Once
you learn about polymorphism, it can seem that everything ought to be inherited
because polymorphism is such a clever tool. This can burden your designs; in
fact if you choose inheritance first when you’re using an existing class
to make a new class things can become needlessly complicated.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">A
better approach is to choose <A NAME="Index699"></A><A NAME="Index700"></A>composition
first, when it’s not obvious which one you should use. Composition does
not force a design into an inheritance hierarchy. But composition is also more
flexible since it’s possible to dynamically choose a type (and thus
behavior) when using composition, whereas inheritance requires an exact type to
be known at compile time. The following example illustrates this:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Transmogrify.java</font>
<font color="#009900">// Dynamically changing the behavior of</font>
<font color="#009900">// an object via composition.</font>
<font color="#0000ff">interface</font> Actor {
<font color="#0000ff">void</font> act();
}
<font color="#0000ff">class</font> HappyActor <font color="#0000ff">implements</font> Actor {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> act() {
System.out.println("HappyActor");
}
}
<font color="#0000ff">class</font> SadActor <font color="#0000ff">implements</font> Actor {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> act() {
System.out.println("SadActor");
}
}
<font color="#0000ff">class</font> Stage {
Actor a = <font color="#0000ff">new</font> HappyActor();
<font color="#0000ff">void</font> change() { a = <font color="#0000ff">new</font> SadActor(); }
<font color="#0000ff">void</font> go() { a.act(); }
}
<font color="#0000ff">public</font> <font color="#0000ff">class</font> Transmogrify {
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
Stage s = <font color="#0000ff">new</font> Stage();
s.go(); <font color="#009900">// Prints "HappyActor"</font>
s.change();
s.go(); <font color="#009900">// Prints "SadActor"</font>
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">A
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Stage</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object contains a handle to an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Actor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which is initialized to a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>HappyActor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object. This means
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>go( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
produces a particular behavior. But since a handle can be re-bound to a
different object at run time, a handle for a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>SadActor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object can be substituted in
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>a</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and then the behavior produced by
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>go( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
changes. Thus you gain dynamic flexibility at run time. In contrast, you
can’t decide to inherit differently at run time; that must be completely
determined at compile time. <A NAME="Index701"></A><A NAME="Index702"></A></FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">A
general guideline is “Use inheritance to express differences in behavior,
and member variables to express variations in state.” In the above
example, both are used: two different classes are inherited to express the
difference in the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>act( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method, and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Stage</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
uses composition to allow its state to be changed. In this case, that change in
state happens to produce a change in behavior.
</FONT><a name="_Toc375545342"></a><a name="_Toc408018558"></a><P></DIV>
<A NAME="Heading236"></A><H3 ALIGN=LEFT>
Pure
inheritance vs. extension
<P><A NAME="Index703"></A><A NAME="Index704"></A><A NAME="Index705"></A></H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
studying inheritance, it would seem that the cleanest way to create an
inheritance hierarchy is to take the “pure” approach. That is, only
methods that have been established in the base class or
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>interface</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
are to be overridden in the derived class, as seen in this diagram:
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
can be termed a pure “<A NAME="Index706"></A>is-a”
relationship because the interface of a class establishes what it is.
Inheritance guarantees that any derived class will have the interface of the
base class and nothing less. If you follow the above diagram, derived classes
will also have
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>no
more
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
than the base class interface.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
can be thought of as <A NAME="Index707"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>pure
substitution
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
because derived class objects can be perfectly substituted for the base class,
and you never need to know any extra information about the subclasses when
you’re using them:
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">That
is, the base class can receive any message you can send to the derived class
because the two have exactly the same interface. All you need to do is upcast
from the derived class and never look back to see what exact type of object
you’re dealing with. Everything is handled through polymorphism.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
you see it this way, it seems like a pure “is-a” relationship is
the only sensible way to do things, and any other <A NAME="Index708"></A>design
indicates muddled thinking and is by definition broken. This too is a trap. As
soon as you start thinking this way, you’ll turn around and discover that
extending the interface (which, unfortunately, the keyword <A NAME="Index709"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>extends</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -