From: andrew@tagsys.com (Andrew Gideon)
Subject: Complexity in the eyes...V2.0
Organization: TAG Systems inc.
Date: Thu, 17 Dec 1992 04:22:07 GMT


Almost a month ago, I tried to start a new thread on the topic
of "complexity".  I assume that everyone agrees that a goal of
software engineering should be the reduction of complexity.  But
what I have recently seen is that the metrics by which complexity
is measured appear to be very subjective.

There were some very interesting comments, but - perhaps because I
fell way behind in my net-readership - the thread died a quiet
death.  I'd like to try again, in this case with a specific example.

First, however, I'd like to reply to some of the postings and email
that I received.

1.	karl@BofA.com (Karl Nicholas) asks why a consumer of classes
	would be peeking around the internals of those classes, 
	and any possible superclasses.

	A lot of people (well...a few) provided answers.  I provided
	a couple of simplisitic ones (I was rushed!): debugging and
	understanding.

	maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) agreed with the
	"understanding" issue.

	I still think, however, that good documentation would solve
	that problem.  The fact that I've never seen documentation
	of sufficient quality may be significant, however.  Perhaps
	some nifty tool which reads class definitions provides a
	"flattened" interface?

	gregc@eagle.fsl.noaa.gov (Greg Compestine), BTW, 
	brought up the same point.  He wants ObjectWorks for
	xmas...I'll be evaluating it shortly.  Maybe it'll turn out
	to be that nifty tool.

2.	I cannot decide quite how to take the answer from
	rmartin@thor.Rational.COM (Bob Martin).  He suggested
	gathering 10 engineers, provide half with a solution one
	way and half with a solution the other way.  Then measure
	how long it takes for them to make a modification.

	If I had a lot of time and 10 engineers, it would certainly
	be an interesting experiment.  Unfortunately, I have neither.

	My *opinion* is that I have been better able to respond to
	shifting requirements than has been my coworker who prefers
	less inheritance, dispatching, and other forms of modularization
	(is that a word?).  But his opinion may differ.

3.	rknazik@x102a.ess.harris.com (knazik bob) suggested a limit
	of inheritance levels to be used (he proposed four).  This
	bothers me.  It is (1) arbitrary and (2) difficult in practice
	to do as the user of a class is STILL required to figure out
	how many of these levels are already "used".

	Also, it ignores the cases where some class within a heriarchy
	must be fragmented.  Maybe I have a Hand class, but I need a
	LeftHand class.  I can fragment the Hand into LeftHand and 
	RightHand, and then reconstruct Hand using MI.  But if there
	are enough levels of inheritance below Hand (and how would 
	one know this!), this fragmentation is prevented.

Anyway, this is getting long, and I want to write my example up.
My example is a problem and two solutions: mine and my coworker's.
The question that I have is: which do you (plural) prefer (for any
reasons), and why?

The problem: moving errors around.  This includes moving errors
between applications, but it also includes the conventional problem
of passing errors up through a sequence of procedure calls.

My solution (well, I don't claim that the original idea was mine):
an error stack class.  This permits a function to push messages
onto the stack as the error is propogated upwards.  An error
stack might contain:

	Save of dohickey failed

	Unable to write dohickey.dat

	disk full

The lower levels contain greater detail on the failure, as they
are generated closer to the actual fail point.  The higher levels
contain additional context information, as they know more of "why"
the failed operation was requested.

Because this is a class, I was easily able to add methods to
support transporting these between applications.  The class also
supports code like:

	ErrorStack err;

	if (!(err = foo()))
	{
		//
		// Take some failure action
		//
		err.push("More error info");
		return(err);
	}

[For those that find this too terse (like me), it also
 provides methods like isOk(), isBad(), etc.  That use of a
 not operator is a convention, but I find "if ((err = foo()).isBad())"
 more obvious.]

To me, this is simple.  It is simple to use merely as a way to
return error/success.  It is simple to get the outer-most error
(or the inner-most).  I also provided an iterator for those hardy
souls who want to access all the messages in the stack.

[I also provided a log method.  This one feature alone was a great aid
 on those all-to-frequent occasions when I had to debug a runtime
 problem.]

My coworker thinks this complex.  He'd prefer returning an integer.
The integer is an offset into an array of error messages.  If there
are multiple modules (when are there not?!), use one byte as a module
number to determine the specific array, and another byte as the offset.

He doesn't even want this encapsulated in a class, as that would
"make it more complex."  He sites as evidence that - at our last
location - few people wanted to use the error stack class.  I
attribute this to lack of documentation (I was not contracted to
provide reusable tools there, so I never did get to build decent 
documentation) and publicity.  Also, my biased opinion is that too
few people even think about errors; some are still happy with global
error number variables.  Error handling behavior is "stuck on" rather
than "designed in".

So...what is the opinion of the net?

	- Andrew Gideon


=== 
 -----------------------------------------------------------
| Andrew Gideon              |                              |
| Consultant                 |                              |
|                            |   TAG Systems inc.           |
| Tel: (201) 890-7189        |   D2-181 Long Hill Road      |
| Fax: (201) 890-1581        |   Little Falls, N.J., 07424  |
| andrew@tagsys.com          |                              |
 -----------------------------------------------------------