¿Algún lenguaje de programación orientado a objetos admite "constructores colectivos"?

8

Recientemente estuve considerando que a veces varios objetos dependen unos de otros (por ejemplo, si contienen referencias cíclicas) y, por lo tanto, sería útil crearlos como parte de una operación atómica que asegure que, después de la construcción, los nuevos objetos cumplan alguna restricción colectiva .

Para hacer esto, uno podría tener constructores que puedan crear más de un objeto. El programador entonces pondría en un solo constructor todo el código asegurando que, una vez que los objetos o 1 , ..., o n hayan sido creados, satisfagan todas las restricciones requeridas (por ejemplo, todos los enlaces entre los nuevos objetos ya están en su lugar). He inventado el término constructores colectivos porque nunca he oído hablar de tal característica, pero bien podría existir un nombre aceptado para este concepto.

Entonces, ¿hay algún lenguaje de programación que soporte este tipo de constructores? Si no, ¿ya se ha probado la idea?

Giorgio
fuente
3
No estoy seguro, pero lo primero que me viene a la mente es el factorypatrón.
JensG
3
¿Puede el votante explicar el voto cerrado? Estoy preguntando si existe un lenguaje de programación con una característica muy específica. La pregunta no parece estar fuera de tema o mal formulada para mí, pero estoy abierto a comentarios.
Giorgio el
@JensG: Pensé en eso, pero una fábrica tiene algunas limitaciones. Por ejemplo, si un objeto contiene una referencia inmutable, no puede establecerlo en una fábrica: solo puede establecerlo en el constructor. Estoy considerando la semántica de Java aquí, pero creo que esto puede ser un problema general.
Giorgio
Esto es interesante ya que actualmente estoy pirateando algo similar en C # usando contenedores IoC. Además, ¿cómo difiere esto de los idiomas que tienen rasgos y permiten que esos rasgos se mezclen para llenar miembros abstractos? Un lenguaje de juguete en el que trabajé sobre la A with Bcomposición de tipo de estilo compatible , cuyo constructor resultante esencialmente construiría el nuevo tipo compuesto por los dos rasgos.
Telastyn el
3
Esta pregunta claramente no es "pedirnos que recomiende una herramienta, biblioteca o recurso favorito fuera del sitio" y es una vergüenza para el sitio que dos personas hayan votado para cerrarlo al hacerlo. He reformulado ligeramente el título para aclararlo aún más.
Carson63000

Respuestas:

12

Esto me suena como el Patrón de construcción , que es una forma más compleja de Fábrica. Un ejemplo de Java es un StringBuilder , que se usa para construir un String, luego se llama builder.toString()cuando se desea el resultado inmutable. Sin embargo, puede usar el patrón generador para construir colecciones de objetos no homogéneas más complejas. Puede usar la friendsemántica en un generador para obtener acceso a variables privadas que se considerarían inmutables después de crear el objeto de resultado.

Los lenguajes funcionales pueden proporcionar algo de inspiración. Scala, por ejemplo, tiene un ListBuffer mutable que puede usarse para la etapa de construcción y luego convertirse en una Lista inmutable .

Otro concepto posiblemente relevante es Freezable Objects , por el cual un objeto se considera mutable hasta Freeze()que se llama a un método. Un constructor podría usar los objetos no congelados y luego congelarlos antes de regresar.

Karl Bielefeldt
fuente
La idea de un patrón de construcción en combinación con un objeto congelable me parece muy buena. No es exactamente lo que tenía en mente, pero resuelve mi problema sin introducir un nuevo concepto ad-hoc, que es bueno para la OMI.
Giorgio
1: Eric Lippert tenía la serie de artículos en cuestión objetos inmutables y llamó a uno de variaciones de "congelable" objetos de paleta inmutabilidad (conveniente término de búsqueda para encontrar los artículos :)) - serie entera puede ser útil para leer si usted está en la construcción de complejos inmutable objetos en lenguaje similar a C #.
Alexei Levenkov
4

Ruby, Smalltalk, Self, Newspeak, etc. no tienen constructores, solo tienen una convención de nomenclatura para los métodos de fábrica (por ejemplo, newen Ruby). Como son simplemente métodos estándar como cualquier otro método, pueden hacer lo que quieran, incluso asignar e inicializar tantos objetos como quieran. El principal problema es que dichos métodos de fábrica devuelven, por convención, un único objeto que es una instancia de la clase a la que se llamó el método de fábrica. Es decir, cuando se llama Foo.new, se espera que recupere un solo objeto que es una instancia de Foo. Pero, con suficiente documentación, podría devolver alguna estructura (por ejemplo, una Array) de múltiples objetos nuevos.

Ejemplo:

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

En ECMAScript, los constructores son solo procedimientos regulares, o más bien, cualquier procedimiento puede usarse como un constructor simplemente pegando la newpalabra clave frente a ella. Nuevamente, dado que son solo procedimientos regulares, pueden hacer lo que quieran.

Jörg W Mittag
fuente
1

(por ejemplo, si contienen referencias cíclicas) [...] Para hacer esto, uno podría tener constructores que puedan crear más de un objeto. El programador entonces pondría en un solo constructor todo el código asegurando que, una vez que los objetos o1, ..., han sido creados,

¿Dependencias cíclicas entre los objetos? Entonces la respuesta es "no use (solo) un constructor", porque las llamadas a métodos cíclicos no tienen ningún sentido. En cambio, este es un escenario clásico para algunos métodos Factory .

Aquí hay un ejemplo (en PHP) que es un poco tonto pero ilustra la idea general: no se puede crear un Sprocketsin un asociado Widget, y viceversa. Cada objeto tiene una referencia al otro cuando está disponible.

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

Si tuviera que hacer esto en PHP 5.2 en la vida real, simplemente dejaría a los constructores públicos y le diría a la gente que no los use . En cambio, tendría una makeWidgetWithSprocket()función. Si el lenguaje fuera Java, usaría sus controles de visibilidad a nivel de paquete para evitar más errores.

Lectura adicional:

Darien
fuente