This page posits that the primitive "new"
operator common to many ObjectOrientedProgramming
languages be ConsideredHarmful
. This is not a suggestion that "new"
be eliminated, but rather that it be replaced by other primitives.
The evidence stacked against "new"
is not insignificant. There exists a plethora of DesignPatterns
aimed to achieve flexibility, ConfigurableModularity
, extensibility at the time of 'setup' for an object graph. These include AbstractFactory
each suggest that something different is needed when it comes to PrimitivesAndMeansOfComposition
languages. Additionally, there are other 'setup' time concerns related to distribution, privacy or secrecy, persistence, redundancy, memory management, and so on, complete with its own gamut of DesignPatterns
, etc.). The collision of setup-time concerns between configuration and object management can, without extreme care, easily result in BigBallOfMud
frameworks (a problem exacerbated further by Static ManifestTyping
I can only suggest that the setup-time decisions and DesignPatterns
introduce a rather strong MissingFeatureSmell
Let us consider a typical implementation of "new"
. The basic responsibilities for this primitive are:
- Return an object name.
- Create a new object.
- Obtain memory from the memory system.
- Call a 'constructor' method.
Further, it inherits properties from the above that may be cause for concern:
- The memory system is often fixed. If so, new inherits from the memory system its specialized properties for physical location, persistence, redundancy, trust, and memory-management responsibility. Memory-management and persistence are especially common concerns, whereas the others are more specialized to DistributedSystems.
- The 'constructor' often has side-effects. If this is allowed then the language's new further introduces synchronization and order-of-construction concerns in addition to idempotence issues when restoring objects from a persistent state
- The 'constructor' is often fixed. If so, new specializes the code in which it is described for a particular behavior definition. AbstractFactory or AbstractConstructor can work around this.
- The 'constructor' often contains other constructors internally (sometimes implicitly, when dealing with member attributes). If so, then constructors may become specialized to an object's position in the overall object-graph. DependencyInjection is aimed to work around this.
A valid question is: Which is the greater responsibility of "new"? to create a new object? or to return an object name?
To answer "creating a new object"
is to ignore the prevalence of the SingletonPattern
and of data-driven access to abstractions of external systems (cameras, printers, clocks, random-number-generators).
Thus I posit that the answer is "to return an object name"
And, if so, I would posit that we really ought to have a primitive named something other than "new"
. I.e. NewConsideredHarmful
There are alternatives to "new"
, especially based around the AbstractConstructor
and possibly the FirstClass
construction of 'object configurations'. My own stab at doing so introduces a primitive named "construct"
which applies to an object-configuration introduced by "configure"
(with configurations in turn composed by FunctionalProgramming
). A fairly trivial configuration would be to create an object-configuration consisting of a name for exactly one object - which already exists. Constructing that configuration would return the name for the existing object. Trivial? Yes. But it does illustrate the distinction between "construct"
on a configuration vs.
"new" on a
Under-the-hood, my language also supports AbstractFactory PluginArchitecture
s (described in the AbstractFactory
page), which essentially operates as something of a distributed, persistent version of a ForeignFunctionInterface
. However, this is not available from the general language without explicit injection of an object that grants access to the AbstractFactory
and so is subject to the ObjectCapabilityModel
Based on my (rather DistributedSystem
s-biased) experience, I submit the following suggestions and reasoning towards designs that replace "new"
- Support something like data-driven AbstractConstructors to alleviate the various problems associated with concrete constructors and data-driven design, BUT also ensure that every such AbstractConstructor may transparently take as input simple references to an existing object which it shall return either intact or wrapped in some trivial fashion (e.g. to intercept messages for logging). This allows 'object graphs' to (without any special attention in user-code) be described by equal parts new and old objects. In turn, this allows new object-graphs to hook easily into existing systems.
- Perhaps make this a constructor for object-graphs rather than individual objects. Doing so would imply FirstClass support for both object configuration and DependencyInjection, which is an important (and oft missing) PrimitivesAndMeansOfComposition feature for ObjectOrientedProgramming. It would also allow a great many optimizations (block allocation, automatic distribution of whole cliques, dead-object elimination, inlining object behaviors for small objects, etc.). Further, it would allow greater SeparationOfConcerns between object vocabulary and the configuration contexts in which objects appear.
- Be rid of constructors and destructors, or more accurately be rid of side-effects other than consumption of resources. This is necessary to avoid side-effects for each persistence, duplication, and mobility operation. It also ensures that lazy construction, distribution, duplication, and persistence have an identical behavior profile to the abstraction of GodRAM - keeping all objects in-memory all the time.
- SeparationOfConcerns between naming objects and allocating them: use FirstClass references that are not associated with memory-addresses in RAM. This, in combination with being rid of side-effects for constructors and destructors, eliminates remaining concerns for order-of-construction when working with object-graphs. It supports lazy allocation, lazy distribution, and allows runtime migration of objects (as opposed to the weaker one-time automatic distribution). Also allows the possibility of automatic duplication-for-redundancy such that other systems might be able to regenerate some objects should their original locations fail - a stunt that is difficult to pull when naming is tightly correlated to a memory address.
- Distinguish synchronous or stateful objects from asynchronous and stateless 'ambients' or behaviors. This allows the ambients to be both 'merged' within machines (to conserve resources within a runtime) and duplicated between machines (to offer better latency and more robust service in the face of disruption). Even when not using DistributedSystems, the former ability to 'merge' identical objects should help with resource management. (Related: AbstractFactory, section on PluginArchitecture for DistributedSystems.)
- In addition to the above, the AbstractFactory page suggests that some plugin-driven objects may be operating as abstract subscriptions to ambient event-sources outside the object framework, such as the current time (update every second or minute), or the RSS feed from your favorite webpage, or an external PublishSubscribeModel service. To support this, one might further distinguish such event-sources from objects for purpose of deciding lazy vs. eager regeneration.