📄 chap1.htm
字号:
object.</P>
<A NAME="auto1069"></A>
<P>The hard part about object-oriented design is decomposing a system
into objects. The task is difficult because many factors come into
play: encapsulation, granularity, dependency, flexibility,
performance, evolution, reusability, and on and on. They all
influence the decomposition, often in conflicting ways.</P>
<A NAME="auto1070"></A>
<P>Object-oriented design methodologies favor many different approaches.
You can write a problem statement, single out the nouns and verbs, and
create corresponding classes and operations. Or you can focus on the
collaborations and responsibilities in your system. Or you can model
the real world and translate the objects found during analysis into
design. There will always be disagreement on which approach is best.</P>
<A NAME="auto1071"></A>
<P>Many objects in a design come from the analysis model. But
object-oriented designs often end up with classes that have no
counterparts in the real world. Some of these are low-level
classes like arrays. Others are much higher-level. For example, the
<A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A> pattern introduces an abstraction for
treating objects uniformly that doesn't have a physical counterpart.
Strict modeling of the real world leads to a system that reflects
today's realities but not necessarily tomorrow's. The abstractions
that emerge during design are key to making a design flexible.</P>
<A NAME="auto1072"></A>
<P>Design patterns help you identify less-obvious abstractions and the
objects that can capture them. For example, objects that represent a
process or algorithm don't occur in nature, yet they are a crucial
part of flexible designs. The <A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy (315)</A> pattern
describes how to implement interchangeable families of algorithms.
The <A HREF="pat5hfs.htm" TARGET="_mainDisplayFrame">State (305)</A> pattern represents each state of an entity
as an object. These objects are seldom found during analysis or even
the early stages of design; they're discovered later in the course of
making a design more flexible and reusable.</P>
<H3>Determining Object Granularity</H3>
<A NAME="auto1073"></A>
<P>Objects can vary tremendously in size and number. They can represent
everything down to the hardware or all the way up to entire
applications. How do we decide what should be an object?</P>
<A NAME="auto1074"></A>
<P>Design patterns address this issue as well. The
<A HREF="pat4efs.htm" TARGET="_mainDisplayFrame">Facade (<As+HREF="#Facade">185</A>) pattern describes how to represent complete
subsystems as objects, and the <A HREF="pat4ffs.htm" TARGET="_mainDisplayFrame">Flyweight (<As+HREF="#Flyweight">195</A>) pattern
describes how to support huge numbers of objects at the finest
granularities. Other design patterns describe specific ways of
decomposing an object into smaller objects. <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> and <A HREF="pat3bfs.htm" TARGET="_mainDisplayFrame">Builder (97)</A>
yield objects whose only responsibilities are creating other objects.
<A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor (331)</A> and <A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command (233)</A> yield
objects whose only responsibilities are to implement a request on
another object or group of objects.</P>
<A NAME="def-signature"></A>
<H3>Specifying Object Interfaces</H3>
<A NAME="auto1075"></A>
<P>Every operation declared by an object specifies the operation's name,
the objects it takes as parameters, and the operation's return value.
This is known as the operation's <STRONG>signature</STRONG>. The set of
all signatures defined by an object's operations is called the
<STRONG>interface</STRONG> to the object. An object's interface
characterizes the complete set of requests that can be sent to the
object. Any request that matches a signature in the object's interface
may be sent to the object.</P>
<A NAME="def-type"></A>
<A NAME="def-subtype"></A>
<A NAME="def-supertype"></A>
<P>A <STRONG>type</STRONG> is a name used to denote a particular interface. We
speak of an object as having the type "Window" if it accepts all
requests for the operations defined in the interface named "Window."
An object may have many types, and widely different objects can share
a type. Part of an object's interface may be characterized by one
type, and other parts by other types. Two objects of the same type
need only share parts of their interfaces. Interfaces can contain
other interfaces as subsets. We say that a type is a <B>subtype</B> of
another if its interface contains the interface of its
<B>supertype</B>. Often we speak of a subtype <EM>inheriting</EM> the
interface of its supertype.</P>
<A NAME="auto1076"></A>
<P>Interfaces are fundamental in object-oriented systems. Objects are
known only through their interfaces. There is no way to know anything
about an object or to ask it to do anything without going through its
interface. An object's interface says nothing about its
implementation—different objects are free to implement requests
differently. That means two objects having completely different
implementations can have identical interfaces.</P>
<A NAME="dynamicbinding"></A>
<P>When a request is sent to an object, the particular operation that's
performed depends on <EM>both</EM> the request <EM>and</EM> the receiving
object. Different objects that support identical requests may have
different implementations of the operations that fulfill these
requests. The run-time association of a request to an object and one
of its operations is known as <STRONG>dynamic binding</STRONG>.</P>
<A NAME="def-polymorphism"></A>
<P>Dynamic binding means that issuing a request doesn't commit you to a
particular implementation until run-time. Consequently, you can write
programs that expect an object with a particular interface, knowing
that any object that has the correct interface will accept the
request. Moreover, dynamic binding lets you substitute objects that
have identical interfaces for each other at run-time. This
substitutability is known as <STRONG>polymorphism</STRONG>, and it's a key
concept in object-oriented systems. It lets a client object make few
assumptions about other objects beyond supporting a particular
interface. Polymorphism simplifies the definitions of clients,
decouples objects from each other, and lets them vary their
relationships to each other at run-time.</P>
<A NAME="auto1077"></A>
<P>Design patterns help you define interfaces by identifying their key
elements and the kinds of data that get sent across an interface. A
design pattern might also tell you what <EM>not</EM> to put in the
interface. The <A HREF="pat5efs.htm" TARGET="_mainDisplayFrame">Memento (283)</A> pattern is a good example.
It describes how to encapsulate and save the internal state of an
object so that the object can be restored to that state later. The
pattern stipulates that Memento objects must define two interfaces: a
restricted one that lets clients hold and copy mementos, and a
privileged one that only the original object can use to store and
retrieve state in the memento.</P>
<A NAME="auto1078"></A>
<P>Design patterns also specify relationships between interfaces. In
particular, they often require some classes to have similar
interfaces, or they place constraints on the interfaces of some
classes. For example, both <A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator (175)</A> and
<A HREF="pat4gfs.htm" TARGET="_mainDisplayFrame">Proxy (<As+HREF="#Proxy">207</A>) require the interfaces of Decorator and Proxy
objects to be identical to the decorated and proxied objects. In
<A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor (331)</A>, the Visitor interface must reflect all
classes of objects that visitors can visit.</P>
<H3>Specifying Object Implementations</H3>
<A NAME="auto1079"></A>
<P>So far we've said little about how we actually define an object. An
object's implementation is defined by its <STRONG>class</STRONG>. The class
specifies the object's internal data and representation and defines
the operations the object can perform.</P>
<A NAME="auto1080"></A>
<P>Our OMT-based notation (summarized in
<A HREF="chapBfs.htm" TARGET="_mainDisplayFrame">Appendix B</A>)
depicts a class as a rectangle with the class name in bold.
Operations appear in normal type below the class name. Any data that
the class defines comes after the operations. Lines separate the
class name from the operations and the operations from the data:</P>
<P ALIGN=CENTER><IMG SRC="Pictures/class.gif"></P>
<A NAME="auto1081"></A>
<P>Return types and instance variable types are optional, since we don't
assume a statically typed implementation language.</P>
<A NAME="auto1082"></A>
<P>Objects are created by <STRONG>instantiating</STRONG> a class. The object
is said to be an <STRONG>instance</STRONG> of the class. The process of
instantiating a class allocates storage for the object's internal data
(made up of <STRONG>instance variables</STRONG>) and associates the
operations with these data. Many similar instances of an object can be
created by instantiating a class.</P>
<A NAME="auto1083"></A>
<P>A dashed arrowhead line indicates a class that instantiates objects
of another class. The arrow points to the class of the instantiated
objects.</P>
<P ALIGN=CENTER><IMG SRC="Pictures/insta045.gif"></P>
<A NAME="auto1084"></A>
<P>New classes can be defined in terms of existing classes using
<STRONG>class inheritance</STRONG>. When a <STRONG>subclass</STRONG> inherits
from a <STRONG>parent class</STRONG>, it includes the definitions of all
the data and operations that the parent class defines. Objects that
are instances of the subclass will contain all data defined by the
subclass and its parent classes, and they'll be able to perform all
operations defined by this subclass and its parents. We indicate the
subclass relationship with a vertical line and a triangle:</P>
<P ALIGN=CENTER><IMG SRC="Pictures/subcl009.gif"></P>
<A NAME="abstractclass"></A>
<A NAME="abstractoper"></A>
<P>An <STRONG>abstract class</STRONG> is one whose main purpose is to define a
common interface for its subclasses. An abstract class will defer some
or all of its implementation to operations defined in subclasses; hence
an abstract class cannot be instantiated. The operations that an
abstract class declares but doesn't implement are called
<STRONG>abstract operations</STRONG>. Classes that aren't abstract are
called <STRONG>concrete classes</STRONG>.</P>
<A NAME="oper-override"></A>
<P>Subclasses can refine and redefine behaviors of their parent classes.
More specifically, a class may <STRONG>override</STRONG> an operation
defined by its parent class. Overriding gives subclasses a chance to
handle requests instead of their parent classes. Class inheritance
lets you define classes simply by extending other classes, making it
easy to define families of objects having related functionality.</P>
<A NAME="pseudocode"></A>
<P>The names of abstract classes appear in slanted type to distinguish
them from concrete classes. Slanted type is also used to denote
abstract operations. A diagram may include pseudocode for an
operation's implementation; if so, the code will appear in a dog-eared
box connected by a dashed line to the operation it implements.</P>
<A NAME="absclass"></A>
<A NAME="pseudocode-16c"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/absclass.gif"></P>
<A NAME="auto1085"></A>
<P>A <STRONG>mixin class</STRONG> is a class that's intended to provide an
optional interface or functionality to other classes. It's similar to
an abstract class in that it's not intended to be instantiated. Mixin
classes require multiple inheritance:</P>
<P ALIGN=CENTER><IMG SRC="Pictures/mixin.gif"></P>
<A NAME="type-vs-class"></A>
<H4>Class versus Interface Inheritance</H4>
<A NAME="auto1086"></A>
<P>It's important to understand the difference between an object's <EM>class</EM> and its <EM>type</EM>.</P>
<A NAME="auto1087"></A>
<P>An object's class defines how the object is implemented. The class
defines the object's internal state and the implementation of its
operations. In contrast, an object's type only refers to its
interface—the set of requests to which it can respond. An object can have
many types, and objects of different classes can have the same type.</P>
<A NAME="auto1088"></A>
<P>Of course, there's a close relationship between class and type.
Because a class defines the operations an object can perform, it also
defines the object's type. When we say that an object is an instance
of a class, we imply that the object supports the interface defined by the
class.</P>
<A NAME="eiffel"></A>
<A NAME="type-def-in-cpp"></A>
<A NAME="type-def-in-eiffel"></A>
<A NAME="type-def-in-smalltk"></A>
<P>Languages like C++ and Eiffel use classes to specify both an object's
type and its implementation. Smalltalk programs do not declare the
types of variables; consequently, the compiler does not check that the
types of objects assigned to a variable are subtypes of the variable's
type. Sending a message requires checking that the class of the
receiver implements the message, but it doesn't require checking that
the receiver is an instance of a particular class.</P>
<A NAME="auto1089"></A>
<P>It's also important to understand the difference between class
inheritance and interface inheritance (or subtyping). Class
inheritance defines an object's implementation in terms of another
object's implementation. In short, it's a mechanism for code and
representation sharing. In contrast, interface inheritance (or
subtyping) describes when an object can be used in place of another.</P>
<A NAME="auto1090"></A>
<P>It's easy to confuse these two concepts, because many languages don't
make the distinction explicit. In languages like C++ and Eiffel,
inheritance means both interface and implementation inheritance. The
standard way to inherit an interface in C++ is to inherit publicly
from a class that has (pure) virtual member functions. Pure interface
inheritance can be approximated in C++ by inheriting publicly from
pure abstract classes. Pure implementation or class inheritance can
be approximated with private inheritance. In Smalltalk, inheritance
means just implementation inheritance. You can assign instances of any
class to a variable as long as those instances support the operation
performed on the value of the variable.</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -