[comp.lang.c++] Objectifying incoming messages?

davidm@cimshop.UUCP (David Masterson) (07/27/89)

I'm working in an area doing C++ development that has several processes
communicating with one another.  The messages shipped back and forth tend to
be asynchronous, so a receiving process cannot know what the message is or who
it came from until it looks at the message data.  My question is this:

What is the appropriate method of taking a generic object (Message) and
turning it into an object of the type that is embedded in the message (say,
Employee)?  Should this be done through virtual functions and subclassing of
the Message object?  Or is it more appropriate to have the Message object look
at itself and construct an object of the appropriate type (perhaps some sort
of array of pointers to object constructors)?

The fundamental question hinges around the lack of context of the incoming
message.  Therefore, the message user must look at the data in order to
determine what to coerce (definition?) the message into.  Is this appropriate
or is there a better way?

aTdHvAaNnKcSe

David Masterson
uunet!cimshop!davidm		or		DMasterson@cup.portal.com

dog@cbnewsl.ATT.COM (edward.n.schiebel) (07/27/89)

From article <444@cimshop.UUCP>, by davidm@cimshop.UUCP (David Masterson):
> The fundamental question hinges around the lack of context of the incoming
> message.  Therefore, the message user must look at the data in order to
> determine what to coerce (definition?) the message into.  Is this appropriate
> or is there a better way?
> 
The same problem arises reading objects from disk.  Until you look at
the data, you don't know what class of object to instance so it
can read itself.  It seems to me, this is one place you still
need a header/type field and a switch statement.

	Ed Schiebel
	AT&T Bell Labs
	att!vilya!dog
	

jaa@hutcs.hut.fi (Jari Alasuvanto) (07/28/89)

In article <1273@cbnewsl.ATT.COM> dog@cbnewsl.ATT.COM (edward.n.schiebel) writes:
>The same problem arises reading objects from disk.  Until you look at
>the data, you don't know what class of object to instance so it
>can read itself.  It seems to me, this is one place you still
>need a header/type field and a switch statement.
>
Or you can hide peeking at your data inside a library code as done in OOPS
(NIH library). There you can read objects from disk and either give an 
expected class as an argument (the library code verifies it) or not give it.
There are some problems in that, however:
	1) Classes are not objects and there is no common base class in 
	   standard C++. A work-around is needed (as done in OOPS) 
	1) It may be hard to know later on what actually came - and you 
	   may have to do a case later on

In our work we use the OOPS methods for writing the C++ objects into files
to implement a simple persistent workspace as well as to exchange them via
email - works fine.  We need to tag the mail messages containing C++/NIH
objects in order to be able to read also plain email.



Jari Alasuvanto	
Lab. of Information Proc. Science, Helsinki Univ. of Techology, Finland
Internet: 	jaa@hutcs.hut.fi 	Bitnet:	jaa%finhutcs.bitnet	
tel: 		+358-0-451 3236 	fax:  +358-0-465 077

hardin@hpindda.HP.COM (John Hardin) (07/28/89)

dog@cbnewsl.ATT.COM (edward.n.schiebel) writes:

> From article <444@cimshop.UUCP>, by davidm@cimshop.UUCP (David Masterson):
> > The fundamental question hinges around the lack of context of the incoming
> > message.  Therefore, the message user must look at the data in order to
> > determine what to coerce (definition?) the message into.  Is this appropriate
> > or is there a better way?
> > 
> The same problem arises reading objects from disk.  Until you look at
> the data, you don't know what class of object to instance so it
> can read itself.  It seems to me, this is one place you still
> need a header/type field and a switch statement.
>----------

Reading objects from disk is exactly the subject of one of the demo
programs that comes with Borland's Turbo Pascal 5.5 (with object oriented
extentions).  I haven't looked at it in detail, but I believe they
derive classes from their stream class and then the derived classes
are somehow "registered" with the stream class.  A get from the stream
then first reads the next object type and calls the "get" method of that
class.  This embeds the switch within the steam's "get" method.
BTW, I believe the demo is called CARDFILE.  Has anyone else looked at
this?  If you have, what do you think of it?

John Hardin
hardin%hpindgh@hplabs.hp.com
--------

davidm@cimshop.UUCP (David Masterson) (07/29/89)

>The same problem arises reading objects from disk.  Until you look at
>the data, you don't know what class of object to instance so it
>can read itself.  It seems to me, this is one place you still
>need a header/type field and a switch statement.
>
Agreed.  In fact, my messages deal with a database server to get information
from the database.  I think a function pointer table is a cleaner way of
implementing this in C, but can you implement an object construction pointer
table in C++?  By the way, would the object/function that reads from the
external entity be a separate thing or should it be a base class that looks at
itself and casts to the appropriate derived class (can that even be done?!?)?
Consider:

1.  All external objects have a common header in order to determine what type
of object they are.

2.  A generic object would get the external object and decide what to do with
it.

3.  Once it decides, it makes (casts) itself a specific object of the
appropriate type.

