|
|
Warp wrote:
> So if setting a parameter to a given value can fail, you can't check
> the success/failure of the function call by having the function return
> eg. a boolean value informing of this, but instead you have to write a
> separate function which you have to call in order to query whether the
> operation was successful or not? And this for each such parameter which
> can fail?
Well, there's two kinds of "setting it failed". One is (for example) setting
a printer to use a certain kind of paper. Here you can't tell before you try
that it's going to fail (assuming it "fails" if that sort of paper isn't in
the printer the moment you set it).
The other kind is (for example) popping the stack on an empty stack, which
you can tell will fail before you even try it. In this case, it's considered
a programming error on the part of the caller to even attempt to pop an
empty stack.
The recommendation that queries and commands be separate is it keeps you
from having to put it in a variable that's unrelated to the object you're
calling. Why would the 'did the paper setting function work' value ever be
stored in the local variables of the function that generates the report,
rather than in the object that represents the printer itself?
It also means that you can have one part of you code intialize the printer
and another part check it's ready to go, without passing printer-specific
values around in code that hasn't anything to do with the printer. It's more
a "big program" sort of technique, along the same lines as "each object
should be responsible for only one thing" sort of thing. The object takes
full responsibility for tracking all its state, and you query the object for
its state every single time you need to know it. If you return status from
mutating calls, you're passing responsibility for tracking the status back
to the caller instead of encapsulating it in the object.
> This presents a multitude of problems. For instance, the class would
> have to store booleans for each parameter somewhere, so that the "did
> the setting of that parameter succeed?" function can return it.
Right. Or, the caller has to do that when they call the function.
The function still has to save that information, because if setting the
paper size failed, you can't print to the printer, right? If you *can*
print to the printer, then it isn't "did the paper setting fail?", but
"after I told you to use A4, what paper size will you use?" It's only a
"failure" if the caller can't accept the paper size the printer's ready to use.
For example: if you set the paper size, the object still has to know what
the paper size is. If you can have a type with a distinguished value (like
NULL in C), you can have the function that returns the paper size return
NULL if the paper size didn't get set correctly, for example.
Look at something like the getchar() function in C. How often do you use
that where you don't have to assign it to a variable to use it? Not very
often. You always wind up writing something like
while ((c = getchar()) != EOF) { do something with c }
Instead, the recommendation is to use the equivalent of
readchar();
while (currentchar() != EOF) {
do something with currentchar()
and something else with currentchar()
readchar();
}
It's a little more verbose, but the "current character" stays with the
object it's associated with. When you start putting distinguished-caller
syntax in the mix, it becomes a lot easier to understand what's going on
when you're in maintenance mode. Like, say you're comparing two lists:
one.initlist();
two.initlist();
while (one.curr() != two.curr()) {
if (one.curr() < two.curr()) one.nextitem();
else if (two.curr() < one.curr()) two.nextitem();
}
If "nextitem" returned the next item of the list, the logic would be
somewhat more convoluted, trying to keep the local variables synchronized
with the contents of the lists, and it would be easy to accidentally assign
two.nextitem() to m_one or something.
> What if the success query function is called *before* the setting of
> the parameter function is called? Can the return value have a meaningful
> interpretation anymore?
I imagine it depends on the function. If it's initialized to a default that
works (like whatever paper size happens to be loaded), then the "will this
work" function would return true. If it's initialized to a value that's
invalid (like the name of the file on a closed file handle) then the "will
this work" function returns false. The constructor has to assure that the
query functions are valid, just like any other initialization the
constructor needs to do.
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
|