[comp.software-eng] Object-oriented system startup

tgl@g.gp.cs.cmu.edu (Tom Lane) (03/26/91)

[I got no response to this in comp.object; maybe somebody here can help.]

I've run into a system structuring issue that seems like it ought to be
a standard problem with standard solutions in O-O programming.
However, I've never heard anybody address it in my (limited) exposure to
the field.  Pointers would be appreciated.

The application is JPEG image compression.  This involves a large number
of logically independent steps, many of which have several possible
implementations.  In some cases these alternatives are mandated by the
JPEG standard; for example, the last phase of compression is either
Huffman or arithmetic-coding compression.  In other cases the motivation
for multiple methods is simply optimization.  For example, one step
involves subsampling (averaging together small groups of pixels) using
varying sampling ratios.  It will probably be worthwhile to have
"hard-wired" code for the most common ratios (2:1, and especially the
trivial case 1:1) as well as a general method to handle any ratio; the
inner loops of the hard-wired methods will be much more efficient than
the general method can be.

I would like to set things up so that the different implementations for
any given step are different instantiations of the same object class
(or different subclasses of the same virtual superclass, if you prefer).
For instance, one could simply invoke last_phase_compression() without
worrying about whether this method points to Huffman or arithmetic
encoding.  More importantly, this lets me encapsulate information about
how many different special-case methods there are for any step and what
the conditions for choosing each one may be.  As a bonus, the conditions
for selecting a method can be quite complex since it's only done once at
startup (whereas the method may be called many times during a run).

Now here's my problem: how do I set up the initial method-selection
phase so as to preserve modularity?  I don't want to have a central
chunk of code that knows the real name of every method in the system
and the conditions for its use; that would be big, ugly, buggy, and a
total failure of modularity.  The only other thing I can think of is to
have a list of applicability-test functions, each of which decides
whether its associated method is applicable.  This has its own faults,
the main one being that the order in which the functions are tried
could create unexpected interactions.

Like I said, this seems like it must be a standard problem: any time you
instantiate particular objects from a large O-O class hierarchy, you have
the question of which subclass to instantiate and where do you put the
decision-making code.  Any advice?

(In case it matters, this will all be coded in vanilla C for portability
reasons.  So the "objects" will just be C structs containing function
pointers.  I'm not really concerned with hiding representational details
about the image data, only in having a cheap and transparent way of
selecting one of N similar routines at runtime.  But I'm not averse to
hearing about solutions that depend on features of real O-O languages;
maybe I can adapt their ideas.)

-- 
			tom lane
Internet: tgl@cs.cmu.edu	BITNET: tgl%cs.cmu.edu@cmuccvma

rick@tetrauk.UUCP (Rick Jones) (04/04/91)

In article <12479@pt.cs.cmu.edu> tgl@g.gp.cs.cmu.edu (Tom Lane) writes:
>[I got no response to this in comp.object; maybe somebody here can help.]

I must have missed it in comp.object! - here are some ideas:

>I've run into a system structuring issue that seems like it ought to be
>a standard problem with standard solutions in O-O programming.
>However, I've never heard anybody address it in my (limited) exposure to
>the field.  Pointers would be appreciated.
>
> [ deleted: outline of problem & underlying difficulty in selecting
>		which object to instantiate ]
>
>Now here's my problem: how do I set up the initial method-selection
>phase so as to preserve modularity?  I don't want to have a central
>chunk of code that knows the real name of every method in the system
>and the conditions for its use; that would be big, ugly, buggy, and a
>total failure of modularity.  The only other thing I can think of is to
>have a list of applicability-test functions, each of which decides
>whether its associated method is applicable.  This has its own faults,
>the main one being that the order in which the functions are tried
>could create unexpected interactions.

Polymorphism is a wonderful thing, but this is the classic catch - somewhere
you have to write the decision code on which of a variety of possible
sub-classes to instantiate, and you are likely to end up with a lot of
if-then-else stuff or complex switches which doesn't look in the least bit
object oriented.  I have run into this myself, and the essential things to keep
in mind are (a) write only one decision routine for one set of criteria, and
(b) encapsulate it.

To achieve this, you need an abstract definition of each of the modules for
which there will be multiple classes.  In a good OOPL - my work is all done in
Eiffel - you can define an abstract class which captures the common elements of
each module's interface.  Most of the routines probably won't be implemented,
but some can be, in terms of the deferred, unimplemented ones.  The code which
uses these modules is all written in terms of the abstract classes.

In order to select which version you are going to use, the cleanest approach is
to write one or more object-generator classes.  These contain functions which
will take parameters on which the decisions will be made, and will instantiate
the correct sub-type, returning a reference to the created object back to the
calling routine.  The declared type of the return value is the abstract type,
so the routine requesting the object never needs to know what actual type was
created.  From then on, you just use the object via its abstract interface, and
dynamic binding takes care of the rest.

By encapsulating the decision code in one place, the whole system is readily
extendable.  If you need to add another compression routine, you just need a
new module for the appropriate method, and another branch in the decision
routine in order to create it.

>Like I said, this seems like it must be a standard problem: any time you
>instantiate particular objects from a large O-O class hierarchy, you have
>the question of which subclass to instantiate and where do you put the
>decision-making code.  Any advice?

Well, that's my advice - I hope it helps.

>(In case it matters, this will all be coded in vanilla C for portability
>reasons.  So the "objects" will just be C structs containing function
>pointers.  I'm not really concerned with hiding representational details
>about the image data, only in having a cheap and transparent way of
>selecting one of N similar routines at runtime.  But I'm not averse to
>hearing about solutions that depend on features of real O-O languages;
>maybe I can adapt their ideas.)

I assume it's clear how you transform this idea into C using structs for the
function pointers - you seem to know what you're doing.  If portability is an
issue, do look at Eiffel.  The compiler will generate a highly optimised C
source suite for the entire program, complete with run-time support (in
source), and a makefile.  You can then transfer the package to anything with a
C compiler and re-build it.  You don't need an Eiffel compiler or any run-time
license for the target system.

If you wan't to go into more details that would be inappropriate for public
discussion, feel free to drop me some email.
-- 
Rick Jones, Tetra Ltd.  Maidenhead, Berks, UK
rick@tetrauk.uucp

Any fool can provide a solution - the problem is to understand the problem