What problems are there in this method?  I think I see a few, but I'm curious
about what others think?  (I'm looking for ammunition ;-)

David Masterson
uunet!cimshop!davidm

dog@cbnewsl.ATT.COM (edward.n.schiebel) (07/29/89)

> ... By the way, would the object/function that reads from the
> external entity be a separate thing or should it be a base class that looks at
> itself and casts to the appropriate derived class (can that even be done?!?)?
As a base class, I don't think so This base class doesn't have enough 
memory allocated to hold derived classes.  You need an object
that knows about all class types it may see, read the header, then
create one on the free store so it may read itself.

	Ed Schiebel
	AT&T Bell Labs
	att!vilya!dog

raph@tigger.planet.bt.co.uk (Raphael Mankin) (08/01/89)

davidm@cimshop.UUCP (David Masterson) writes:

>I'm working in an area doing C++ development that has several processes
>communicating with one another.  The messages shipped back and forth tend to
>be asynchronous, so a receiving process cannot know what the message is or who
>it came from until it looks at the message data.  My question is this:

>What is the appropriate method of taking a generic object (Message) and
>turning it into an object of the type that is embedded in the message (say,
>Employee)?  Should this be done through virtual functions and subclassing of
>the Message object?  Or is it more appropriate to have the Message object look
>at itself and construct an object of the appropriate type (perhaps some sort
>of array of pointers to object constructors)?

>The fundamental question hinges around the lack of context of the incoming
>message.  Therefore, the message user must look at the data in order to
>determine what to coerce (definition?) the message into.  Is this appropriate
>or is there a better way?

This is a fundamental problem of strongly typed languages. Ada
pretends that it is not there but really has a worse problem than C++.

Suppose you have a file with many variant records (unions) in it. You
cannot allocate buffer space for the record until you know what type it
is, and you cannot know what type it is until you have allocated space
and read the record in. Ada does not permit you to read into a char*
or (void*) and then cast the data when you have examined the
non-variant header.

The only way I have found of doing this in C++ is to go back to the
old fashioned way of using a switch with a different cast in each
branch. This is messy verbose and generally nasty. The only
satisfactory solution is to allow types to be first-class objects so
that we could have 'variable' casts. e.g.
	Type t = typelist[discriminant];

	return (t)record_area;

The run-time costs of this are horrible and I am not really suggesting
it as a solution in C++. 

Does anyone have a sensible solution?


Raphael Mankin			raph@planet.bt.co.uk

davidm@cimshop.UUCP (David Masterson) (08/03/89)

In a previous message,

	I talked about the mechanics of changing general messages into
specific objects in order to turn a bit-stream (say, from a network) into a
known object.  Most seem to feel that it is the responsibility of an object to
write itself out and read itself back in (construct itself).  Obviously, the
message processor (disk reader) has to get clues from the bit-stream as to
what type of object is coming in and, therefore, switch to the appropriate
object construction mechanism.  The problem is that this leads to
inefficiencies because of the need to move bits around more than once in order
to get it into a usable form (move bits to a buffer, determine object type,
construct object type, and move bits into constructed area).  An implication
has been that there are well-known ways of dealing with such things in C that
C++ does not handle as well.

	Perhaps, there is a need to define what types of data are capable of
being written out of a program and (by implication) what types of data can be
read into a program.  Since an object is represented by its data and methods
and methods are of no use outside a process (cannot be written out), maybe it
is better to not define external data as an object (at least in C++).  An
object can be constructed to contain this external data, but the data itself
is not an object (therefore, no virtual function pointers, esoteric object
types, etc.).  Therefore, anything passing between C++ programs (via shared
memory, messages, disk files, databases, etc.) can only be data structures.
Many people would say that this is obvious, but others might not.  What I'm
looking for is to see if people agree with this formalization of definition
and its implications for understanding the problem.

	Now that I've made that point, let me jump back into message passing
in a multitasking system and my main question.  Let's assume a situation in
order to clarify the problem.  In a system where a number of processes may be
communicating with each other via interprocess messages, a single process can
not make an assumption as to what message is coming in.  Therefore, the
message processor will have to read the message, look for clues as to what
type of message it is, and have it processed in the appropriate manner.  In a
C++ scenario, an object might be working on constructing itself by sending a
message to a data server and awaiting a reply.  Due to the nature of the
process, several things may be going on at the same time (like window
management), so its not appropriate for the process to hang waiting for the
reply to the data request (it might never arrive anyway).

	In C, this would be handled by having the request procedure issue the
request and return to the message processing procedure.  The reply would
eventually come back and the message procedure would give it to the reply
procedure.  However, in C++, its not a good idea for a constructor function to
return before the reply (and, therefore, the data) has been received.  Thus
the C++ program is hung.  So, my question is what is the appropriate C++
fashion for constructing an object when the object construction may involve
sending a message and getting a reply that you don't want to wait for?

	Some might suggest treating the message processor as an object and
having the constructor function call this processor when it needs a reply
message in order to complete its work.  I think this leads to a recursive
problem in that potentially many messages may have come in that led to many
objects being constructed that led to many outstanding requests.  This sounds
like a potential stack overflow problem.  If your trying to create a server of
some sort, this is not a stateless way of doing things.  There must be a
better way!

	I see I'm beginning to ramble, so I'll hold it here and see what sort
of "brilliant" ideas come in (I hope, I hope ;-).  Have a good one!

David Masterson
uunet!cimshop!davidm		or		DMasterson@cup.portal.com

eberard@ajpo.sei.cmu.edu (Edward Berard) (08/03/89)

In article <452@cimshop.UUCP>, davidm@cimshop.UUCP (David Masterson) writes:
> In a previous message,
> 
> I talked about the mechanics of changing general messages into specific
> objects in order to turn a bit-stream (say, from a network) into a known
> object.  Most seem to feel that it is the responsibility of an object to
> write itself out and read itself back in (construct itself).  Obviously, the
> message processor (disk reader) has to get clues from the bit-stream as to
> what type of object is coming in and, therefore, switch to the appropriate
> object construction mechanism.  The problem is that this leads to
> inefficiencies... [stuff deleted]
> 
There are other potential problems as well, in particular the
probability of undesirable object coupling. Before I define "object
coupling," I must first define the concept of "primitive" objects and
classes. 

	A primitive object or class is one which is furnished by the
	environment, e.g., an object or class which is defined in the
	definition of the programming language. Primitive objects and
	classes have two main uses: the construction of non-primitive
	(i.e., user-defined) objects and classes, and a means of
	low-level communication among objects, and with the outside
	world.

Object coupling occurs when a class refers to some _specific_
non-primitive object which is an instance of a class other than
itself, or to some _specific_, _different_ non-primitive class. There
are two types of object coupling: white-box object coupling and
black-box object coupling.

Black-box object coupling occurs when the class treats the specific,
different class as a black box, i.e., it makes no direct references to
the state of the class, or to the states of objects which are
instances of the class. For example, consider a "list of names" class.
Assume that this class is treated like any other list, e.g., one can
add names to the list, delete names from the list, and ask how many
names are contained in the list. However, there are no specific
references to the state of an item contained in the list (i.e., a
name). Assuming that this class refers to a specific "name" class, and
that the "list of names" class was derived from a more generic "list
class," then we say that the "list of names" class black-box couples
the list class with the name class.

White-box coupling occurs when a class makes specific references to
the state of an object which is an instance of a specific different
class, or takes explicit advantage of the knowledge of the underlying
implementation of a different class. For example, using the "list of
names" class from the above example, suppose that we required that the
list be an ordered list. If we accomplished this by, for example,
letting the list class know that the underlying implementation of a
name object was an ASCII string, then the original list class is now
white-box coupled with the name class.

The most common examples of white-box coupling are when people place
input and output operations in the interface to a class. For example,
consider a counter class, i.e., instances of this class are used to
retain counts of some phenomenon. Suppose the operations in the
interface for the counter class are: zero (the counter), increment
(the value of the counter by one), decrement (the value of the counter
by one), and display (the value of the counter). Concerning the
"dislay" operation: the question is "display the value of the counter
_where_?" Because the "display" operation is required to change the
state of an object which is not an instance of the counter class
(i.e., it must change the state of some "output object") we say that
the counter object is white-box coupled with the "output object."

Object coupling has two undesirable side effects:

	- It reduces the reusability of objects and classes. For
	  example, suppose another application required a counter
	  class, but did not require the specific output object to
	  which the above counter class was coupled. A generic list
	  class, for example, can be combined with many other classes. 

	- It reduces the reliability of systems which contain coupled
	  objects by providing a mechanism for "ripple effects," i.e.,
	  it allows for a change in one part of a system to
	  potentially result in unwanted erroneous changes in other
	  parts of the same system. For example, suppose the
	  operations in the interface to the output object change.
	  Then all the methods in other classes which make specific
	  reference to these operations may potentially have to be
	  changed. 

Please note that object coupling is necessary for object-oriented
applications to accomplish work. What we want to avoid is unnecessary
object coupling. This brings up a number of systems questions, e.g.,
when should objects be coupled, and what collections of coupled
objects (i.e., systems of objects) are both highly reusable, and
reasonably reliable?

[By the way, there are specific techniques for removing undesirable
object coupling. There are also specific guidelines for both avoiding
unnecessary object coupling, and in the construction of reusable
systems of objects.]

> 	Perhaps, there is a need to define what types of data are capable of
> being written out of a program and (by implication) what types of data can be
> read into a program.  Since an object is represented by its data and methods
			^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> and methods are of no use outside a process (cannot be written out), maybe it
> is better to not define external data as an object (at least in C++).

Objects encapsulate:

	- knowledge of state information
	- operations (and their corresponding methods)
	- other objects, i.e., there can be:
		- homogeneous composite objects
		- heterogeneous composite objects
	- exceptions
	- constants
	- concepts

If an object is a composite object, then you have your original
problem defined recursively. Specifically, if you have large objects
which are built on smaller objects, which are themselves built on
still smaller objects, you have a very interesting "objectification"
problem. 

On another matter: suppose a distributed application is, for example,
constructed using a different object-oriented programming language at
each node. The language at node X supports exceptions as part of a
class definition.  The language at node Y supports constants as part
of a class definition. The language at node Z supports assertions as
part of method definition. Suppose further that the language used at
node W does not directly support some, or all, of these items. This
obviously will have some impact on "objectification" strategies.

[Before you say, "no one should ever attempt such a thing," consider
this. Even if a single object-oriented programming language is used
in a distributed application, there are still the issues of:

	- interfacing this system to other similar systems
	- enhancements to the original object-oriented language which
	  may require a phased introduction of new software on the
	  network 
	- a decision to change the implementation language (or a desire
	  to have the flexibility to make that decision) before all
	  nodes are brought on line. This will probably only be an
	  issue in systems which take years to develop and deploy.]

There are quite a number of other issues and solutions I would like to
discuss, but I have already taken enough space.

				-- Ed Berard
				   (301) 353-9652

davidm@cimshop.UUCP (David Masterson) (08/05/89)

In message <533@ajpo.sei.cmu.edu>, eberard@ajpo.sei.cmu.edu writes:
>There are quite a number of other issues and solutions I would like to
>discuss, but I have already taken enough space.
>
Discuss more... Discuss more...  I agree with you that there are quite a
number of interesting issues here that need to be discussed in order for the
object-oriented paradigm that C++ uses to move out of the single process type
of development into a more multi-tasking environment (yes, you can write
multiple cooperating tasks in C++, but does C++ effectively define how they
will interface?).

>[stuff deleted]				 There
>are two types of object coupling: white-box object coupling and
>black-box object coupling.
>
Your definitions and examples here threw me some.  I think I understand what
you're talking about, but never before put names to it, so I'm having trouble
matching up what you're saying with what I think I know.  I'm interested in
how coupling might go too far and where the cross-over point from black to
white is (is it really that black and white?).

>       Because the "display" operation is required to change the
>state of an object which is not an instance of the counter class
>(i.e., it must change the state of some "output object") we say that
>the counter object is white-box coupled with the "output object."
>
I don't understand "output object" or "display".  Isn't it more appropriately
called asking the "counter" object for its current value?  Is that black or
white coupling (one object requests information from another)?

>[By the way, there are specific techniques for removing undesirable
>object coupling. There are also specific guidelines for both avoiding
>unnecessary object coupling, and in the construction of reusable
>systems of objects.]
>
Where?  I think I want to look this up.

>Objects encapsulate:
>
>	- knowledge of state information
>	- operations (and their corresponding methods)
>	- other objects, i.e., there can be:
>		- homogeneous composite objects
>		- heterogeneous composite objects
>	- exceptions
>	- constants
>	- concepts
>
Agreed about composite objects (a point I've been trying to make around here).
Could you define "exceptions, constants, and concepts" more?  Particularly as
to transferring this information from process to process?  A similar question:
should processes be thought of as objects and what implications does that
have?

David Masterson					(415) 691-6311
uunet!cimshop!davidm		or		DMasterson@cup.portal.com

eberard@ajpo.sei.cmu.edu (Edward Berard) (08/05/89)

In article <455@cimshop.UUCP>, davidm@cimshop.UUCP (David Masterson) writes:
> In message <533@ajpo.sei.cmu.edu>, eberard@ajpo.sei.cmu.edu writes:
> >There are quite a number of other issues and solutions I would like to
> >discuss, but I have already taken enough space.
> >
> Discuss more... Discuss more...  I agree with you that there are quite a
> number of interesting issues here that need to be discussed in order for the
> object-oriented paradigm that C++ uses to move out of the single process type
> of development into a more multi-tasking environment (yes, you can write
> multiple cooperating tasks in C++, but does C++ effectively define how they
> will interface?).

First, I would like to give you some background. Since 1981, I have
been researching, applying, and writing about the object-oriented
paradigm. In addition to applying object-oriented technology on
approximately one million lines worth of source code in house, I have
consulted on object-oriented techniques with a substantial number of
clients. I also developed (and still give) a number of courses on
object-oriented technology.

My clients and students use many different programming languages, and
develop a wide range of applications (everything from embedded,
real-time to classic business information systems). Since I never had
the "luxury" of a single programming language or application domain, I
had to extract "the essence" of object-oriented technology, i.e., the
_concepts_ as opposed to the _implementations_.

My clients have always wanted me to take ideas and words and make them
real. My job is to make the technology systematic, to clearly define
terms and processes, and to do this on many different levels of
abstraction. What you see discussed here is the result of a great deal
of research and "hard knocks." I'm still learning.

> >[stuff deleted]				 There
> >are two types of object coupling: white-box object coupling and
> >black-box object coupling.
> >
> Your definitions and examples here threw me some.  I think I understand what
> you're talking about, but never before put names to it, so I'm having trouble
> matching up what you're saying with what I think I know.  I'm interested in
> how coupling might go too far and where the cross-over point from black to
> white is (is it really that black and white?).
> 

One way to decrease both the resuability and reliability of an object
or class is through "object coupling." When Larry Constantine was
researching structured design he noticed that (functional) modules
could be "coupled," but that all coupling was not the same. I (and
others) noticed the same thing about objects. In attempting to
quantify object coupling (really "class coupling") I observed:

	- There was a type of object coupling which had a strongly
	  negative impact on software reusability, but a fairly
	  small impact on software reliability. This was because one
	  class might know about another specific class by name, but
	  would know little else about the other class, i.e., it
	  treated this other specific class as a "black-box."

	- There was another type of object coupling which not only had
	  a strongly negative impact on software reusability, but also
	  had a very detrimental effect on software reliability. This
	  other type of object coupling made maintenance and
	  modification of object-oriented systems difficult and error
	  prone. The main reason for this was because one class made
	  use of some very specific characteristics of one, or more,
	  different classes. Unlike classes exhibiting the first type
	  of object coupling, classes exhibiting this type of object
	  coupling "knew" something about another class, and took
	  explicit advantage of what it "knew." In effect, the other
	  class was a "white-box."

[System Axiom: The more any part of a system "knows" about any other
part of the same system, the more tightly coupled those two parts of
the system become. Further, tight coupling both reduces the
reusability of at least one of the components, and makes the overall
system both harder to modify, and more vulnerable to negative side
effects when the system is changed in any way. (See Herbert Simon's
discussion of "nearly decomposable systems" in his book: "Sciences of
the Artificial.")]

The first type of coupling (what I now call "black box object
coupling") was less serious than the second type of coupling (what I
now call "white box object coupling"). I also noticed:

	- That black box object coupling was relatively easy to
	  remove through a technique I refer to as "data abstraction
	  decoupling" (although it is probably more accurately
	  described as "object abstraction decoupling").

	- That white box object coupling takes more work, and more
	  skill to remove. The three techniques for removing black box
	  object coupling are: "selector decoupling," "constructor
	  decoupling," and "iterator decoupling."

	- That while there are varying degrees of both white box and
	  black box object coupling there does not appear to be, as
	  yet, any reason to identify any further refinement of
	  either form of object coupling.


> >       Because the "display" operation is required to change the
> >state of an object which is not an instance of the counter class
> >(i.e., it must change the state of some "output object") we say that
> >the counter object is white-box coupled with the "output object."
> >
> I don't understand "output object" or "display".  Isn't it more appropriately
> called asking the "counter" object for its current value?  Is that black or
> white coupling (one object requests information from another)?

Two key points to remember when describing any object or class are:
describe the object or class from an _external_ viewpoint only, and
describe the characteristics of the object or class _in_ _isolation_,
i.e., independent of any other object or class, and definitely
independent of any specific application. This favorably increases the
probability that the object or class will be constructed in such a way
as to make it more reusable.

Assume that "output object" is a simple object with one operation in
its interface, i.e., "put." "Put" accepts strings of characters as its
input, and its method directs these strings of characters to the
"outside world." (In Smalltalk parlance: messages sent to the output
object consist of a selector (i.e., "put") and a "string object.") If
you like concrete examples, imagine that "output object" is a one-line
LCD display, or a speech synthesizer, or even a very dumb ASCII
terminal screen.

"Display" is a composite operation whose function is to take the
present value of the counter object, and to give it to "output object"
for "'display' to the outside world." A "composite operation" is an
operation which is composed of two, or more, primitive operations. A
"primitive operation" is an operation which cannot be accomplished
simply, reliably, and efficiently without knowledge of the underlying
implementation of the object or class in which it is encapsulated.

*** In the next three paragraphs, I know I will be defining some terms ***
*** in a manner which is inconsistent with their usage in some         ***
*** programming languages. I am specifically thinking about Smalltalk  ***
*** and C++. Please be kind. If my definitions offend you, focus on    ***
*** the concepts, not the terminology. Please :-) 		       ***

A "selector" operation is an operation which returns state information
about the object in which it is encapsulated, but cannot, by
definition, change the state of that object. Examples of selector
operations include: sine functions, end-of-file functions, and an
operation which tells you how may items are contained in a list.

A "constructor" operation is an operation which can (and often does)
change the state of the object in which it is encapsulated. Examples
of constructor operations include: an operation which adds an item to
a list, an operation which increments a counter, and an operation
which causes characters to appear on an output device.

An "iterator" operation is only applicable to homogeneous composite
objects. (A homogeneous composite object is a composite objects whose
component objects are all (conceptually) instances of the same class.)
An iterator capability is a simple concept, it provides the user with
the capability to "iterate over" ("loop through") the composite
object. 

Although a user of a homogeneous composite object may know
conceptually that the object is a composite object, he or she is
unaware of the underlying implementation of the object, e.g., it could
be a linked list, an array, a record structure, or something entirely
different. Further, the designer of the object or class cannot
possibly imagine all the possible things users might want to do as
they iterate over the object.

Therefore, iterators are actually two-part operations. The designer of
the object or class obviously knows about the underling implementation
of the object or class, and thus supplies the capability to visit one,
or more, nodes of the object in succession. The user of the iterator
capability obviously knows what he or she wants done at each node, and
hence they supply the appropriate operation(s).

(There are several types of iterators, e.g., selective, constructive,
active, and passive.)

*** And now, back to our story ... ***

A rule of class design is that classes contain only primitive
operations in their interfaces. (Don't worry. There are well-defined
ways for dealing with highly-reusable composite operations.) Another
rule is that each operation in the interface of a class must be
definable as a selector, constructor, or iterator _solely_ with
respect to that class.

Let us now re-examine the "counter class." We said that the counter
class had several operations in its interface:

	- Zero (the value of the counter), which is a constructor
	  operation with respect to the counter, and is also a
	  primitive operation for the counter class.

	- Increment (the value of the counter by one), which is also a
	  constructor operation with respect to the counter, and is
	  also a primitive operation for the counter class.

	- Decrement (the value of the counter by one), which is yet
	  another primitive constructor operation for the counter
	  class. 

However, when we come to the display (the current value of the counter
on an output device) operation, we run into trouble. We might be
tempted to describe this operation as a selector with respect to the
counter class since it does not change the state of a counter object,
but that is not the whole story. Further examination reveals that the
state of the output object (device) is being changed, e.g., characters
appear on the device.

We now realize that we have a composite operation on our hands, i.e.,
"Display." There are at least two primitive operations, each for a
different class, i.e,:

	- a selector operation which returns the current value of a
	  counter object, and

	- a constructor operation which alters the state of the output
	  object (e.g., the aforementioned "put" operation)

There may also be other classes and operations involved.

Our course of action is simple. We first remove the composite
operation (i.e., "Display") from the interface of the counter class
and replace it with a selector operation which returns the current
value of a counter object. We have now decoupled the counter class
from the output object class using selector decoupling. 

At this point, we have quite a number of options available to us, but
I choose not to discuss them, or the tradeoffs among them, here.
However, I will say that a good course of action is to decompose the
composite operation ("Display"), uncovering primitive operations as we
go, and decoupling objects as you go as well.

> >[By the way, there are specific techniques for removing undesirable
> >object coupling. There are also specific guidelines for both avoiding
> >unnecessary object coupling, and in the construction of reusable
> >systems of objects.]
> >
> Where?  I think I want to look this up.

See the above discussion. More details are available now in the my course
notes, and, later, in my book (if I ever get it finished).

> >Objects encapsulate:
> >
> >	- knowledge of state information
> >	- operations (and their corresponding methods)
> >	- other objects, i.e., there can be:
> >		- homogeneous composite objects
> >		- heterogeneous composite objects
> >	- exceptions
> >	- constants
> >	- concepts
> >

> Could you define "exceptions, constants, and concepts" more?  Particularly as
> to transferring this information from process to process?  A similar question:
> should processes be thought of as objects and what implications does that
> have?

Please allow me to provide you with some partial answers to these
questions. (This message has got to be quite long already, and I'm
bushed.)

Exceptions are a mechanism which allows an object to alert the system
of objects in which it is encapsulated that an exceptional condition
has occurred. Unlike an "error flag," or the return value for a
function, which the system can forget to check for, or ignore,
exceptions demand that the system take some action -- even if that
action is only to "turn off" (or "lower") the exception.

Many programming languages provide exceptions (e.g., PL/1, Mesa, Ada),
and the semantics of exceptions, and exception handling is slightly
different in each one. (Exception semantics and exception handling in
the presence of multiple threads of control is another fairly
interesting topic area.)  However, the designer of a class must
determine what exceptional conditions might arise through use (or
abuse) of the class, and provide well-named exceptions for these
conditions.

Examples of exceptional conditions for which one might want to provide
exceptions are: attempting to add another item to a list which is
already full, attempting to address a memory location in an EEPROM
which is out of range, and a temperature sensor which detects a value
outside of the limits set by a user.

Notice that exceptions are one mechanism which allows an object to
notify other objects of specific situation. Notice further that since
exceptions do not require mention of other specific classes or
objects, they should not contribute to object coupling. Well-designed
objects and classes provide well-named exceptions in their interfaces.

By "constants" I mean objects of constant state. The designer of an
object or class may occasionally find it useful, or even necessary, to
provide a constant in the interface to the object or class. These
constants are usually used by one, or more, of the operations in the
interface for the class, but may have other uses. Note that the
constants I am referring to are not part of the state of an object.

By "concepts" I mean the idea behind the abstraction, including such
things as the intended, and proper, use of the object or class. For
example, given a savings account object, one must put money in before
one can expect to take money out.

Objects and classes which lack coherent, and well-thought-out concepts
are useless, dangerous, or both.

There is so much more to tell, but Uncle Ed is really beat. (Its after
1:00 a.m.) See you next time. ;-) ;-) ;-)

				-- Ed Berard
				   Berard Software Engineering, Inc.
				   18620 Mateney Road
				   Germantown, Maryland 20874
				   Phone: (301) 353-9652
				   E-Mail: eberard@ajpo.sei.cmu.edu

