[comp.lang.c++] object-oriented design

psrc@pegasus.ATT.COM (Paul S. R. Chisholm) (03/27/90)

(I tried to be careful about attribution; I apologize if I goofed.)

In article <169@pollux.kulcs.uucp>, herman@kulcs.uucp (Herman Moons) writes:
> I don't know whether this question appeared earlier on the net.

(*sigh*)  I don't suppose anyone's collecting frequently asked
questions (and their answers) . . .  Also, please note that this whole
discussion deals only with the abstract data typing features of C++,
not the object-oriented features.

> Are there any *good* reasons for having the friend concept in C++ ?

In article <10589@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
> Sure.  One of the most common is when you're writing several
> interrelated classes.

In article <1082@targon.UUCP>, ruud@targon.UUCP (Ruud Harmsen) writes:
> Only too true.  And isn't this also the fundamental weakness of the
> object oriented concept?  For in the real world, and so in any
> realistic information system describing a part of that real world,
> *nearly all* classes are interrelated to *nearly all* other
> classes.  Classes often only make sense just because of their
> relations to other classes.  So if you have to use friends for that,
> everyone will be anybody's friend, and as a result of all this
> friendship, a *real* project in an OO-language tends to become just
> the same mess as it does in a traditional language, like C.

As Dr. Koenig pointed out in a later article, "interrelated" means "the
implementation of one thing depends on the *implementation* of another
thing".

A "thing" in this case may be a whole class.  If you're careful, only a
few functions in class B (e.g., member function foo and static member
function bar) will need to know about the implementation of class A; in
which case, only B::foo and B::bar will need be friends of A.  This is
a Good Thing; it specifically limits what software can mess with an A,
and what code must be looked at when the implementation of A changes.

There's still a limitation, in that B::foo and B::bar must be visible
to A to be friends of A.  That implies that one class's private
functions can only be friends of another class if the functions are
public (or possibly protected), or if the whole first class is a friend
of the second class.  (I *assume* the visibility rules apply to
friendship.)

In article <626@ksr.UUCP>, reg@ksr.UUCP (Rick Genter) writes:
> As one who does have experience with OOT and with complicated
> applications, I (unfortunately) have to agree with the gist of this
> assessment. . . .

> Note that these [hard applications] can be made to work, but at a
> significant performance penalty.  The penalty is not the penalty of
> a single virtual method invocation vs. a single function call; I
> grant that that is (usually) insignficant.  But often what happens
> is that a method will invoke several other methods, each of which
> will invoke other methods, and so on and so on.  Suddenly that
> insignificant overhead becomes quite significant.

This has nothing to do with object-oriented design or programming; it
has to do with data abstraction, and with structuring software into
layers.  (I assume that you're talking about function call overhead per
se, and not function invocation vs. virutual method invocation.  The
difference is still trivial, especially if you add in the comparisons
that would otherwise need to be made.)  Granted, almost all object-
oriented designs will have layering; but so will almost all non-trivial
designs, OO or otherwise.

One of the highlights of C++ is that it allows trivial layers to have
trivial cost (i.e., a layer that just invokes another layer can be
implemented with an inline function).

> It seems to me that a study needs to be performed not on how to make
> everything OO, but on how to make composite designs work: i.e., how
> do I easily integrate my OO UI with my procedural data-flow
> analysis.

What, ho!  "Composite design" is a collection of guidelines to follow
when designing software:  to maximize the strength (or "cohesion") of
modules, and to minimize the coupling between modules.  These
principles are as applicable to OOD as any other design approach, and I
strongly recommend all of the following references:

L. L. Constantine, G. J. Meyers, and W. P. Stevens, "Structured
Design", IBM SYTEMS JOURNAL, vol. 13, no. 2, pp. 115-139.

Glenford J. Myers, RELIABLE SOFTWARE THROUGH COMPOSITE DESIGN, New
York:  Petrocelli/Charter, 1975.

Edward Yourdon and Larry L. Constantine, STRUCTURED DESIGN:
FUNDAMENTALS OF A DISCIPLINE OF COMPUTER PROGRAM AND SYSTEMS DESIGN,
Englewood Cliffs, N.J.:  Prentice-Hall, 1979.

