Alguma linguagem de programação orientada a objetos suporta "construtores coletivos"?

8

Recentemente, eu estava considerando que, às vezes, vários objetos dependem um do outro (por exemplo, se eles contêm referências cíclicas) e, portanto, seria útil criá-los como parte de uma operação atômica que garante que, após a construção, os novos objetos cumpram alguma restrição coletiva. .

Para fazer isso, é possível ter construtores que podem criar mais de um objeto. O programador colocaria em um único construtor todo o código, garantindo que, uma vez criados os objetos o 1 , ..., n , eles satisfizessem todas as restrições necessárias (por exemplo, todos os links entre os novos objetos já estão em vigor). Eu mesmo inventei o termo construtores coletivos, porque nunca ouvi falar desse recurso, mas é possível que exista um nome aceito para esse conceito.

Então, existe alguma linguagem de programação suportando esse tipo de construtores? Caso contrário, a idéia já foi testada?

Giorgio
fonte
3
Não tenho certeza, mas a primeira coisa que me vem à cabeça é o factorypadrão.
JensG
3
O eleitor pode, por favor, explicar a votação apertada? Estou perguntando se existe uma linguagem de programação com um recurso muito específico. A pergunta não parece fora de tópico ou mal formulada para mim, mas estou aberto a feedback.
Giorgio
@ JensG: Eu pensei sobre isso, mas uma fábrica tem algumas limitações. Por exemplo, se um objeto contém uma referência imutável, você não pode configurá-lo em uma fábrica: você pode configurá-lo apenas no construtor. Estou considerando a semântica Java aqui, mas acho que isso pode ser um problema geral.
Giorgio
Isso é interessante, pois estou cortando algo semelhante em C # usando contêineres IoC atualmente. Além disso, como isso difere dos idiomas que possuem características e permite que essas características sejam misturadas para preencher membros abstratos? Uma linguagem de brinquedo em que trabalhei na A with Bcomposição de tipo de estilo suportada , cujo construtor resultante essencialmente construiu o novo tipo composto pelas duas características.
Telastyn
3
Essa questão claramente não está "nos pedindo para recomendar uma ferramenta, biblioteca ou recurso favorito externo" e é uma vergonha para o site o fato de duas pessoas terem votado para fechá-lo. Eu reformulei o título um pouco para tornar isso ainda mais claro.
Carson63000

Respostas:

12

Isso me parece o padrão Builder , que é uma forma mais complexa de uma fábrica. Um exemplo de Java é um StringBuilder , que você usa para criar uma String e, em seguida, chama builder.toString()quando deseja o resultado imutável. No entanto, você pode usar o padrão do construtor para criar coleções de objetos não homogêneas mais complexas. Você pode usar a friendsemântica em um construtor para obter acesso a variáveis ​​privadas que seriam consideradas imutáveis ​​após a criação do objeto de resultado.

Linguagens funcionais podem fornecer alguma inspiração. Scala, por exemplo, possui um ListBuffer mutável que pode ser usado para o estágio de construção e, em seguida, convertido em uma lista imutável .

Outro conceito possivelmente relevante é o Freezable Objects , pelo qual um objeto é considerado mutável até que um Freeze()método seja chamado. Um construtor pode usar os objetos descongelados e congelá-los antes de retornar.

Karl Bielefeldt
fonte
Acho muito boa a ideia de um padrão de construtor em combinação com um objeto congelável. Não é exatamente o que eu tinha em mente, mas resolve o meu problema sem introduzir um novo conceito ad-hoc, que é bom para a OMI.
Giorgio
+1: Eric Lippert tinha uma série de artigos relacionados a objetos imutáveis ​​e denominou uma das variações de objetos "congeláveis" Imutabilidade de picolé (termo de pesquisa conveniente para encontrar os artigos :)) - toda a série pode ser útil para ler se você estiver construindo imutáveis ​​complexos objetos em linguagem semelhante a C #.
Alexei Levenkov 15/01
4

Ruby, Smalltalk, Self, Newspeak etc. não têm construtores, apenas possuem uma convenção de nomenclatura para métodos de fábrica (por exemplo, newem Ruby). Como são apenas métodos padronizados como qualquer outro método, eles podem fazer o que quiserem, incluindo alocar e inicializar quantos objetos quiserem. O principal problema é que esses métodos de fábrica retornam, por convenção, um único objeto que é uma instância da classe na qual o método de fábrica foi chamado. Ou seja, ao chamar Foo.new, espera-se que você retorne um único objeto que é uma instância do Foo. Mas, com documentação suficiente, você pode retornar alguma estrutura (por exemplo, uma Array) de vários novos objetos.

Exemplo:

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

No ECMAScript, construtores são apenas procedimentos regulares, ou melhor, qualquer procedimento pode ser usado como construtor apenas colocando a newpalavra - chave na frente dele. Novamente, como são apenas procedimentos regulares, eles podem fazer o que quiserem.

Jörg W Mittag
fonte
1

(por exemplo, se eles contêm referências cíclicas) [...] Para isso, é possível ter construtores que podem criar mais de um objeto. O programador colocaria em um único construtor todo o código, garantindo que, uma vez criados os objetos o1, ...,

Dependências cíclicas entre os objetos? Então a resposta é "não use (apenas) um construtor", porque as chamadas de método cíclico não fazem nenhum sentido. Em vez disso, este é um cenário clássico para alguns métodos de fábrica .

Aqui está um exemplo (em PHP) que é meio bobo, mas ilustra a idéia geral: Você não pode criar um Sprocketsem um associado Widgete vice-versa. Cada objeto tem uma referência ao outro quando ele se torna disponível.

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

Se eu tivesse que fazer isso no PHP 5.2 na vida real, simplesmente deixaria os construtores públicos e diria às pessoas para não usá-los . Em vez disso, eu teria uma makeWidgetWithSprocket()função. Se a linguagem fosse Java, eu usaria seus controles de visibilidade no nível do pacote para evitar erros.

Leitura adicional:

  • Se você deseja passar muitas opções e configurações create(), considere o padrão Construtor .
  • Se você precisar de muita flexibilidade para religar as coisas de maneiras diferentes, observe as estruturas Inversion of Control , que geralmente (mas nem sempre) usam injeção de dependência para conectar objetos "de fora".
Darien
fonte