jacob@gore.com (Jacob Gore) (08/05/89)

/ comp.lang.c++ / eberard@ajpo.sei.cmu.edu (Edward Berard) / Aug  4, 1989 /
> Our course of action is simple. We first remove the composite
> operation (i.e., "Display") from the interface of the counter class
> and replace it with a selector operation which returns the current
> value of a counter object. We have now decoupled the counter class
> from the output object class using selector decoupling. 

This appears to imply that there is an intermediate representation for the
object's value that can be returned by the source object's selector and the
destination object's constructor.  In this case, it's easy: a number object
can represent the counter's value.

What do you do if the value is quite complex?  Encode it in some
agreed-upon representation, such as in a structure or a character string?

Jacob
--
Jacob Gore	Jacob@Gore.Com		{nucsrl,boulder}!gore!jacob

eberard@ajpo.sei.cmu.edu (Edward Berard) (08/06/89)

In article <110003@gore.com>, jacob@gore.com (Jacob Gore) writes:
> / comp.lang.c++ / eberard@ajpo.sei.cmu.edu (Edward Berard) / Aug  4, 1989 /
> > Our course of action is simple. We first remove the composite
> > operation (i.e., "Display") from the interface of the counter class
> > and replace it with a selector operation which returns the current
> > value of a counter object. We have now decoupled the counter class
> > from the output object class using selector decoupling. 
> 
> This appears to imply that there is an intermediate representation for the
> object's value that can be returned by the source object's selector and the
> destination object's constructor.  In this case, it's easy: a number object
> can represent the counter's value.
> 
> What do you do if the value is quite complex?  Encode it in some
> agreed-upon representation, such as in a structure or a character string?