The last is in some sense the most complete.  It also describes what
has since been known as "structured design", or the Yourdon half of the
Yourdon/DeMarco methodology that underlies almost all CASE tools.  One
starts by drawing dataflow diagrams; then one picks the "central
transform" in the diagram, making that the root of a tree, and
mechanically transforming the diagram into a tree.  (The result,
according to a comment from my CASE instructor, does *not* produce
designs that have high cohesion and low coupling; but I have no
reference other than this heresay.)

While the method sounds reasonable, I've never been able to
non-trivially decompose an interactive application into a dataflow
diagram.  Thus, Rick's comment about dataflow diagrams and OO user
interfaces baffles me.

Paul S. R. Chisholm, AT&T Bell Laboratories
att!pegasus!psrc, psrc@pegasus.att.com, AT&T Mail !psrchisholm
I'm not speaking for the company, I'm just speaking my mind.

dog@cbnewsl.ATT.COM (edward.n.schiebel) (03/27/90)

From article <4568@pegasus.ATT.COM>, by psrc@pegasus.ATT.COM (Paul S. R. Chisholm):
> A "thing" in this case may be a whole class.  If you're careful, only a
> few functions in class B (e.g., member function foo and static member
> function bar) will need to know about the implementation of class A; in
> which case, only B::foo and B::bar will need be friends of A.  This is
> a Good Thing; it specifically limits what software can mess with an A,
> and what code must be looked at when the implementation of A changes.
> 
> There's still a limitation, in that B::foo and B::bar must be visible
> to A to be friends of A.  That implies that one class's private
> functions can only be friends of another class if the functions are
> public (or possibly protected), or if the whole first class is a friend
> of the second class.  (I *assume* the visibility rules apply to
> friendship.)
> 
Not quite.  A private member function of B cannot be called by an A, but
class A may make a private member of B a friend: B's foo() may
access private parts of A, but A cannot call B::foo().

class B {
public:
  B();
private:
  void B::foo();
}

class A {
public:
  A();

  friend void B::foo();
};

	Ed Schiebel
	AT&T Bell Laboratories
	dog@cblph.att.com
			

ruud@targon.UUCP (Ruud Harmsen) (03/29/90)

In article <4568@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes:
>[...]
>In article <1082@targon.UUCP>, ruud@targon.UUCP (Ruud Harmsen) writes:
>> Only too true.  And isn't this also the fundamental weakness of the
>> object oriented concept?  For in the real world, and so in any
>> realistic information system describing a part of that real world,
>> *nearly all* classes are interrelated to *nearly all* other
>> classes.
>> [...]
>As Dr. Koenig pointed out in a later article, "interrelated" means "the
>implementation of one thing depends on the *implementation* of another
>thing".

If the knowledge of the "implementation" of a thing includes to know names
and types of the elements in a structure, it is my experience that some 70
to 80 percent of the code deals with several elements from several
structures, in many different combinations.  If then you try to restrict
direct access to the structure elements to functions of the same class,
while providing access from outside the class through functions, you may
have to create an individual function for every element.
This gives me the feeling that calling such a function becomes just a
syntactic variant of the "." and "->" operators, and only obscure things,
and help in no way whatever.

>A "thing" in this case may be a whole class.  If you're careful, only a
>few functions in class B (e.g., member function foo and static member
>function bar) will need to know about the implementation of class A; in
>which case, only B::foo and B::bar will need be friends of A.  This is
>a Good Thing; it specifically limits what software can mess with an A,
>and what code must be looked at when the implementation of A changes.
> [...]


In theory, this is correct.  But if in practice, in a particular
application 70 to 80 percent *has* to mess with a class A, then you can't
do very much limiting.
I think smart cross-reference tools, that tell you where the messing is
done, and also how (read-only, or write-also) it is done, are much more
useful, and can be applied to traditional languages just as well.

ruud@targon.UUCP (Ruud Harmsen) (04/05/90)

In article <4593@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes:
>I wrote:
>> >A "thing" in this case may be a whole class.  If you're careful, only a
>> >few functions in class B (e.g., member function foo and static member
>> >function bar) will need to know about the implementation of class A; in
>> >which case, only B::foo and B::bar will need be friends of A.  This is
>> >a Good Thing; it specifically limits what software can mess with an A,
>> >and what code must be looked at when the implementation of A changes.
>
I wrote:
>> In theory, this is correct.  But if in practice, in a particular
>> application 70 to 80 percent *has* to mess with a class A, then you can't
>> do very much limiting.
>
>Exactly right.  That's the signature of a poor design.  You have to
>*try*, hard, to hide the implementation of your data types.  Twenty
>years of programming have taught me that it's worth the effort.  Lots
>of other believe it, too.

