On Jun 28, 8:48 pm, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
wrote:
On Thu, 28 Jun 2007 18:54:44 -0700, jehugalea...@gm ail.com
<jehugalea...@g mail.comwrote:
[...]
Notice that there is no longer a need for try/catch. If something goes
wrong, the copy, tempDog, just gets wasted. The final line is to
update 'this' with the changes.
Now, this seems really simple to me. I have trouble believing that
someone didn't come up with something more general or correct (I think
mine is correct???). For instance, there is a lot to be concerned
about with deep/shallow copies.
I'm afraid I don't really understand the question. The technique -- to
create a temporary instance on which one operates and then somehow replace
the original with the temporary version -- you describe is both general
and widespread. It applies in a number of scenarios, and not just in OOP
classes (for example, writing to a temp file, and only deleting the
original and renaming the temp file if the temp file is successfully and
completely created).
That said, C++ does not inherently support this technique any more than C#
does. So I don't really understand the comparison you seem to be making
between C++ and C#.
As far as whether the technique is appropriate generally, I'd say much of
the time it's not. It really depends on what you're doing, but creating a
complete copy of an instance, modifying, and then copying all of that back
to the original has a lot of overhead and itself requires implementation
of the Copy() method or something like it. I have yet to run into a
situation where I felt it was unreasonable to simply do whatever cleanup I
need to do locally should an exception occur.
Note also that in your example, you sort of gloss over the fact that the
exception still needs to be caught somewhere. Yes, you can catch it and
just ignore it. But if you're going to have to write the exception
handler anyway, why not have it do something useful, like clean up after
whatever operation the code contained within was doing?
For those times when the technique is appropriate, then sure...I'd agree
that it's, well...appropri ate. Would I write it into every one of my
classes as a standard matter? Nope.
Pete
My original post asks for a community-wide approach to exception
safety. I wasn't really concerned with the differences between
languages or how the copy/modify/replace idiom applies to various
other aspects of computer science. I simply want to know what other
developers have done to face the problem.
I don't agree that catch blocks should be utilized to undo what the
try was trying to. It is probably one of the most efficient means.
However, programmers are generally forgetful. Having to control that
depth of code inside potentially many catch blocks is a maintenance
nightmare. You would probably create a single method that resets the
values. But then you need to know what to reset them to. So you need
to store those values somewhere.
I am just suggesting (as it makes sense to group related data) to
create a copy. You don't have to muddy up your code with try/catch
then. And I am not all too concerned about handling the exceptions. As
such, I don't even want to make my code *appear* to be concerned about
them; it should just do the right thing.
I don't think the implementation of a copy() method would be a big
chore. You saw that it can be fairly easy. You are just saving your
state. assignment assignment assignment . . .
Now here is the dilemma . . . since we created a copy of our class, we
cannot call any methods that create a copy of our class without
redundant copies being made. So we have to assume that we are only
dealing with the fields directly or methods that are not exception
safe. It seems like a huge limitation. However, I think this presents
a wonderful opportunity! I think this suggests that we create
interface methods and implementation methods, where one simply
delegates to the other. We can then formalize the copy/modify/replace
process.
public class Dog
{
public void CelebrateBirthd ay()
{
Dog tempDog = new tempDog(); // all public modifying methods
begin and end the same way
tempDog.celebra teBirthday(); // implementation method
this.copy(tempD og);
}
}
Now, personally, I do not think copying is a large waste. We are
talking about just a few bytes for typical classes (most of which
would need to be stored to undo changes anyway). We could probably
even get away with reduntant copies if we wanted to. It depends how
anal we are about efficiency. If we truly wanted, we could even make
the state of the class a private struct with public fields and leave
ourselves open to enter the realm of the state pattern (if needed) and
just copy/modify/replace it.
We need to store the original state somehow, somewhere. We originally
created the class to enclose that data. Why not use it?
Now, from my experience, I rarely deal with exceptions locally - I
don't know how to; it's not always plausible/possible. At most I will
use a finally block to "clean up". However, clean up always takes
place, so I can't put an "undo" operation here. I feel stupid dealing
with clean up in each catch or even one catch. Your time-traveling Dog
ends up looking like this:
public void CelebrateBirthd ay()
{
int ageChange = new Random().Next(1 0); // what does -1 mean for a
Dog that can travel to the past?
try
{
age += ageChange;
}
catch
{
age -= ageChange;
}
}
My version would be about three lines long. I don't need to worry
about the scope of ageChange; I don't need to handle all the other
variables that go into my time-travelling Dog. True, I only need to
store the variables I change, but how do I know one of the methods
inside my method won't change someday to alter additional ones? Other
programmers aren't going to understand why you have all the additional
lines of code, just to add a few years to a poor dog's life. Where we
create an implementation method, the code is straight forward because
it removes all exception handling. It just does what it says it does
and nothing else. Since we can formalize the copy/modify/replace in
every public method, my programmers only need to remember one thing
and not need to understand (they can practically forget, like most
programmers do anyway) about exception safety. If efficiency is
absolutely critical, your method is pure adrenaline. But in general,
this approach is more readable and more maintainable, and hardly less
efficient.
Let me know if I am getting on your nerves. I think I have something
that is not just occassionally appropriate; I think it is appropriate
(not always the most efficient) for just about any exception-safe
class. I think efficiency gains should be made after a profiler has
said you should. It all comes down to a memcopy my friend; it just a
matter of how big the loop is.
~Travis