About KCP and automatic initialize
Richard A. O'Keefe
ok at cs.otago.ac.nz
Mon Sep 15 06:01:34 UTC 2003
Richard Staehli <rastaehli at mac.com> wrote:
Pattern 1: Canonical Initializer
Problem: How to establish a single method for an object that
initializes to a well defined state. Users with minimal training
should be able to discover this method and use it reliably to
initialize the object.
Solution: Require that every object have a method named with the
prefix 'initCanonical' and taking a set of arguments that completely
define the state of the object. If the object inherits state from a
superclass, the 'initCanonical' method is responsible for initializing
this state and this is easily accomplished by calling the superclass'
canonical initializer.
This invites the obvious questions:
(1) Why SHOULD a method have a single "best" initialisation method?
For example, suppose one has
File openForInput: aFileName
File openForReplacement: aFileName
File openForAppending: aFileName
There might in fact *be* a common method,
File pvtOpen: aFileName method: rwa
which they all share, but you might have excellent reason to keep
that method private. (Not least so you don't have to worry about
unexpected values for the rwa argument.)
One of the big differences between Smalltalk and Simula 67 was that
in Simula 67 a class was basically a procedure that returned its
(heap-allocated) activation record, so there _could_ only be one
constructor for a class, whereas Smalltalk lifted this restriction.
(2) I don't like the sound of "users with minimal training".
(3) If there _is_ a preferred instance creation method, even users with
minimal training *ought* to be able to find it in the class comment,
no? Isn't insisting that class comments be filled out even better
than insisting on twisted method names?
Really, anyone who can't look for 'instance creation' in the browser
and maybe read one or two method comments is so minimally trained that
they shouldn't be using Squeak yet. One hour of instruction should
fix that. If there _aren't_ any method comments, _nothing_ is going
to help; there isn't any guarantee that the default method is the
right one and there never can be.
(4) "initCanonical" violates several good naming practices. Why is
"init" abbreviated by not "Canonical"? Why not just say that it
should begin with "initialize"? Even then, "init" isn't _quite_
intention-revealing. The important thing to do is to establish the
class invariant, and that may involve updating other objects entirely,
and it might _not_ involve initialising any instance variables at all.
(5) The "initialize" method will normally be of no interest to clients
at all; they will be interested in some flavour of "new".
(6) Just because an object inherits state from a parent class, it does
NOT follow that the right way that shared state is to use the parent's
initialiser. Often it is, often it isn't. The way Java forces me to
do this is one of the things I dislike about Java, and the way Eiffel
*doesn't* is one of the things I like about Eiffel.
Pattern 2: Convenience Method
Problem: Commonly repeated sequences of code increase the chance of
error and obscure more important aspects.
Solution: Define a method that aggregates the repeated sequence into a
single message send. For example, if a canonical initializer is
frequently called with the same value for many of its arguments,
replace these calls with a call to a convenience method that
encapsulates these invariant values. The invocation of the convenience
method is syntactically shorter because it takes only the arguments
that vary.
This is just factoring.
Pattern 3: Default Constructor
Problem: Users frequently need to obtain new objects with well
defined state, but cannot be expected to understand how to reliably
initialize that state.
WHAAAAAAAAAT?
If "users" cannot "cannot be expected to understand how to reliably
initialise" an object, then they cannot be expected to understand
how to do ANYTHING with an object.
Solution: Require every class to have a method named
'defaultNew' that takes no arguments, but that creates a basic
new instance and calls the canonical initializer for the object
with default arguments.
I have recently explained at some length why this is HIGHLY UNDESIRABLE,
leading to poor design, or the exposure of incompletely initialised
objects, or both.
My whole contribution to this thread is a protest against this
absurd and dangerous idea that there *ought* to be a default parameterless
way of initialising every kind of object and a plea that indulging in such
a dangerous practice should be made *harder*, not easier.
To repeat an example I've used before, there is *NO* set of default
arguments which could make "Point new" reasonable.
To put it in pattern terms:
"No #'new's is good news"
Problem:
For many classes a parameterless #new (and correspondingly
a parameterless #initialize) makes no sense. The automatic
inheritance of Object>>new, and the recent proposal to make
that automatically invoke #initialize, make it far too easy
for people to create incompletely initialised objects and
encourage bad design.
Solution:
Change the browser so that when you create a new class
the class method "new self shouldNotImplement" with a
suitable comment is automatically added, requiring explicit
programmer action to *remove* this protection.
Finally:
It would seem to be a good idea to agree on a "canonical initializer"
prefix and "default constructor" name for squeak.
We *have* a default constructor name for Squeak, it's #new, and we had
better live with it rather than try to invent something different.
The problem is not the *name* of the method, but it's very *existence*.
Adopting the "No #'new's is good news" pattern would fix the problem.
More information about the Squeak-dev
mailing list
|