I really hope you, and the other believers, are right.  But I'm not
convinced.  I don't think the trouble is in bad design, I think it's
in the nature of things.  The world simply isn't simple, cannot be
forced into strict hierarchical models (remember hierachical databases,
how much trouble they cause(d) in designing a system?), and any design
method that ignores or simplifies these facts, will eventually fail.

But I am always willing to learn, and I will keep on trying to apply
the theories, because I do believe the principles you mention are
themselves useful, and will help a bit, only not as much as many
people seem to think.

>I'm not speaking for the company, I'm just speaking my mind.
So am I.

	Ruud Harmsen.

ruud@targon.UUCP (Ruud Harmsen) (04/05/90)

In article <4593@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes:
>> In theory, this is correct.  But if in practice, in a particular
>> application 70 to 80 percent *has* to mess with a class A, then you can't
>> do very much limiting.
>
I wrote:
>Exactly right.  That's the signature of a poor design.  You have to
>*try*, hard, to hide the implementation of your data types.  Twenty
>years of programming have taught me that it's worth the effort.  Lots
>of other believe it, too.

Perhaps it all depends a lot on the kind of application.  In the ones
I dealt with most (financial accounting, invoicing, banking etc.) the
implementation of the data types, say the fields of the structs, are
exactly what the *user* of the system deals with, viz. enters, browses,
prints, talks about with his clients, etc.

How could it be useful to *hide* these things, if to the *user* of the
system, they are the only things that matter?  If you hide the data-items,
nothing useful remains.

I can imagine that in other sorts of applications, perhaps an operating
system, or a graphics system, this is quite different.
For example, the UNIX implementation of buffered IO is built in an
OO-fashion, and I am very content that I don't have to know what exactly
hides behind the type FILE, to be able to use it.  But now tell
a bookkeeper he doesn't need to know what data items are in the system
about his debtors!

So maybe that's why we may well be both right, and simply talking ahead of
each other, because we simply don't have the same kind of systems in mind?

davidm@uunet.UU.NET (David S. Masterson) (04/10/90)

In article <1115@targon.UUCP> ruud@targon.UUCP (Ruud Harmsen) writes:

   Perhaps it all depends a lot on the kind of application.  In the ones
   I dealt with most (financial accounting, invoicing, banking etc.) the
   implementation of the data types, say the fields of the structs, are
   exactly what the *user* of the system deals with, viz. enters, browses,
   prints, talks about with his clients, etc.

   How could it be useful to *hide* these things, if to the *user* of the
   system, they are the only things that matter?  If you hide the data-items,
   nothing useful remains.

Even in these types of applications, there is more to be concerned with than
just the structure of the data.  There may also be rules that go with the
data.  For instance, updating an employee's record may invoke a rule against
the company's financial records.  This may not be hidden from the user (or it
may), but the business rule must occur and, so, should be "tied" to the
employees data structure.  Sometimes (oftentimes), it is useful to hide this
tie from the "ordinary" user who is not allowed to change the rule anyway.
--
===================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mt. View, CA  94043
===================================================================
"If someone thinks they know what I said, then I didn't say it!"

lucio@proxima.UUCP (Lucio de Re) (04/10/90)

In article <1114@targon.UUCP> ruud@targon.UUCP (Ruud Harmsen) writes:
>In article <4593@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes:
>I wrote:
>>> In theory, this is correct.  But if in practice, in a particular
>>> application 70 to 80 percent *has* to mess with a class A, then you can't
>>> do very much limiting.
>>
>>Exactly right.  That's the signature of a poor design.  You have to
>>*try*, hard, to hide the implementation of your data types.  Twenty
>>years of programming have taught me that it's worth the effort.  Lots
>>of other believe it, too.

I am one of those believers. Fortunately for us programmers, the
reality we have to contend with is seldom of such a nature that
abstraction cannot reduce it to the point where classes provide
an adequate description. Even Modula 2, without the object oriented
abstractions did a formidable job of data hiding and at the same
time simplified coding. I appreciate that it is difficult to isolate
individual classes at a glance in a great many instances, and I
presume no proof is available to demonstrate that a class concept
is general enough to cover all instances of programmable tasks
(wow, what a mouthful!); I also think that Paul Chisholm used
excessively strong terminology in "the signature of a poor design,"
but his point is valid, better design will produce one or more
classes more faithfully modelling the system underscrutiny (I must
stop this pompousness, please no flames!).

