I agree with answer of Leandro. Defensive programming is rarely the Smalltalk way of doing things. There is no static typing for enforcing anything, nor private messages. Smalltalk is open and late-bound. But on the other hand, errors are rarely catastrophic (Smalltalk generally does not crash on error).
This late-binding along with graceful error recovery is what makes evolution of the system a very lightweight process. When you are programming in Smalltalk, you do nothing but make the live system evolve if you think of it.
Since the only thing we have is to send messages, we can eventually use that for negotiating the contracts, or just verifying some pre-condition.
The strategy that you are suggesting is possible though thru usage of Exception. The idea is that it's sometimes better to have an early Exception nearest possible to the root cause, than a late Exception more difficult to debug and correct. See for example the message #shouldNotImplement
and its senders: you'll see that it is sometimes used to prevent usage of #new
.
Also don't forget that there is an #initialize
message that can be used to give a reasonable default value to the instance variables (a bit like the default constructor of C++). There are variants when you want to pass additional information: for Collections that are generally created thru #new:
there is a #initialize:
message taking the size as argument. You can refine similar mechanisms at will for passing other information at creation.
But don't try to prevent usage of #basicNew
. You would create pain to yourself by breaking some services relying on this low level feature, like mutating existing instances when you modify your class layout, like copying, like storing in external files, etc... see #adoptInstance:
for example.
Instead, you must learn to trust the users of your library (including yourself): users won't use basicNew
unless they know what they do (they will learn that sooner or later). And as Amos said, document the contracts and expectations in class or message comments or unit tests, so that people can learn more rapidly, and maybe use not so inviting names like basicNew when you want to express that a message is for knowledgeable use only.
EDIT by the way, if you forbid basicNew, you will not be able to create instances at all, so you will have to create a new message invoking the primitive for creating an instance, and in the end you will just have obfuscated/complexified code for nothing, because you just displaced the problem. Unless you do very nasty things like:
basicNew
thisContext sender method selector == #mySepcialCreationMessageWithParameter: ifTrue: [^super basicNew].
^self error: 'new instances should be created with mySepcialCreationMessageWithParameter:'
As I said above, you can play with it, but don't do it for real.
EDIT 2 Another point of view is that coding is a social activity, and the code you are writing in Smalltalk is not just for programming an automaton. It is for being read and reused by humans. In this context, defensive programming is a bit like managing by coercion. Instead of losing time in trying to constrain, it's more efficient to spend time in creating simple and logical abstractions that can be easily understood and reused, maybe in other contexts you had not foreseen.