Unterstützen objektorientierte Programmiersprachen „kollektive Konstruktoren“?

8

Ich habe kürzlich darüber nachgedacht, dass manchmal mehrere Objekte voneinander abhängen (z. B. wenn sie zyklische Referenzen enthalten). Daher wäre es nützlich, sie als Teil einer atomaren Operation zu erstellen, die sicherstellt, dass die neuen Objekte nach der Konstruktion eine kollektive Einschränkung erfüllen .

Zu diesem Zweck könnte man Konstruktoren haben, die mehr als ein Objekt erstellen können. Der Programmierer würde dann den gesamten Code in einen einzigen Konstruktor einfügen, um sicherzustellen, dass die Objekte o 1 , ..., o n nach dem Erstellen alle erforderlichen Einschränkungen erfüllen (z. B. sind alle Verknüpfungen zwischen den neuen Objekten bereits vorhanden). Ich habe den Begriff kollektive Konstruktoren selbst erfunden, weil ich noch nie von einem solchen Merkmal gehört habe, aber es könnte durchaus einen akzeptierten Namen für dieses Konzept geben.

Gibt es eine Programmiersprache, die diese Art von Konstruktoren unterstützt? Wenn nicht, wurde die Idee bereits ausprobiert?

Giorgio
quelle
3
Ich bin mir nicht sicher, aber das erste, was mir in den Sinn kommt, ist das factoryMuster.
JensG
3
Kann der Wähler bitte die enge Abstimmung erklären? Ich frage, ob es eine Programmiersprache mit einer ganz bestimmten Funktion gibt. Die Frage scheint für mich nicht unangebracht oder schlecht formuliert zu sein, aber ich bin offen für Rückmeldungen.
Giorgio
@JensG: Ich habe darüber nachgedacht, aber eine Fabrik hat einige Einschränkungen. Wenn ein Objekt beispielsweise eine unveränderliche Referenz enthält, können Sie diese nicht in einer Factory festlegen. Sie können sie nur im Konstruktor festlegen. Ich denke hier über die Java-Semantik nach, aber ich denke, dies kann ein allgemeines Problem sein.
Giorgio
Dies ist interessant, da ich derzeit etwas Ähnliches in C # mit IoC-Containern hacke. Wie unterscheidet sich dies von Sprachen mit Merkmalen, die es ermöglichen, diese Merkmale zu mischen, um abstrakte Mitglieder zu füllen? Eine Spielzeugsprache, an der ich an der unterstützten A with BKomposition von Stiltypen gearbeitet habe, deren resultierender Konstruktor im Wesentlichen den neuen Typ erstellen würde, der aus den beiden Merkmalen besteht.
Telastyn
3
Bei dieser Frage geht es eindeutig nicht darum, "uns zu bitten, ein Tool, eine Bibliothek oder eine bevorzugte externe Ressource zu empfehlen", und es ist für die Site peinlich, dass zwei Personen dafür gestimmt haben, sie zu schließen. Ich habe den Titel leicht umformuliert, um dies noch deutlicher zu machen.
Carson63000

Antworten:

12

Das klingt für mich wie das Builder-Muster , eine komplexere Form einer Fabrik. Ein Java-Beispiel ist ein StringBuilder , mit dem Sie einen String erstellen und dann aufrufen, builder.toString()wenn Sie das unveränderliche Ergebnis wünschen. Sie können jedoch das Builder-Muster verwenden, um komplexere inhomogene Sammlungen von Objekten zu erstellen. Sie können die friendSemantik in einem Builder verwenden, um Zugriff auf private Variablen zu erhalten, die nach dem Erstellen des Ergebnisobjekts als unveränderlich gelten.

Funktionale Sprachen könnten Inspiration liefern. Scala verfügt beispielsweise über einen veränderlichen ListBuffer , der für die Erstellungsphase verwendet und dann in eine unveränderliche Liste konvertiert werden kann .

Ein weiteres möglicherweise relevantes Konzept sind Freezable Objects , bei dem ein Objekt als veränderlich betrachtet wird, bis eine Freeze()Methode darauf aufgerufen wird. Ein Builder könnte die nicht gefrorenen Objekte verwenden und sie dann einfrieren, bevor er zurückkehrt.

Karl Bielefeldt
quelle
Ich finde die Idee eines Builder-Musters in Kombination mit einem gefrierbaren Objekt sehr gut. Es ist nicht genau das, was ich mir vorgestellt habe, aber es löst mein Problem, ohne ein neues Ad-hoc-Konzept einzuführen, das IMO gut ist.
Giorgio
+1: Eric Lippert hatte eine Reihe von Artikeln, die unveränderliche Objekte betrafen, und nannte eine der Variationen von "einfrierbaren" Objekten. Eis am Stiel Unveränderlichkeit (bequemer Suchbegriff, um die Artikel zu finden :)) - ganze Reihen können nützlich sein, wenn Sie komplexe unveränderliche Objekte konstruieren möchten Objekte in C # -ähnlicher Sprache.
Alexei Levenkov
4

Ruby, Smalltalk, Self, Newspeak usw. haben keine Konstruktoren, sondern nur eine Namenskonvention für Factory-Methoden (z new. B. in Ruby). Da es sich wie bei jeder anderen Methode nur um Standardmethoden handelt, können sie alles tun, was sie wollen, einschließlich der Zuweisung und Initialisierung so vieler Objekte, wie sie möchten. Das Hauptproblem besteht darin, dass solche Factory-Methoden gemäß Konvention ein einzelnes Objekt zurückgeben, das eine Instanz der Klasse ist, für die die Factory-Methode aufgerufen wurde. Dh beim Aufrufen Foo.newwird erwartet, dass Sie ein einzelnes Objekt zurückerhalten, das eine Instanz von ist Foo. Bei ausreichender Dokumentation können Sie jedoch eine Struktur (z. B. eine Array) mehrerer neuer Objekte zurückgeben.

Beispiel:

class Foo
  def self.new
    new_foo = super

    new_bar = Bar.new
    new_bar.foo = new_foo

    new_foo.bar = new_bar

    return new_foo, new_bar
  end

  attr_accessor :bar
end

class Bar
  attr_accessor :foo
end

foo, bar = Foo.new

In ECMAScript sind Konstruktoren nur reguläre Prozeduren, oder vielmehr kann jede Prozedur als Konstruktor verwendet werden, indem einfach das newSchlüsselwort davor gesetzt wird. Da es sich nur um reguläre Verfahren handelt, können sie alles tun, was sie wollen.

Jörg W Mittag
quelle
1

(zB wenn sie zyklische Referenzen enthalten) [...] Dazu könnte man Konstruktoren haben, die mehr als ein Objekt erstellen können. Der Programmierer würde dann den gesamten Code in einen einzigen Konstruktor einfügen, um sicherzustellen, dass, sobald Objekte o1, ..., on erstellt wurden,

Zyklische Abhängigkeiten zwischen den Objekten? Dann lautet die Antwort "Verwenden Sie (nur) keinen Konstruktor", da zyklische Methodenaufrufe keinen Sinn ergeben. Stattdessen ist dies ein klassisches Szenario für einige Factory-Methoden .

Hier ist ein Beispiel (in PHP), das irgendwie albern ist, aber die allgemeine Idee veranschaulicht: Sie können kein Sprocketohne zugehöriges erstellen Widgetund umgekehrt. Jedes Objekt hat einen Verweis auf das andere, sobald es verfügbar ist.

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);

Wenn ich dies in PHP 5.2 im wirklichen Leben tun müsste, würde ich die Konstruktoren einfach öffentlich lassen und den Leuten sagen , dass sie sie nicht verwenden sollen . Stattdessen hätte ich eine makeWidgetWithSprocket()Funktion. Wenn die Sprache Java wäre, würde ich die Sichtbarkeitssteuerelemente auf Paketebene verwenden, um Fehler weiter zu vermeiden.

Zusätzliche Lektüre:

  • Wenn Sie viele Optionen und Einstellungen übergeben möchten create(), berücksichtigen Sie das Builder-Muster .
  • Wenn Sie viel Flexibilität benötigen, um Dinge auf unterschiedliche Weise neu zu verdrahten, schauen Sie sich Inversion of Control- Frameworks an, die üblicherweise (aber nicht immer) die Abhängigkeitsinjektion verwenden , um Objekte "von außen" miteinander zu verbinden.
Darien
quelle