Remember that inheritance, and particularly multiple inheritance,
with its greater complexity, ought to extend the power of object
oriented design to (hopefully) all programmable instances. It
probably doesn't, as there is an infinite amount of complexity out
there and (I guess) a finite number of constructs programmers can
design, but somebody better versed in mathematical theory than I
should attempt a proof in either direction while we programmers
continue with our heuristic approach to problem solving.

>I really hope you, and the other believers, are right.  But I'm not
>convinced.  I don't think the trouble is in bad design, I think it's
>in the nature of things.  The world simply isn't simple, cannot be
>forced into strict hierarchical models ...

I suppose the proof of the pudding ... I contend that (a) you are
correct in believing that hierarchies are not sufficient to represent
all possible conditions, but (b) the problems that programmers face
in (say) 90% of instances have solutions that can be decomposed into
hierarchical representations, and (c) that careful analysis and possi-
bly a fair amount of thumb-sucking guesswork can bring whatever the
initial percentage might be a lot closer to a 100% of all USEFUL
applications.

My fear, and perhaps it is Paul's fear as well, is that because the
object-oriented model is not always immediately obvious, the program-
mer may overlook it and use a (currently) more traditional approach.

I suggest that when a problem that looks as if it does not lend itself
to decomposition into object oriented classes occurs, it should be
submitted for scrutiny on this forum. I believe that the field is
young enough for all of us to have something to learn from the
experiences of others, while at the same time the presenter should
weigh the (possible) embarrassment of having the obvious pointed
out to him/her (and I do not exclude the possibility that the forum
may not succeed in the quest for a suitable representation, to the
embarrassment of the forum in its entirity) against the possibility
that an elegant solution may simplify immediate programming (unlikely
as we all have deadlines to work to), and certainly future maintenance.

----------------------------------------------------------------------
I used to design nuclear reactors and you should see the code that
engineers and scientists come up with. Yuuuck! Spaghetti everywhere.
-------------------------------------------------- (Kenneth L Moore) -
Lucio de Re                        ...uunet!ddsw1!olsa99!proxima!lucio
-------------------------------------------------------- lucio@proxima
                Disclaimer: He must have been joking!

lucio@proxima.UUCP (Lucio de Re) (04/10/90)

In article <1115@targon.UUCP> ruud@targon.UUCP (Ruud Harmsen) writes:
>Perhaps it all depends a lot on the kind of application.  In the ones
>I dealt with most (financial accounting, invoicing, banking etc.) the
>implementation of the data types, say the fields of the structs, are
>exactly what the *user* of the system deals with, viz. enters, browses,
>prints, talks about with his clients, etc.
>
>How could it be useful to *hide* these things, if to the *user* of the
>system, they are the only things that matter?  If you hide the data-items,
>nothing useful remains.

I think there is some confusion here. I presume (and I'm a bit of a
novice at this, so please be tolerant) that, whereas the *user*does*
want to see the fields separately, they are grouped at all times and
are very seldom manipulated out of context. Nobody contends that the
fields are not disjoint, but, in APL parlance, changing one element
of a matrix affects the matrix as a whole. Likewise, changing the
balance on a debtor record affects the entire record. Let's take one
example: Let's say that the internal representation of a debtor
consists of a master file record with debit and credit balances,
current and prior to the present, as well as the (active) history
in the form of transactions. It is arbitrary whether one wants the
occurrence of a new transaction to affect the debtor's balance in
the master file record or not, but it is not arbitrary that the
actual debtor's balance _is_ affected. Good design dictates that
the representation of the debtor in the computer system should be
such that the master file record is tightly coupled to the
transaction records, so that they are viewed by the system as a
single entity. That is how I perceive OOP to function, not as a
limiting factor that forces all objects to be lumped together, but
rather providing a unification glue so that aspects that belong
together are always kept that way.

Sorry to soapbox like this, I hope I haven't muddled the concepts
beyond understanding.

----------------------------------------------------------------------
I used to design nuclear reactors and you should see the code that
engineers and scientists come up with. Yuuuck! Spaghetti everywhere.
-------------------------------------------------- (Kenneth L Moore) -
Lucio de Re                        ...uunet!ddsw1!olsa99!proxima!lucio
-------------------------------------------------------- lucio@proxima
                Disclaimer: He must have been joking!