📄 ch20.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
<META NAME="Author" Content="Steph Mineart">
<TITLE>Ch 20 -- Encapsulation</TITLE>
</HEAD>
<BODY
BACKGROUND="bg1.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/bg1.gif" BGCOLOR="#FFFFFF">
<P ALIGN="CENTER"><IMG SRC="sams.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/sams.gif" WIDTH="75" HEIGHT="24" ALIGN="BOTTOM"
BORDER="0"><BR>
<BR>
<A HREF="index-3.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/index.htm"><IMG SRC="toc.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/toc.gif" WIDTH="40" HEIGHT="40" ALIGN="BOTTOM"
ALT="TOC" BORDER="0" NAME="toc4"></A><A HREF="ch19.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch19.htm"><IMG SRC="back-1.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/back.gif"
WIDTH="40" HEIGHT="40" ALIGN="BOTTOM" ALT="BACK" BORDER="0" NAME="toc1"></A><A HREF="ch21.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch21.htm"><IMG
SRC="forward.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/forward.gif" WIDTH="40" HEIGHT="40" ALIGN="BOTTOM"
ALT="FORWARD" BORDER="0"
NAME="toc2"></A></P>
<H2 ALIGN="CENTER"><FONT COLOR="#000077">Charlie Calvert's C++ Builder Unleashed</FONT></H2>
<P>
<H2 ALIGN="CENTER"><A NAME="Heading1"></A><FONT COLOR="#000077">- 20 -</FONT></H2>
<H2 ALIGN="CENTER"><A
NAME="Heading2"></A><FONT COLOR="#000077">Encapsulation</FONT></H2>
<P>
<H3><A NAME="Heading3"></A><FONT COLOR="#000077">Overview</FONT></H3>
<P>In this chapter, you continue the overview of object-oriented programming begun
in the last chapter. In
particular, the focus is on encapsulation and properties.
The next chapter focuses on polymorphism.</P>
<P>This chapter covers the following topics:
<UL>
<LI>An overview of encapsulation, including the need to hide data and certain parts
of the
implementation
<P>
<LI>An in-depth look at the <TT>private</TT>, <TT>protected</TT>, <TT>public</TT>,
and __<TT>published</TT> access specifiers
<P>
<LI>Property creation
<P>
<LI>The five basic types of properties
<P>
<LI>Read-only properties
<P>
<LI>Default value declarations for properties
</UL>
<P>This chapter features some of the syntactical jewels in the BCB treasure chest.
In particular, it offers a complete array of scoping directives. These tools enable
you to fine-tune access to
your objects in a way that helps promote their re-use.
Properties are also cutting-edge tools, and their implementation in BCB yields some
surprising fruits, such as arrays that are indexed on strings.
<H3><A NAME="Heading4"></A><FONT
COLOR="#000077">Encapsulation</FONT></H3>
<P>The words encapsulation and object are closely linked in my mind. Encapsulation
is one of the primary and most fundamental aspects of OOP. It is useful because it
helps to enlist related methods and data
under a single aegis, and to hide the implementation
details that don't need to be exposed or that might change in future versions of
an object.</P>
<P>The ability to encapsulate methods and data inside an object is important because
it helps you
design clean, well-written programs. To use a classic example, suppose
you were to create an object called <TT>TAirplane</TT>, which would represent (naturally
enough) an airplane. This object might have fields such as <TT>Altitude</TT>,
<TT>Speed</TT>,
and <TT>NumPassengers</TT>; it might have methods such as <TT>TakeOff</TT>, <TT>Land</TT>,
<TT>Climb</TT>, and <TT>Descend</TT>. From a design point of view, everything is
simpler if you can encapsulate all these fields and methods in
a single object, rather
than leaving them spread out as individual variables and routines:</P>
<PRE><FONT COLOR="#0066FF">class TAirplane: public TObject
{
int Altitude;
int Speed;
int NumPassengers;
void TakeOff();
void Land();
void Climb();
void Descend();
};
</FONT></PRE>
<P>There is a sleek elegance in this simple object declaration. Its purpose and the
means for implementing its functionality are readily apparent.</P>
<P>Consider the class declaration from the
current version of the Object3 program:</P>
<PRE><FONT COLOR="#0066FF"> class THierarchy: public TMyObject
{
int FTextColor;
int FBackColor;
protected:
virtual void SetTextColor(int Color)
{ FTextColor = Color; textcolor(FTextColor); }
virtual void SetBackColor(int Color)
{ FBackColor = Color; textbackground(FBackColor); }
public:
THierarchy() : TMyObject() {}
virtual void PrintString(AnsiString S);
virtual void ClrScr();
__property int
TextColor={read=FTextColor,write=SetTextColor};
__property int BackColor={read=FBackColor,write=SetBackColor};
};
</FONT></PRE>
<P><TT>THierarchy</TT> encapsulates a certain amount of functionality, including
the <TT>ShowHierarchy</TT> method it
inherits from <TT>TMyObject</TT>. If you want
to call <TT>ShowHierarchy</TT>, you need to first instantiate an object of type <TT>THierarchy</TT>
and then use that object as a qualifier when you call <TT>ShowHierarchy</TT>:</P>
<PRE><FONT
COLOR="#0066FF">void main()
{
THierarchy *H = new THierarchy();
H->TextColor = YELLOW;
H->BackColor = BLUE;
H->ShowHierarchy(H);
delete H;
}
</FONT></PRE>
<P>This kind of encapsulation is useful primarily because it makes
you treat everything
about the <TT>THierarchy</TT> object as a single unit. There are, however, other
advantages to this system, which become apparent when you examine the access specifiers
used in the class declaration.</P>
<P>BCB defines four
keywords meant to aid in the process of encapsulation by specifying
the access levels for the various parts of an object:
<UL>
<LI><B><TT>private</TT>:</B> Use this directive to declare a section in a class that
can be accessed only from inside the
current object. The only way around this limitation
is to declare a class as a friend.
<P>
<LI><B><TT>protected</TT>:</B> Code declared in a protected section can be accessed
only by the current object and by descendant objects. The point here is
that protected
fields and methods are available primarily to other component developers, not to
standard consumers of the object.
<P>
<LI><B><TT>public</TT>:</B> Code declared in a public section of an object is available
to anyone who uses an
object of that particular type. Along with the published section,
it is the standard interface of the object.
<P>
<LI><B><TT>__published</TT>: </B>Properties that are declared in the published section
are public variables that appear in the Object
Inspector. Furthermore, you can discover
the type of published properties at runtime; simple published properties can be streamed
to disk automatically. Because published properties are for use in the Object Inspector,
you should not declare a
property as published unless the object descends from <TT>TComponent</TT>
or one of <TT>TComponent</TT>'s children. <TT>TComponent</TT> is the base object
from which all objects that appear on the Component Palette must descend.
</UL>
<P>All the
data in your programs should be declared <TT>private</TT> and should be
accessed only through methods or properties. As a rule, it is a serious design error
to ever give anyone direct access to the data of an object. Giving other objects
or non-OOP
routines direct access to data is a sure way to get into deep trouble
when it comes time to maintain or redesign part of a program. To the degree that
it's practical, you might even want the object itself to access its own data primarily
through
properties or public routines.</P>
<P>The whole idea that some parts of an object should remain forever concealed from
other programmers is one of the hardest ideas for new OOP programmers to grasp. In
fact, in early versions of Turbo Pascal with
Objects, this aspect of encapsulation
was given short shrift. Experience, however, has shown that many well-constructed
objects consist of two parts:
<UL>
<LI>Data and implementation sections hidden from the programmers who use the object.
<P>
<LI>A set of interface routines that enable programmers to talk to the concealed
methods and data that form the heart of an object.
</UL>
<P>Data and implementation sections are hidden so that the developer of the object
can feel free to change
those sections at a later date. If you expose a piece of
data or a method to the world and find a bug that forces you to change the type of
that data or the declaration for that method, you are breaking the code of people
who rely on that data or that
method. Therefore, it's best to keep all your key methods
and data hidden from consumers of your object. Give them access to those methods
and procedures only through properties and public methods. Keep the guts of your
object private, so that you can
rewrite it, debug it, or rearrange it at any time.</P>
<P>One way to approach the subject of hiding data and methods is to think of objects
as essentially modest beings. An object doesn't want to show the world how it performs
some task, and it is
especially careful not to directly show the world the data or
data structures it uses to store information. The actual data used in an object is
a private matter that should never be exposed in public. The methods that manipulate
that data should also
be hidden from view, because they are the personal business
of that object. Of course, an object does not want to be completely hidden from view,
so it supplies a set of interface routines that talk to the world--but these interface
routines jealously
guard the secret of how an object's functionality is actually
implemented.</P>
<P>A well-made object is like a beautiful woman who conceals her charms from a prying
world. Conversely, a poorly made object should also hide, much like an elderly man
who
doesn't want the world to see how his youth and virility have faded. These analogies
are intentionally a bit whimsical, but they help to illustrate the extremely powerful
taboo associated with directly exposing data and certain parts of the
implementation.</P>
<P>Of course, the point of hiding data and implementations is not primarily to conceal
how an object works, but to make it possible to completely rewrite the core of an
object without changing the way it interacts with the world.
In short, the previous
analogies collapse when it comes to a functional analysis of data hiding, but they
serve well to express the spirit of the enterprise.
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>If you have the
source code to the
VCL, you will find that at least half of most key objects in the BCB code base are
declared as <TT>private</TT>. A few complex objects contain several hundred lines
of private declarations, and only 20 or 30 methods and
properties that serve as an
interface for that object. Objects near the top of a hierarchy don't always follow
this model, because they usually consist primarily of interface routines. It's the
core objects that form the heart of a hierarchy that
are currently under discussion.
<HR>
</BLOCKQUOTE>
<P>If you will allow me to mix my metaphors rather egregiously, and to introduce
a fairly strong set of images, I can provide one further way to think about the internal
and public sections of an
object. Rather than thinking of the private parts of an
object from a sexual perspective, you can literally think of them as the internal
organs of the object. As such, they should, by definition, never be exposed directly
to the light of day.
Instead, they are covered by skin and bone, which is the public
interface of the object.</P>
<P>Our face and hands are the public part of our being, and our internal organs are
covered with skin and bone and never see the light of day. The only reason
ever to
expose these organs is if you are in the hospital--that is, during object maintenance.
Furthermore, any object that does expose its private parts to the light of day is
incomplete, a monster from Dr. Moreau's island, where gruesome experiments
roam free.
<H3><A NAME="Heading6"></A><FONT COLOR="#000077">Simplicity: The Secret of Good Object
Design</FONT></H3>
<P>Before moving on, I want to talk about the importance of creating easy-to-use
interfaces. Notice, for instance, how easy it is to
use the <TT>TTable</TT>, <TT>TDataSource</TT>,
and <TT>TDBGrid</TT> objects. These are examples of well-constructed objects with
easy-to-use interfaces. If you take these objects away, however, and access the functions
in the <TT>DbiProcs.c</TT>
directly, you see that <TT>TTable</TT>, <TT>TDBGrid</TT>,
and <TT>TDataSource</TT> conceal a great deal of complexity. In other words, if you
have to call the raw BDE functions directly to open and view a table, you will find
that the process is both
tedious and error-prone. Using <TT>TTable</TT>, however,
is very easy. You might need to have someone tell you how it works the first time
you see it, but after that, the process of getting hooked up to a table and displaying
data to the user is
trivial. All good programmers should strive to make their objects
this easy to use.</P>
<P>In the next few chapters, I discuss component creation. Components are very useful
because they help guide you in the process of creating a simple, easy-to-use
object.
If you can drop an object onto a form and hook it up with just a few clicks in the
Object Inspector, you know that you have at least begun to create a good design.
If you drop an object onto a form and then need to mess around with it for 15
or
20 minutes before you can use it, you know something is probably wrong. Objects should
be easy to use, whether they are placed on the Component Palette or not.</P>
<P>Simplicity is important because it helps people write error-free programs. If
you
have to complete 30 steps to hook up an object, there are 30 opportunities to
make a mistake. If you need to complete only two steps, it is less likely that an
error will occur. It is also easier for people to understand how to complete two
steps,
whereas they often can become confused when trying to learn a 30-step process.</P>
<P>I do not mean to say that the act of creating an object is simple or easy. I have
seen great programmers wrestle for months with the overwhelming minutiae of
creating
a complex object that is easy to use. A good programmer doesn't think the job is
done just because the code works. Good code should not only work, but it should also
be easy to use! Conversely, I often suspect that an object is poorly
designed if
it is difficult to use. In most cases, my suspicions turn out to be true, and a complex
interface ends up being a wrapper around a buggy and an ineptly coded object.</P>
<P>No hard-and-fast rules exist in the area of object design.
However, the presence
of an easy-to-use interface is a good sign. If you are working with a component,
you should be able to drop it onto a form and hook it up in just a few seconds. If
you are working with an object that is not encapsulated in a
component, you should
be able to initialize and use it by writing just a few lines of code. If hooking
up takes more than a few minutes, you often have reason to be suspicious of the entire
enterprise.</P>
<P>The private methods of your objects should
also be cleanly and logically designed.
However, it is not a disaster if your private methods are difficult to understand,
as long as your public methods are easy to use. The bottom line is that the private
methods should be designed to accomplish a
task, and the public methods should be
designed to be easy to use. When you look at the design this way, you can see a vast
difference between the public and private methods in an object.</P>
<P>The scope of knowledge programmers must master today is
huge. Professionals are
expected to understand the Windows API, OLE, the Internet, graphics, at least two
languages, object design, and database architectures. Some of these fields, such
as Internet programming, break down into many complex parts such
as ISAPI, CGI, WinINet,
TCP/IP, electronic mail, HTTP, and FTP. Learning about all these areas of programming
is an enormously complex task. In fact, for all practical purposes, it is impossible.
No one person can know everything necessary to write
most contemporary programs from
scratch. As a result, he or she needs to find easy-to-use objects that encapsulate
complexity and make it usable.</P>
<P>The worst mistake an object creator can make is to insist that the consumer of
the object knows as
much about the subject as its creator. It's not enough to define
a set of useful routines that can speed development if you already know a subject
inside and out. Instead, you need to create objects that someone can use, even if
he or she doesn't
understand the details of how a particular subject works.</P>
<P>The trick to good object design, of course, is to simultaneously present an easy-to-use
interface, while also giving experienced programmers access to the fine points of
a particular
area. Once again, <TT>TTable</TT> and <TT>TQuery</TT> serve as an example.
<TT>TTable</TT> is an easy-to-use object providing neophytes easy access to data.
At the same time, it allows an expert to manipulate one-to-many relationships, lookups,
filters, and indices. Add <TT>TQuery</TT> to the mix, and there are few limits to
what you can do with these objects in terms of manipulating databases.</P>
<P><TT>TMediaPlayer</TT> is another example of a component that takes a relatively
complex API
and makes it easy to use. It is arguable that this component could have
a bit more depth, but it allows programmers to run multimedia applications without
having to understand <TT>mciSendCommand</TT> and its many complex parameters, structures
and
constants.</P>
<P>The bottom line is simplicity. If you create an object that is as hard to use
as the API it encapsulates, 95 percent of the people who see your object will consider
it useless. The reason programmers create objects is to provide an
easy, bug-free
interface to a particular area of programming.</P>
<P>As you read through the next few chapters, you might consider thinking about the
importance of creating a simple interface to your objects. Having a simple interface
is particularly
important for the top-level objects in your hierarchy.
<H3><A NAME="Heading7"></A><FONT COLOR="#000077">A Concrete Example</FONT></H3>
<P>Neither <TT>TMyObject</TT> nor <TT>THierarchy</TT> provides much scope for exploring
encapsulation. As a result,
it's time to introduce a new class called <TT>TWidget</TT>,
which is a descendant of <TT>TCustomControl</TT> and uses a descendant of <TT>THierarchy</TT>
through aggregation. In Chapter 21, "Polymorphism," <TT>TWidget</TT> becomes
the
ancestor of a series of different kinds of widgets that can be stored in a warehouse.
In other words, in a computer factory <TT>TWidget</TT> might be the ancestor of several
classes of objects such as <TT>TSiliconChip</TT>, <TT>TAddOnBoard</TT>, and
<TT>TPowerSupply</TT>.
Descendants of these objects might be <TT>TPentiumChip</TT>, <TT>TPentiumProChip</TT>,
<TT>TVideoBoard</TT>, and so on.
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>I suppose one could literally
think
of the <TT>TWidget</TT> descendants as representations of concrete objects or as
abstract representations of these objects. The meaning I intend is the latter. <BR>
<BR>
This later, more abstract, technique is similar to the process used in
object-oriented
databases. Instead of having a table that stores rows of raw data, the table could
instead store objects that represent data. When a new 486 chip rolls off the line,
a new <TT>T486Chip</TT> object is added to the database to
represent this physical
object. You could then query the abstract object and ask it questions: "When
were you made? What part of the warehouse are you stored in? What is your wholesale
price? What is your street price?" Thus, the abstract
object would be a computerized
representation of something in the real world.<BR>
<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -