[comp.lang.c++] C++, Object Design Methodologies and Software Engineering

rmy@beach.cis.ufl.edu (Rasthiyaadu Yakaa) (10/13/90)

There is an issue in oo programming and software engieering
that has been bothering us for a few weeks, and i would like
to hear from knowledgeable/experienced persons on the net who
may have encountered a similar problem ..


On a database project, the work was divided up based on
functionality, e.g. data dictionary, query processor, object
management, among various individuals. Of course, a top down
design was done (blah, blah ..)

Now, each component is to be implemented in an oo manner.
Following, guidelines for object based design (Grady Booch etc)
the necessary system objects and classes were identified and
implemented. Each component obviously was broken down into 
various smaller components and so on.  Following reasonable
software engineering practices.

HOWEVER, we keep finding that when looking at a module or
component from a point of view of its functionality, and in its
entirety, we desire an interface for the entire component or 
sub component as a whole. Consider a software module or component
which comprises of several objects and classes. Clearly, each class
has an interface, which may be used by other classes in the same
module or by external components. When taken collectively, all 
classes in a module have a tremendous amount of interfaces
(messages) but only a small sub set of these need to be visible 
outside the module to other modules (sort of like an IC where 
one only sees the external interfaces and the internal interfaces 
between internal components are hidden - hope this is a
reasonable analogy). This makes it easier to conform to specs.
It is also easier to understand, convenient, and allows
changing internal interfaces. This it would seem is an issue
common to fairly large software projects ..

The question is: would it be wrong to define a class that simply
represents an interface to a layer or module. It would represent
collectively a set of interfaces corresponding to a software
module. e.g. a dictionary-handler class which represents the 
outside interface to a DBMS dictionary or catalog (which are
implemented as classes and objects). If this is OK,
what is the connection between such an interface class and 
system (dictionary) objects and classes.  If this is not OK, 
what are we doing wrong ?


Thanks in advance for all responses

e-mail is appreciated since i may miss articles in this 
newsgroup ..

yaseen

(rmy@beach.cis.ufl.edu OR rmy@trout.cis.ufl.edu)

eberard@bse.com (Edward V. Berard) (10/14/90)

In article <24872@uflorida.cis.ufl.EDU>, rmy@beach.cis.ufl.edu (Rasthiyaadu Yakaa) writes:
> 
> On a database project, the work was divided up based on
> functionality, e.g. data dictionary, query processor, object
> management, among various individuals. Of course, a top down
> design was done (blah, blah ..)

I have quite a number of comments based on real life experience:

	1. While a top-down approach is compatible with an object-oriented
	   approach, dividing the system up "based on functionality" is asking
	   for trouble. This is due to the different types of localization used
	   for functional decomposition approaches and object-oriented approaches.
	   Simply put, functional decomposition approaches localize information
	   around objects, and object-oriented approaches localize information
	   around objects. Objects are not functions and functions are not objects.

	   What happens is this: Very often some information about an object is
	   placed in one functional "chunk" (piece of the system), and other
	   information is located in other functional chunks. Each functional
	   piece of the system is then usually handed off to separate teams. Each
	   team then attempts to identify objects in their specific chunk. As
	   they identify objects, they will attempt to characterize the object
	   they have based on the information at hand, i.e., their specific chunk.
	   Unfortunately, it is highly unlikely that their chunk of the system
	   contains a complete description of the object. Further, it is also
	   very likely that if the "same" object shows up in other chunks, it
	   will appear to have different characteristics  -- and, thus, will be
	   designed differently.

	   All will appear to be going well until integration time. If objects
	   must be transmitted among several parts of the system, those dealing
	   with objects coming from other chunks of the system will notice that
	   others did not characterize the "same" objects in the same way. If
	   you are working with a strongly typed language, some intended interactions
	   may not be allowed. Even if you are dealing with a comparatively weakly
	   typed language, some operations will not be possible due to such things
	   as "missing" operations in object interfaces.

	   As you begin to modify the system objects (and possibly even the system
	   design) to compensate for these problems you will uncover other interesting
	   phenomena. For example, you will find that many objects which should
	   be common to several different parts of the system, will have been
	   implemented many different times in many different ways (so much for
	   reusability). What will be especially irritating to management is the
	   fact that the effort seemed to be going along at an acceptable pace,
	   until integration time -- then a lot of re-work will slow progress
	   to a crawl.

	   The way to avoid all this nonsense is to do two things. First, identify
	   objects at the beginning of the effort. Specifically, survey the entirety
	   of the requirements information to identify candidate objects, and
	   survey the entirety of the requirements information again to gain as
	   complete an understanding of the objects as possible. (This information
	   will be in two general categories: information about each object in
	   isolation, and information about how the objects will interact to affect
	   a solution to the original problem.)

	   The second thing you need to do is an object-oriented decomposition
	   (i.e., _not_ a functional decomposition). Identify the large objects
	   in the system, then identify the objects which make up these objects.
	   Continue the process until you have small, easily-definable objects.
	   This is not unlike designing an automobile. The automobile is a large
	   object composed of several smaller objects, e.g., an engine, a transmission,
	   and an electrical system. These will, in turn, be composed of still
	   smaller objects, e.g., nuts and bolts.

	2. The connotations of what an object is in terms of an "object management
	   system" often conflict with what an object is in the classic object-oriented
	   sense.

> The question is: would it be wrong to define a class that simply
> represents an interface to a layer or module. It would represent
> collectively a set of interfaces corresponding to a software
> module. e.g. a dictionary-handler class which represents the 
> outside interface to a DBMS dictionary or catalog (which are
> implemented as classes and objects). If this is OK,
> what is the connection between such an interface class and 
> system (dictionary) objects and classes.  If this is not OK, 
> what are we doing wrong ?

Here, you are touching on object-oriented development in the large (OODIL).
The problem you have encountered is not unlike dealing with a very large object,
e.g., an automobile. When I drive a car, I am oblivious to the smaller components
which make up the car, e.g., I do not see the engine. Even if I raise the
hood (bonnet), and look at the engine, I cannot see all the objects which
are internal to the engine.

There are several key points:

	1. By viewing a system as simply a large object, and asking what should
	   be in the interface to this object, with no regard to how the object
	   is implemented internally, I avoid the problem of attempting to put
	   a coat of "object-oriented paint" on something which is clearly not
	   an object, i.e., a functionally-designed component.

	2. While classes, metaclasses, and instances of each are sufficient for
	   small, textbook problems, they are not entirely satisfactory for larger
	   systems. For example, consider the engine of an automobile. It presents
	   one interface to the transmission of the automobile, another (completely
	   different) interface to the fuel system, and yet another different
	   interface to the cooling system. In effect, the engine is a very large
	   object with what appears to be several completely disjoint interfaces.
	   (For a better picture, ask yourself if your university or company is
	   an object. Of course it is. Now ask yourself if there is only one interface
	   to this very large object. No, there are many.)

	   There will be other large objects which seem to represent capabilities
	   rather than stand-alone applications. For example, consider a windowing
	   system, e.g., X Windows. rather than being a very physically cohesive
	   system of objects, it appears to be more like a library of objects
	   whose purpose is to support a very large object-oriented concept, i.e.,
	   windows. To construct a dialog window, for example, you will need quite
	   a number of objects, e.g., radio buttons, check boxes, static and editable
	   text, icons, pictures, scroll bars, etc.

	   The first large object I mentioned (i.e., the one with a high degree
	   of physical cohesion and the possibility of multiple disjoint interfaces)
	   is what I refer to as a "system of interacting objects." You might
	   say that each object-oriented application you create is just such a beast.
	   The second large object (i.e., the one which has a high degree of logical
	   cohesion (the objects are all there to support a larger, object-oriented
	   concept) and is granular (you can select individual components)) is
	   what I and others refer to as a "subsystem."

	3. In designing any system, but especially a large system, it is crucial
	   to have well-defined interfaces among the components. These interfaces
	   should be defined early, and should remain fixed. Otherwise, it is
	   difficult to "hand off" pieces of the system to different teams and
	   expect them to smoothly integrate the pieces.

				-- Ed