You have just touched on quite a number of interesting issues, e.g.:

	- The difference between primitive, simple, and composite
	  objects and classes

	- The concept of parameterizing classes (thus creating
	  metaclasses) 

	- Designing object-oriented systems such that you minimize
	  coupling and maximize reusability

Lets start with the differences between primitive, simple, and
composite classes:

	- primitive objects and classes are those supplied by the
	  environment, e.g., typically by the programming language.
	  They have two main uses: creation of user-defined objects
	  and classes and low-level communication among user defined
	  objects (i.e., they are the "lingua franca," the "common
	  language understood by all entities in the system").
	  Although available primitive objects and classes vary from
	  environment to environment (e.g., from programming language
	  to programming language), common examples of primitive
	  objects and classes are: integer, character, string, and
	  real numbers.

	- simple objects and classes are user-defined objects and
	  classes which, from an _external_ viewpoint, _conceptually_
	  have no discernible structure, i.e., they are monolithic entities.
	  Although we might suspect that the _internal_ structure of a
	  simple object or class might be quite complex, if we cannot
	  unambiguously describe what that structure is, given only an
	  outside view, then the object or class is a simple object or
	  class. Examples of simple objects and classes include: a
	  temperature sensor, a month, and a counter.

	- composite objects and classes are (for the most part)
	  user-defined objects and classes which, from an _external_
	  viewpoint, _conceptually_ have a discernible structure.
	  Those outside the object or class, may not know the precise
	  underlying implementation of the composite object or class,
	  but the _conceptual_ structure is known. There are two types
	  of composite object and classes: heterogeneous composite
	  object and classes and homogeneous composite objects and
	  classes.

	  Homogeneous composite objects and classes are _conceptually_
	  composed of other objects and classes which are
	  _conceptually_ all of the same type. Examples of homogeneous
	  composite objects and classes include: a list of names, a
	  mailbox, and a priority queue.

	  Heterogeneous composite objects and classes are
	  _conceptually_ composed of other objects and classes which
	  are _conceptually_ of potentially different types. Examples
	  of heterogeneous composite objects and classes include: a
	  date (composed of a month, a day, and a year), a purchase
	  order, and (in a communications system) a message.

	  Sometimes the primitive objects and classes provided to us
	  include composite objects and classes. The class "string" is
	  an example of a primitive homogeneous composite class.

The above distinctions are quite useful, for example, in
systematically designing resuable objects and classes, avoiding object
coupling, and in overall object-oriented development. (Yes, there are
guidelines, rules, techniques, and methods which involve the above.)

Next, we move to the discussion of parameterizing classes. Suppose I
wish to create a "date class." I know that I want this class to
conceptually involve a month, a day, and a year. I have a number of
options available to me:

	- "Hardcode" some primitive class, or primitive classes in the
	  underlying implementation. This is not flexible. Suppose,
	  for example, I wish to change how months are represented, or
	  I wish to extend the range for the years.

	- I could "hardwire" a specific "day class," a specific "month
	  class," and a specific "year class." This again doesn't
	  sound terribly flexible. 

Suppose, however, I decided to parameterize the date class.
Specifically, I designed the date class in a "generic" manner, i.e., I
treated the day class, month class, and year class as abstractions. I
stipulated that users of the "generic date class" would have to supply
their own month class, day class, year class, and any other necessary
associated operations and other appropriate items.

As you might have guessed, this "generic date class" is no longer a
conventional class, i.e., it is a metaclass. A metaclass is a class
whose instances are themselves classes. To use the generic date class,
I supply the necessary parameters, and create an instance, i.e., a
specific date class. This "specific date class" can then be used to
create objects, i.e., date objects.

Now, let's answer Jacob's specific question with an example. Suppose I
want to create a simple application which will read in, and print out,
dates. I decide that I will need the following classes:

	- the aforementioned "generic date class"
	- a month class
	- a day class
	- a year class
	- an input object class
	- an output object class
	- for purposes of this example, a primitive class, i.e.,
	  string

In the process of creating this application, I realize that I will
need something to coordinate all of these classes, and their
associated objects. We will call this intelligent ether "the glue" The
glue is itself an object (actually, a "system of objects" -- see,
e.g., my recent posting on comp.lang.smalltalk). It is the glue which
knows about all of the objects and classes in the application.

Now let us look at the generic date class. We find that there are six
operations in its interface:

	- given a specific date object, and a specific month object,
	  set the value of the month in the date object to the value
	  of the month object

	- given a specific date object, return a month object
	  containing the value of the month for the specific date

	- a pair of similar operations for the day

	- a pair of similar operations for the year

Notice that the date object has no conception of what a month, a day,
or a year look like. It is as if each date object was a black box with
three bins, i.e., one each fro a month object, a day object, and a
year object. (Yes, there are ways to create an "intelligent date
class," but we don't have the space to discuss it here.)

We do know that if we wish to set the value of, say, the year for a
given date object, we had better show up with a black box called a
year object. Further, if we ask for the value of a day for a given
date, we will be handed a black box called a day object.

On the other side, we have our input object and our output object,
which are instances of the input object class, and the output object
class respectively. We notice that each has only one operation in its
interface (please, this is a simple example):

	- The input object has an operation called "get" in its
	  interface which returns instances of the primitive
	  homogeneous composite class "string." It knows nothing about
	  dates, months, days, years, output objects, and glue. It
	  just spews out strings.

	- The output object has an operation called "put" in its
	  interface which requires input in the form of instances of
	  the primitive class string. Like the input object, it knows
	  about no other objects or classes, except string.

So here we sit. The input and output objects only know about strings,
and the date objects only know about month, day and year objects (from
a strictly external viewpoint at that). We must now examine the month
class, day class, and year class. Since we will most likely do the
same thing to all three classes, let's pick the year class as an
example:

In the interface to the year class, we will find two operations:

	- a constructor operation we will call "from_string," whose
	  purpose is to take a string object, and return an instance
	  of the year class, i.e., a year object.

	- a selector operation we will call "to_string," whose purpose
	  is to take a year object, and return its value as an
	  instance of the string class.

Note that the underlying implementation of a year could be anything,
e.g., a string, an integer, an enumerated type, or an encrypted value.

In the interest of polymorphism, we will have operations with
identical names and purposes in the month class and the day class. 

Since I am trying to make this discussion
programming-language-independent, we will now discuss how the overall
application will work in general terms. Remember that it is the "glue"
which contains all the application-specific information. We will start
from the point where all objects have been declared.:

	1. The "glue" gives a prompt, in the form of a string object,
	   to the output object.

	2. The glue waits for a string object from the input object.

	3. The glue sends the sting object to the appropriated class
	   (i.e., month, day, or year) requesting the from_string
	   operation. Assuming all goes well, an object of the
	   appropriate class is returned.

	4. The glue sends the month, day, or year object to the date
	   object requesting that the appropriate operation
	   (set_month, set_day, or set_year) be accomplished.

	5. Steps 1-4 are repeated until we have one, or more, date
	   objects with values for each of its component objects.

	6. The glue then requests a month, day, or year object from a
	   given date object.

	7. The glue then sends the month, day, or year object
	   requesting the to_string operation. Assuming all goes well,
	   an object of the class string is returned.

	8. The glue then sends a prompt in the form of an instance of
	   class string to the output object. Then, the glue sends the
	   string object representation for the month, day, or year
	   object to the output object.

	9. Steps 6-8 are repeated until all the desired dates are
	   printed (displayed, or whatever).

Notice that knowledge of the primitive class string was only required
by the input object class, the output object class, and the month, day,
and year classes. In true polymorphic form, the date class dealt with
other objects only as black boxes, and in a uniform manner. If we had
had a "list metaclass," we could have constructed a list of dates.

I have said way too much already. I have to go. Thanks for listening.

				-- Ed Berard 
				   (301) 353-9652

gregw@otc.otca.oz.au (Greg Wilkins) (08/22/89)

davidm@cimshop.UUCP (David Masterson) writes:

> However, in C++, its not a good idea for a constructor function to
>return before the reply (and, therefore, the data) has been received.  Thus
>the C++ program is hung. So, my question is what is the appropriate C++
>fashion for constructing an object when the object construction may involve
>sending a message and getting a reply that you don't want to wait for?

The problem of  Objectifying incoming messages is a very large complex problem.
However I think you have invented an extra problem for yourself.  
If you cannot completely construct an object until a message is sent and
a reply recieved, then
I see the the solution is to construct a base class, this bass class
then sends the message to find out
which particular specialization it is and coerces itself to a derived
class when it recieves the reply.

As I said before, I dont think this is the main issue with Objectifying
incoming messages.  The main problem
is coming up with a general method for inserting/extracting type
information without big case statements.
The central type server (to which you send a querry and recieve your
type in reply) may be one solution, but
it appears that all it is achieving is moving the case statement
somewhere else and ignoring the problem of
placement of explicit type information.

gregw@otc.oz

dog@cbnewsl.ATT.COM (edward.n.schiebel) (08/22/89)

From article <828@otc.otca.oz>, by gregw@otc.otca.oz.au (Greg Wilkins):

[stuff deleted]

> However I think you have invented an extra problem for yourself.  
> If you cannot completely construct an object until a message is sent and
> a reply recieved, then
> I see the the solution is to construct a base class, this bass class
> then sends the message to find out
> which particular specialization it is and coerces itself to a derived
> class when it recieves the reply.
> 
You cannot do this.  The memory allocated at the time of the call to
the constructor is for a base class. By coercing it to a derived class
you
	1. are probable going to walk of the end of your memory
	2. are requiring a base class to know of its derived classes,
	   something which you may want to avoid.
	
One possible way around this is to define a base class which defines
the interface to your hierarchy of objects which manages only
a pointer to another base class, the real base class of your hierarchy.
The interface-base's constructor can figure out what type of object
is needed, allocate/construct it, and set its member pointer-to-base-class
to the new object.  It costs you a level of indirection, but it works.
Another win with this scheme, is you can change the apparent type of the 
object at run-type by deleting the current poniter and re-creating a
new different one from the hierrchy.
i.e.:
	class interface_base {
	public:
	   // interface functions
	   void interface_function1() {ptr->interface_function1();}
	private:
	   abstract_base* ptr; // points to one of many possibilities
	                       // in the hierarchy
	};
	
	Ed Schiebel
	AT&T Bell Laboratories
	...att!vilya!dog
	
ps.
thanks to jonathan shopiro for this idea a couple years ago!

davidm@uunet.UU.NET (David S. Masterson) (08/22/89)

>As I said before, I dont think this is the main issue with Objectifying
>incoming messages.
>
Agreed, to a point.  However, the need to objectify an incoming message is
often the result of a outgoing request of some sort.  So, the question of
exitting a constructors and reentering constructors was more of "is there a
proper arrangement for making requests that will facilitate processing replies
without switch statements in C++?"

>The main problem is coming up with a general method for inserting/extracting
>type information without big case statements.  The central type server (to
>which you send a querry and recieve your type in reply) may be one solution,
>but it appears that all it is achieving is moving the case statement somewhere
>else and ignoring the problem of placement of explicit type information.
>
Agreed.  In C, there is the method of function call jump tables to get around
massive switch statements.  Is there such a thing as an object call jump
table?  (The return types would probably be different.)  Could you define
coercion of a base class into a derived class (the method still throws me)?

David Masterson
uunet!cimshop!davidm