(e.g. if they contain cyclic references) [...] In order to do this one could have constructors that can create more than one object. The programmer would then put in a single constructor all the code ensuring that, once objects o1, ..., on have been created,
Cyclic dependencies between the objects? Then the answer is "don't use (just) a constructor", because cyclic method calls don't make any sense. Instead, this is a classic scenario for some Factory methods.
Here's an example (in PHP) which is sort of silly but illustrates the general idea: You cannot create a Sprocket
without an associated Widget
, and vice-versa. Each object has a reference to the other when it becomes available.
class Widget {
protected $sprocket = null;
public function getSprocket(){ return $this->sprocket; }
protected function __construct(){
// Constructor cannot be called from outside, not public
// Initialize self, to a limited degree
}
public static function create(Sprocket $partner=null){
$me = new Widget(); // Can call constructor from same class
// Here we can make all sorts of changes and additions and other
// objects, wiring them all together, before revealing the result
// to the outside world.
if($partner !== null){
$me->sprocket = $partner;
}else{
$me->sprocket = Sprocket::create($me);
}
return $me;
}
}
/*
* Practically identical to Widget, just with some renaming
*/
class Sprocket {
protected $widget = null;
public function getWidget(){ return $this->widget; }
protected function __construct(){ }
public static function create(Widget $partner=null){
$me = new Sprocket();
if($partner !== null){
$me->widget = $partner;
}else{
$me->widget = Widget::create($me);
}
return $me;
}
}
/*
$mw = new Widget(); // NOT ALLOWED! Constructor not public
*/
$w = Widget::create(); // OK to call
$s = $w->getSprocket();
$w2 = $s->getWidget();
assert($w == $w2);
If I had to do this in PHP 5.2 in real life, I'd simply just leave the constructors public, and tell people not to use them. Instead I'd have a makeWidgetWithSprocket()
function. If the language was Java, I'd use its package-level visibility controls to further prevent mistakes.
Additional reading:
- If you want to pass lots of options and settings to
create()
, consider the Builder pattern.
- If you find yourself needing a lot of flexibility to rewire things in different ways, look at Inversion of Control frameworks, which commonly (but not always) use dependency injection to connect objects together "from the outside".
factory
pattern.A with B
style type composition, whose resultant constructor would essentially build the new type composed of the two traits.