----------------------------------------------------------------------------
Edward V. Berard                                | Phone: (301) 353-9652
Berard Software Engineering, Inc.               | FAX:   (301) 353-9272
18620 Mateney Road                              | E-Mail: eberard@bse.com
Germantown, Maryland 20874                      | 
----------------------------------------------------------------------------

rfg@NCD.COM (Ron Guilmette) (11/03/90)

In article <24872@uflorida.cis.ufl.EDU> rmy@beach.cis.ufl.edu () writes:
>
>The question is: would it be wrong to define a class that simply
>represents an interface to a layer or module...

A related question was discussed awhile back, i.e. the question of
why C++ can't be a bit more like Ada.

Specifically, I think that the kind of thing that you are perhaps
looking for might be a header file that looks kinda like this:

	class car;

		car::car (int cost);
		car::~car ();
		heading car::turn (direction d);
		next_date car::change_oil (station s);

In other words, a sort of *abbreviated* description of the interface to
a class which is provided to some user of the class.

Allowing abbreviated descriptions like this (which leave out information
about data members) could potentially cut down dramatically on the
amount of recompilations we have to do over the lifetime of a class.

I hope that the ANSI committee will consider this possible solution
for an important (and as yet unresolved) problem.

I (for one) can't see any problems with the solution proposed here.
It certainly doesn't break any existing syntax rules, and if (for
instance) `car::turn' was never really defined for the class car, then
you most certainly would find out about *that* problem at link time.

One minor problem with this proposed `feature' is that `car::change_oil'
might be private to the class car (in which case the user of the header
file should not be allowed to use `car::change_oil'.

That problem could easily be solved by providing three different name
manglings for member functions.  One (and only one) of these would be
selected and used depending upon the access category (i.e. public,
private, or protected) of the member function where its *true*
declarations (within the class body) occurs.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

vaughan@mcc.com (Paul Vaughan) (11/04/90)

   From: rfg@NCD.COM (Ron Guilmette)

   A related question was discussed awhile back, i.e. the question of
   why C++ can't be a bit more like Ada.

   Specifically, I think that the kind of thing that you are perhaps
   looking for might be a header file that looks kinda like this:

	   class car;

		   car::car (int cost);
		   car::~car ();
		   heading car::turn (direction d);
		   next_date car::change_oil (station s);

   In other words, a sort of *abbreviated* description of the interface to
   a class which is provided to some user of the class.

   Allowing abbreviated descriptions like this (which leave out information
   about data members) could potentially cut down dramatically on the
   amount of recompilations we have to do over the lifetime of a class.

   I hope that the ANSI committee will consider this possible solution
   for an important (and as yet unresolved) problem.
 . . .

I've suggested this sort of thing for different reasons before, but it
hasn't sparked any enthusiasm.  I proposed that this sort of
declaration would help to solve severe modularity problems.  One thing
that became clear to me was that a very complete declaration is
required.  For instance if the real class definition said

class car : public virtual vehicle {
public:	
  virtual Heading heading() const;
//  . . .
};

You're going to have to declare

class car : public virtual vehicle;
public virtual Heading car::heading() const;

Anyway, I'd lend some support if anyone wants to start a ball rolling
in the ANSI committee.

 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan

mjy@charis.UUCP (Michael J. Young) (11/04/90)

In article <2390@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes:
>Specifically, I think that the kind of thing that you are perhaps
>looking for might be a header file that looks kinda like this:
>
>	class car;
>
>		car::car (int cost);
>		car::~car ();
>		heading car::turn (direction d);
>		next_date car::change_oil (station s);
>
>In other words, a sort of *abbreviated* description of the interface to
>a class which is provided to some user of the class.

That might be fine, as long as you don't ever try to use class car.  But
what happens when you try to create an instance of car, or use it as a
member of another class?  The compiler has no way of determining sizeof(car).
-- 
Mike Young
UUCP: lectroid.sw.stratus.com!charis!mjy                 Tel: +1 508 568 1572

pcg@cs.aber.ac.uk (Piercarlo Grandi) (11/05/90)

On 3 Nov 90 04:15:50 GMT, rfg@NCD.COM (Ron Guilmette) said:

rfg> Specifically, I think that the kind of thing that you are perhaps
rfg> looking for might be a header file that looks kinda like this:

rfg> 	class car;
rfg> 	/* void */	car::car (int cost);
rfg> 	/* void */	car::~car ();
rfg> 	heading 	car::turn (direction d);
rfg> 	next_date	car::change_oil (station s);

You can well do this currently with a little sleight of hand using
inheritance. The key to understanding thsi is that your proposal only
works if you manipulate only pointers or references to car objects, and
not for all operations (all that imply dereferencing are out).

Otherwise you have to pull in the full declaration.

rfg> In other words, a sort of *abbreviated* description of the interface to
rfg> a class which is provided to some user of the class.

Also, if my modest proposal for an alternative to member functions
(including destructors and constructors) were allowed, you would write:

 	class car;

 	void		operator { (car &self,int cost); // constructor
 	void		operator } (car &self); // destructor
 	heading		turn (car &self,direction d);
 	next_date	change_oil (car &self,station s);

Then you could write (references are implemented with pointers):

	class car;			typedef struct char char;
	car *c1 = new car(8999);	car *c1 = (car *) malloc(sizeof (car));
					__op_lbrace__car__int(c1,8999);
	c1->turn(90);			__turn__car__direction(c1,90);
	change_oil(*c1,esso);		__change_oil__car__station(c1,esso);
	delete c1;			__op_rbrace__car(c1);
					free((void *) c1);

But please note the problem in the first line; we do not know the value
of sizeof (car). The description is too abbreviated for that:
	
rfg> Allowing abbreviated descriptions like this (which leave out
rfg> information about data members) could potentially cut down
rfg> dramatically on the amount of recompilations we have to do over the
rfg> lifetime of a class.

Precisely -- but this is only because you are going to use pointers for
access. Just like opaque types in Modula 2. And note that without
knowing the size of a car object you cannot even allocate them.

I know of some languages in which to get around this problem and
minimize recompilation and favour binary compatibility across
struct declaration revisions you can say something like

	class[100] car; // car is not more than 100 bytes long
	class[100] { char *make; long unsigned serialno; ... } car;

This has the effect of wasting some space, but avoiding recompilation of
code if one only adds news fields, as long as the total size does not
grow beyond 100 bytes.

Naturally you can go the Ada way and assume that definitions are
precompiled, and by saying just 'import car;' the definition of car is
imported and you don't need to repeat it. But this is not C++, which
assumes conventional compiler technology.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

scp@acl.lanl.gov (Stephen C. Pope) (11/05/90)

on 3 Nov 90 20:01:59 GMT,
vaughan@mcc.com (Paul Vaughan) said:

Paul>    From: rfg@NCD.COM (Ron Guilmette)

Ron>    A related question was discussed awhile back, i.e. the question of
Ron>    why C++ can't be a bit more like Ada.

[...]

Paul> I've suggested this sort of thing for different reasons before, but it
Paul> hasn't sparked any enthusiasm.  I proposed that this sort of
Paul> declaration would help to solve severe modularity problems.  One thing
Paul> that became clear to me was that a very complete declaration is
Paul> required.

There are other problems.  Like inlined methods, which can't be used
in this way since the compiler must know about the implementation
(data members) of the class to generate inline code.

stephen pope
advanced computing lab, lanl
scp@acl.lanl.gov

bp@porpoise.cis.ufl.edu (Brian Pane) (11/05/90)

In article <12823@cadillac.CAD.MCC.COM>, vaughan@mcc.com (Paul Vaughan) writes:
|
|   From: rfg@NCD.COM (Ron Guilmette)
|
|   A related question was discussed awhile back, i.e. the question of
|   why C++ can't be a bit more like Ada.
    ^^^^^^^^^^^ aaargh! ^^^^^^^^^^^^^^^^
|
|   Specifically, I think that the kind of thing that you are perhaps
|   looking for might be a header file that looks kinda like this:
|
|	   class car;
|
|		   car::car (int cost);
|		   car::~car ();
|		   heading car::turn (direction d);
|		   next_date car::change_oil (station s);
|
|   In other words, a sort of *abbreviated* description of the interface to
|   a class which is provided to some user of the class.
|
|   Allowing abbreviated descriptions like this (which leave out information
|   about data members) could potentially cut down dramatically on the
|   amount of recompilations we have to do over the lifetime of a class.
|
|   I hope that the ANSI committee will consider this possible solution
|   for an important (and as yet unresolved) problem.
| . . .
|
|I've suggested this sort of thing for different reasons before, but it
|hasn't sparked any enthusiasm.  I proposed that this sort of
|declaration would help to solve severe modularity problems.

That sort of abbreviated class description seems like a very good approach
for leaf classes.  However, you still need the full declaration in order
to derive child classes.  IMHO, such a syntax would only make it harder to
get any work done; maintaining consistency among the full class declaration,
the abbreviated declaration, and the member implementations would require
extra software engineering work.

Actually, I suppose you could achieve the same result by writing a filter
that read the class declaration and wrote out a version containing only
the function prototypes, not the data members.  This filtered file could be
included by files which needed only to call the class's member functions, and
the original could be included by files implementing derived functions.  You
could maintain declaration coherency by writing a makefile rule which regenerated
the filtered file when the original header file was modified, perhaps.  How
does this approach sound?


| Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
| Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan

-------------------------------------------------------------------------
Brian_Pane::~Brian_Pane() { "bp@swamp.cis.ufl.edu"; }
// University of Florida  Department of Computer Science  Class of 1991
-------------------------------------------------------------------------

vaughan@mcc.com (Paul Vaughan) (11/06/90)

   From: scp@acl.lanl.gov (Stephen C. Pope)
   vaughan@mcc.com (Paul Vaughan) said:

   Paul>    From: rfg@NCD.COM (Ron Guilmette)

   Ron>    A related question was discussed awhile back, i.e. the question of
   Ron>    why C++ can't be a bit more like Ada.

   [...]

   Paul> I've suggested this sort of thing for different reasons before, but it
   Paul> hasn't sparked any enthusiasm.  I proposed that this sort of
   Paul> declaration would help to solve severe modularity problems.  One thing
   Paul> that became clear to me was that a very complete declaration is
   Paul> required.

   There are other problems.  Like inlined methods, which can't be used
   in this way since the compiler must know about the implementation
   (data members) of the class to generate inline code.

   stephen pope
   advanced computing lab, lanl
   scp@acl.lanl.gov

I may be missing something here, but I don't really see inline methods
as being a problem, especially when using this as a technique to
enhance modularity, rather than data hiding. Inline methods can only
be inlined in modules where the inline method definition is present.
In my proposal, the class definition must be available in order to
define any method of that class. So if you've got the class
definition, you can define inline methods and they can be inlined.  If
you haven't got the class definition, you can't have any inline method
definitions, and obviously, they can't be inlined.  This really isn't
any different from the current situation.

So, it comes down to a tradeoff between modularity and speed.  The
more stuff you dump into a module, the more potential for inlining and
speed you get.  Having the ability to declare methods (and data
members, BTW) in this way gives the originator or user of a class the
flexibility to make this tradeoff.

The biggest problem that I see is that it transfers even more duties
to the linking stage.  I believe it can be handled there, but the
error messages (for instance, about using a method declared
independently as public but listed as private in the class definition)
aren't going to be as precise as we've come to expect.  Also, I don't
know much about how well it can be supported by a compiler that first
produces C code.

 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan