📄 chap01.htm
字号:
of the system in code. Indeed, one of the difficulties people have with
object-oriented design is that it’s too simple to get from the beginning
to the end. A mind trained to look for complex solutions is often stumped by
this simplicity at first.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I41'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I42>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you inherit from an existing type,
you create a new type. This new type contains not only all the members of the
existing type (although the <B>private</B> ones are hidden away and
inaccessible), but more important, it duplicates the interface of the base
class. That is, all the messages you can send to objects of the base class you
can also send to objects of the derived class. Since we know the type of a class
by the messages we can send to it, this means that the derived class <I>is the
same type as the base class</I>. In the previous example, “a circle is a
shape.” This type equivalence via inheritance is one of the fundamental
gateways in understanding the meaning of object-oriented
programming.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I42'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I43>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since both the base class and derived
class have the same interface, there must be some implementation to go along
with that interface. That is, there must be some code to execute when an object
receives a particular message. If you simply inherit a class and don’t do
anything else, the methods from the base-class interface come right along into
the derived class. That means objects of the derived class have not only the
same type, they also have the same behavior, which isn’t particularly
interesting.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I43'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I44>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You have two ways to differentiate your
new derived class from the original base class. The first is quite
straightforward: You simply add brand new functions to the derived class. These
new functions are not part of the base class interface. This means that the base
class simply didn’t do as much as you wanted it to, so you added more
functions. This simple and primitive use for
<A NAME="Index85"></A><A NAME="Index86"></A>inheritance is, at times, the
perfect solution to your problem. However, you should look closely for the
possibility that your base class might also need these additional functions.
This process of discovery and iteration of your design happens regularly in
object-oriented
programming.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I44'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I45>
</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIJ207.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although inheritance may sometimes imply
(especially in Java, where the keyword that indicates inheritance is
<B>extends</B>)<B> </B>that you are going to add new functions to the interface,
that’s not necessarily true. The second and more important way to
differentiate your new class is to <I>change</I> the behavior of an existing
base-class function. This is referred to as
<A NAME="Index87"></A><A NAME="Index88"></A><I>overriding</I> that
function.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I45'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I46>
</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIJ208.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To override a function, you simply create
a new definition for the function in the derived class. You’re saying,
“I’m using the same interface function here, but I want it to do
something different for my new
type.”
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I46'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I47>
</FONT><A NAME="_Toc375545194"></A><A NAME="_Toc408018391"></A><A NAME="_Toc472654687"></A><A NAME="_Toc481064472"></A><BR></P></DIV>
<A NAME="Heading27"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Is-a vs. is-like-a relationships<BR><A NAME="Index89"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s a certain debate that can
occur about inheritance: Should inheritance override <I>only</I> base-class
functions (and not add new member functions that aren’t in the base
class)? This would mean that the derived type is <I>exactly</I> the same type as
the base class since it has exactly the same interface. As a result, you can
exactly substitute an object of the derived class for an object of the base
class. This can be thought of as <A NAME="Index90"></A><I>pure substitution</I>,
and it’s often referred to as the <A NAME="Index91"></A><I>substitution
principle</I>. In a sense, this is the ideal way to treat inheritance. We often
refer to the relationship between the base class and derived classes in this
case as an <I>is-a</I> relationship, because you can say “a circle <I>is
a</I> shape.” A test for inheritance is to determine whether you can state
the is-a relationship about the classes and have it make
sense.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I47'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I48>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are times when you must add new
interface elements to a derived type, thus extending the interface and creating
a new type. The new type can still be substituted for the base type, but the
substitution isn’t perfect because your new functions are not accessible
from the base type. This can be described as an
<I>is-like-a</I></FONT><A NAME="fnB6" HREF="#fn6">[6]</A><FONT FACE="Georgia">
relationship; the new type has the interface of the old type but it also
contains other functions, so you can’t really say it’s exactly the
same. For example, consider an air conditioner. Suppose your house is wired with
all the controls for cooling; that is, it has an interface that allows you to
control cooling. Imagine that the air conditioner breaks down and you replace it
with a heat pump, which can both heat and cool. The heat pump <I>is-like-an</I>
air conditioner, but it can do more. Because the control system of your house is
designed only to control cooling, it is restricted to communication with the
cooling part of the new object. The interface of the new object has been
extended, and the existing system doesn’t know about anything except the
original interface.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I48'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I49>
</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIJ209.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, once you see this design it
becomes clear that the base class “cooling system” is not general
enough, and should be renamed to “temperature control system” so
that it can also include heating—at which point the substitution principle
will work. However, the diagram above is an example of what can happen in design
and in the real world.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I49'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I50>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you see the substitution principle
it’s easy to feel like this approach (pure substitution) is the only way
to do things, and in fact it <I>is</I> nice if your design works out that way.
But you’ll find that there are times when it’s equally clear that
you must add new functions to the interface of a derived class. With inspection
both cases should be reasonably
obvious.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I50'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I51>
</FONT><A NAME="_Toc375545195"></A><A NAME="_Toc408018392"></A><A NAME="_Toc472654688"></A><A NAME="_Toc481064473"></A><BR></P></DIV>
<A NAME="Heading28"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Interchangeable objects <BR>with polymorphism<BR><A NAME="Index92"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When dealing with type hierarchies, you
often want to treat an object not as the specific type that it is, but instead
as its base type. This allows you to write code that doesn’t depend on
specific types. In the shape example, functions manipulate generic shapes
without respect to whether they’re circles, squares, triangles, or some
shape that hasn’t even been defined yet. All shapes can be drawn, erased,
and moved, so these functions simply send a message to a shape object; they
don’t worry about how the object copes with the
message.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I51'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I52>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Such code is unaffected by the addition
of new types, and adding new types is the most common way to extend an
object-oriented program to handle new situations. For example, you can derive a
new subtype of shape called pentagon<I> </I>without modifying the functions that
deal only with generic shapes. This ability to extend a program easily by
deriving new subtypes is important because it greatly improves designs while
reducing the cost of software
maintenance.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I52'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I53>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s a problem, however, with
attempting to treat derived-type objects as their generic base types (circles as
shapes, bicycles as vehicles, cormorants as birds, etc.). If a function is going
to tell a generic shape to draw itself, or a generic vehicle to steer, or a
generic bird to move, the compiler cannot know at compile-time precisely what
piece of code will be executed. That’s the whole point—when the
message is sent, the programmer doesn’t <I>want</I> to know what piece of
code will be executed; the draw function can be applied equally to a circle, a
square, or a triangle, and the object will execute the proper code depending on
its specific type. If you don’t have to know what piece of code will be
executed, then when you add a new subtype, the code it executes can be different
without requiring changes to the function call. Therefore, the compiler cannot
know precisely what piece of code is executed, so what does it do? For example,
in the following diagram the <B>BirdController</B> object just works with
generic <B>Bird</B> objects, and does not know what exact type they are. This is
convenient from <B>BirdController</B>’s perspective because it
doesn’t have to write special code to determine the exact type of
<B>Bird</B> it’s working with, or that <B>Bird</B>’s behavior. So
how does it happen that, when <B>move( )</B> is called while ignoring the
specific type of <B>Bird</B>, the right behavior will occur (a <B>Goose
</B>runs, flies, or swims, and a <B>Penguin</B> runs or
swims)?
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I53'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I54>
</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIJ210.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The answer is the primary twist in
object-oriented programming: the compiler cannot make a function call in the
traditional sense. The function call generated by a non-OOP compiler causes what
is called <A NAME="Index93"></A><A NAME="Index94"></A><I>early binding</I>, a
term you may not have heard before because you’ve never thought about it
any other way. It means the compiler generates a call to a specific function
name, and the linker resolves this call to the absolute address of the code to
be executed. In OOP, the program cannot determine the address of the code until
run-time, so some other scheme is necessary when a message is sent to a generic
object.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER1_I54'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER1_I55>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To solve the problem, object-oriented
languages use the concept of <A NAME="Index95"></A><A NAME="Index96"></A><I>late
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -