![]() |
Sponsored Link •
|
Advertisement
|
Objects that expose public instance variables are messengers, because they carry data, but these objects are naked messengers.
Point
and Dimension
-- could be immutables
ServiceMatches
-- could be immutable
Entry
-- good reason to be defenseless
Messengers are more vulnerable when they walk around naked, because they
are exposing their privates. For example, I can set the width
field of a java.awt.Dimension
to -5, a corrupt value, and
there's not a damn thing that Dimension
can do about it.
Is this the kind of world you want to live in?
A Dimension
with both set
and get
methods could be reused,
Class Entry
is an interesting example of a naked object. Explain
the reasoning behind them. Any methods I've ever put into entries are for
convenience. Because when you don't control your data, you can't guarantee
that you can fulfill a contract. In summary, naked messengers are
quite contrary to the whole thrust of object-oriented design, where
responsibilities are divided among various objects, and each object
promised to fulfill the obligations outlined in its contract.
Talk about the increment
method in ChatMessage
and perhaps the getFactory
method in UIDescriptor
. Private data is key to enabling an
object to fulfill its contract.
Most of the things I mentioned in Guideline 1, debuggable, flexible, modular, abstract, require private data.
When you don't control your data, you don't control your destiny.
Use the URLFactoryLink
as an example of can't cache a messenger. Got to
create a new ServiceItem
each time. What they did in Component
was
add a getSize
method that takes a Dimension
, a bucket to fill.
Oddly enough, if the idea of making public data is that it can be reused, then it is up
to the client to reuse the object, because provider can't cache and reuse.
get
is sometimes dropped from high traffic methods (e.g., iterator()
in
java.util.Collection
instead of getIterator()
, so getX()
could have been left as x()
.
Make members as private as possible, especially data.
set()
and get()
, set()
and get()
properties, not instance variables
I also tend to shy away from protected data and package access data. At least in package access data, the coupling is still limited to the package, but its still more heavily coupled. A class can't really be responsible for fulfilling its contract if it has package-access data, its package is responsible for fulfilling its contract. Likewise, if data is protected, then its package and all its subclasses are responsible for fulfilling its contract. Once again, you can see the responsibility being spread to far and wide. You are missing the advantage of encapsulation.
Occasionally, bundles of class methods or bundles of constants Use class members to control access to objects, provide utility methods, or offer bundles of constants. Examples of controlling access to objects, static factory methods, singleton pattern.
James Gosling: What is the API to Windows? Nobody knows. In fact, even Microsoft doesn't know what the Windows API is. Because they publish an API, but lots of people sneak in through back doors. [...]Because there isn't a really good, strong interface notion in C, people just sort of reach in through the back doors. And they do God knows what. And so when Microsoft goes to release a new version of Windows, what they essentially have to do is test everything.
It was a very important property of interfaces that they are very strict. That wasn't just me deciding to be nasty. That was the world is madness if the contracts aren't strict. Because then all of a sudden--the whole notion of object-oriented programming falls apart. You lose the ability to unplug this and plug that in. If you don't know what the shape of the plug is, how do you know it can plug in?
Complexity is the enemy of robustness, because complexity tends to generate highly coupled systems. The more complex a system becomes, the more difficult it is to achieve overall robustness. that objects are a debuggable units. Because of minimized coupling, you can hammer away at those objects and get them working very well. Achieve robustness on the whole by having each object piece be very robust (i.e., work well). An object is also a debuggable unit. Objects help programmers manage, not eliminate, complexity. Individual objects can still be quite complex. And where there is complexity, there are bugs.
At the first Jini Community meeting, Bill Joy said that back when he was at Berkeley, he would print out C code that comprised UNIX and try and see how many bugs he could find by just looking at the code. He said he found an average of two bugs per page. He said that because C doesn't have a strong interface notion, it's hard to isolate things, which encourages highly coupled systems.
I once worked on a C program that served as control software for a semiconductor photomask inspection station. Unfortunately, the code that I changed was using a global variable that was coupled to some other, completely unrelated part of the program in a very non-intuitive way. Before long, a customer reported a serious new bug. By making seemingly innocuous changes to fix a very minor bug, I had inadvertently created the serious bug.
Separation of interface and implementation, one of the main ideas in object-oriented programming, helps programmers create robust objects. The code that manipulates an object's data is sitting in the object's class. If that data becomes corrupt while the object is in use, you know the bug is in some method in the object's class. Once you find the bug and fix it, it is fixed everywhere. When code that uses data is spread out all over the place, as is often the case in C programs, you can't be certain you won't encounter the same bug in some other snipet of code somewhere else that access the same data.
If the instance variables of an object are private, and those Because each island represents a managable level of complexity, Once the provider of an object gets all the bugs get ironed out of the object's implementation, those bugs tend to stay fixed because client's can't directly interact with the implementation, they can only talk to the interface. (Of course, someone in the next cubicle could of course in there and unfixes it).
Well-designed objects allow you to prevent such scary coupling in your systems, no matter how complex.
A modular unit: Can't go around the interface, so can unplug this and plug that back in. (Requires strict enforcement of separation of interface and implementatoin at runtime.)
A Secure Unit - Type Safety is the cornerstone of Java's security model
Sponsored Links
|