Pergunta

Quais são alguns , exemplos do mundo real comuns de usar o padrão Builder? O que comprar? Porque não usar um padrão Factory?

Foi útil?

Solução

A principal diferença entre um construtor e IMHO fábrica, é que um construtor é útil quando você precisa fazer um monte de coisas para construir um objeto. Por exemplo, imagine um DOM. Você tem que criar uma abundância de nós e atributos para obter o seu objeto final. A fábrica é utilizada quando a fábrica pode facilmente criar o objeto inteiro dentro de uma chamada de método.

Um exemplo do uso de um construtor é um edifício de um documento XML, eu usei este modelo quando a construção de fragmentos de HTML, por exemplo, eu poderia ter um construtor para a construção de um tipo específico de mesa e ele pode ter os seguintes métodos (parâmetros não são mostrados) :

BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()

Este construtor, então cuspir o HTML para mim. Isto é muito mais fácil de ler do que caminhar através de um método processual grande.

Confira Builder Padrão na Wikipedia .

Outras dicas

Abaixo estão algumas razões defendendo a utilização do código padrão e exemplo, em Java, mas é uma implementação do padrão Builder coberta pelo Gang of Four no Design Patterns . As razões que você usaria em Java são também aplicáveis ??a outras linguagens de programação também.

Como Joshua Bloch afirma em Effective Java, 2nd Edition :

O padrão do construtor é uma boa escolha ao projetar classes cujos construtores ou fábricas estáticas teria mais do que um punhado de parâmetros.

Nós temos tudo em algum momento encontrou uma classe com uma lista de construtores, onde cada adição adiciona um novo parâmetro de opção:

Pizza(int size) { ... }        
Pizza(int size, boolean cheese) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }

Isto é chamado o padrão telescópica Constructor. O problema com este padrão é que uma vez que os construtores são 4 ou 5 parâmetros de tempo torna-se difícil lembrar o necessário ordem dos parâmetros , bem como o determinado construtor que você pode querer em uma determinada situação.

Um alternativa você tem que o padrão telescópica Construtor é o JavaBean padrão onde você chamar um construtor com os parâmetros obrigatórios e, em seguida, chamar qualquer setters opcionais depois:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

O problema aqui é que, porque o objeto é criado ao longo de várias chamadas que pode estar em um parcialmente estado inconsistente através de sua construção. Isso também requer uma grande quantidade de esforço extra para garantir a segurança do thread.

A melhor alternativa é usar o padrão Builder.

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

    public Pizza build() {
      return new Pizza(this);
    }
  }

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

Note que Pizza é imutável e que os valores dos parâmetros são todos em um único local . Porque métodos setter do construtor retornar o objeto Builder são capaz de ser encadeadas .

Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

Isso resulta em código que é fácil escrever e muito fácil de ler e entender. Neste exemplo, o método de construção poderia ser modificado para verificar os parâmetros depois de terem foi copiado do construtor para o objeto Pizza e lançar uma IllegalStateException se um valor de parâmetro inválido foi fornecido. Este padrão é flexível e é fácil adicionar mais parâmetros para ele no futuro. É realmente útil apenas se você vai ter mais do que 4 ou 5 parâmetros para um construtor. Dito isto, pode valer a pena, em primeiro lugar se você suspeitar que você pode estar adicionando mais parâmetros no futuro.

Eu emprestado pesadamente sobre este tema do livro Effective Java, 2nd Edition por Joshua Bloch. Para saber mais sobre esse padrão e outras práticas Java eficazes eu recomendo.

Considere um restaurante. A criação de "refeição de hoje" é um padrão de fábrica, porque você dizer a cozinha "me refeição de hoje" ea cozinha (de fábrica) decide o objeto para gerar, com base em critérios escondidos.

O construtor aparece se você pedir uma pizza personalizada. Neste caso, o garçom diz o chef (construtor) "Eu preciso de uma pizza;! Cheese Adicionar, cebolas e bacon para ele" Assim, o construtor expõe os atributos do objeto gerado deve ter, mas esconde como configurá-los.

.NET StringBuilder classe é um grande exemplo de padrão do construtor. É usado principalmente para criar uma string em uma série de etapas. O resultado final você começa a fazer ToString () é sempre uma cadeia, mas a criação dessa cadeia varia de acordo com o que foram utilizadas funções da classe StringBuilder. Para resumir, a idéia básica é a construção de objetos complexos e esconder os detalhes de implementação de como ele está sendo construído.

Para um problema de multi-threaded, precisávamos de um objeto complexo a ser construído para cada segmento. O objecto representado os dados a serem processados, e pode mudar dependendo da entrada de utilizador.

Podemos usar uma fábrica em vez disso? Sim

Por que não foi? Builder faz mais sentido eu acho.

As fábricas são utilizados para a criação de diferentes tipos de objetos que são do mesmo tipo básico (implementam a mesma interface ou classe base).

Builders construir o mesmo tipo de objeto mais e mais, mas a construção é dinâmico, por isso pode ser alterado em tempo de execução.

Ao atravessar framework Microsoft MVC, eu tenho um pensamento sobre padrão do construtor. Me deparei com o padrão na classe ControllerBuilder. Esta classe é para retornar a classe de fábrica controlador, que é então utilizado para controlador de compilação de concreto.

vantagem que eu vejo no uso padrão do construtor é que, você pode criar uma fábrica de seu próprio país e conecte-o ao quadro.

@Tetha, pode haver um restaurante (Framework), gerido pela cara italiano, que serve pizza. A fim de preparar a pizza cara italiano (Object Builder) usa Owen (Factory) com uma base de pizza (classe base).

Agora cara indiano assume o restaurante de cara italiano. restaurante indiano servidores (Framework) Dosa em vez de pizza. A fim de preparar dosa cara (construtor de objeto) indiana usa Frying Pan (Factory) com um Maida (classe base)

Se você olhar para cenário, a comida é diferente, como a comida é preparada é diferente, mas no mesmo restaurante (sob mesmo quadro). Restaurante deve ser construído de tal forma que ele pode suportar chineses, mexicanos ou qualquer cozinha. construtor de objeto dentro facilita-quadro para plugin tipo de cozinha que você deseja. por exemplo

class RestaurantObjectBuilder
{
   IFactory _factory = new DefaultFoodFactory();

   //This can be used when you want to plugin the 
   public void SetFoodFactory(IFactory customFactory)
   {
        _factory = customFactory;
   }

   public IFactory GetFoodFactory()
   {
      return _factory;
   }
}

Você usá-lo quando você tem muitas opções para lidar com eles. Pense em coisas como jMock:

m.expects(once())
    .method("testMethod")
    .with(eq(1), eq(2))
    .returns("someResponse");

Ela se sente muito mais natural e é ... possível.

Há também edifício xml, construção corda e muitas outras coisas. Imagine se java.util.Map tinha colocado como um construtor. Você poderia fazer coisas como esta:

Map<String, Integer> m = new HashMap<String, Integer>()
    .put("a", 1)
    .put("b", 2)
    .put("c", 3);

Com base nas respostas anteriores (trocadilho intencional), um excelente exemplo do mundo real é Groovy 's suporte embutido para Builders.

Builders na Documentação Groovy

Outra vantagem do construtor é que, se você tem uma fábrica, ainda há algum acoplamento em você código, porque para a fábrica para trabalhar, tem que conhecer todos os objetos que podem, eventualmente, criar . Se você adicionar outro objeto que poderia ser criado, você terá que modificar a classe de fábrica para incluí-lo. Isso acontece na Abstract Factory também.

Com o construtor, por outro lado, você apenas tem que criar um novo construtor concreto para essa nova classe. A classe diretor vai ficar na mesma, porque recebe o construtor no construtor.

Além disso, existem muitos sabores de construtor. Kamikaze Mercenary`s dá outra.

/// <summary>
/// Builder
/// </summary>
public interface IWebRequestBuilder
{
    IWebRequestBuilder BuildHost(string host);

    IWebRequestBuilder BuildPort(int port);

    IWebRequestBuilder BuildPath(string path);

    IWebRequestBuilder BuildQuery(string query);

    IWebRequestBuilder BuildScheme(string scheme);

    IWebRequestBuilder BuildTimeout(int timeout);

    WebRequest Build();
}

/// <summary>
/// ConcreteBuilder #1
/// </summary>
public class HttpWebRequestBuilder : IWebRequestBuilder
{
    private string _host;

    private string _path = string.Empty;

    private string _query = string.Empty;

    private string _scheme = "http";

    private int _port = 80;

    private int _timeout = -1;

    public IWebRequestBuilder BuildHost(string host)
    {
        _host = host;
        return this;
    }

    public IWebRequestBuilder BuildPort(int port)
    {
        _port = port;
        return this;
    }

    public IWebRequestBuilder BuildPath(string path)
    {
        _path = path;
        return this;
    }

    public IWebRequestBuilder BuildQuery(string query)
    {
        _query = query;
        return this;
    }

    public IWebRequestBuilder BuildScheme(string scheme)
    {
        _scheme = scheme;
        return this;
    }

    public IWebRequestBuilder BuildTimeout(int timeout)
    {
        _timeout = timeout;
        return this;
    }

    protected virtual void BeforeBuild(HttpWebRequest httpWebRequest) {
    }

    public WebRequest Build()
    {
        var uri = _scheme + "://" + _host + ":" + _port + "/" + _path + "?" + _query;

        var httpWebRequest = WebRequest.CreateHttp(uri);

        httpWebRequest.Timeout = _timeout;

        BeforeBuild(httpWebRequest);

        return httpWebRequest;
    }
}

/// <summary>
/// ConcreteBuilder #2
/// </summary>
public class ProxyHttpWebRequestBuilder : HttpWebRequestBuilder
{
    private string _proxy = null;

    public ProxyHttpWebRequestBuilder(string proxy)
    {
        _proxy = proxy;
    }

    protected override void BeforeBuild(HttpWebRequest httpWebRequest)
    {
        httpWebRequest.Proxy = new WebProxy(_proxy);
    }
}

/// <summary>
/// Director
/// </summary>
public class SearchRequest
{

    private IWebRequestBuilder _requestBuilder;

    public SearchRequest(IWebRequestBuilder requestBuilder)
    {
        _requestBuilder = requestBuilder;
    }

    public WebRequest Construct(string searchQuery)
    {
        return _requestBuilder
        .BuildHost("ajax.googleapis.com")
        .BuildPort(80)
        .BuildPath("ajax/services/search/web")
        .BuildQuery("v=1.0&q=" + HttpUtility.UrlEncode(searchQuery))
        .BuildScheme("http")
        .BuildTimeout(-1)
        .Build();
    }

    public string GetResults(string searchQuery) {
        var request = Construct(searchQuery);
        var resp = request.GetResponse();

        using (StreamReader stream = new StreamReader(resp.GetResponseStream()))
        {
            return stream.ReadToEnd();
        }
    }
}

class Program
{
    /// <summary>
    /// Inside both requests the same SearchRequest.Construct(string) method is used.
    /// But finally different HttpWebRequest objects are built.
    /// </summary>
    static void Main(string[] args)
    {
        var request1 = new SearchRequest(new HttpWebRequestBuilder());
        var results1 = request1.GetResults("IBM");
        Console.WriteLine(results1);

        var request2 = new SearchRequest(new ProxyHttpWebRequestBuilder("localhost:80"));
        var results2 = request2.GetResults("IBM");
        Console.WriteLine(results2);
    }
}

Eu sempre não gostou do padrão Builder como pesado alguma coisa, intrusivos e muitas vezes abusado por programadores menos experientes. É um padrão que só faz sentido se você precisa para montar o objeto de alguns dados que requer uma etapa de pós-inicialização (ou seja, uma vez que todos os dados são coletados - fazer algo com ele). Em vez disso, em 99% dos construtores de tempo são simplesmente usados ??para inicializar os membros da classe.

Em tais casos, é muito melhor do que simplesmente declarar setters tipo withXyz(...) dentro da classe e fazê-los retornar uma referência a si mesmo.

Considere o seguinte:

public class Complex {

    private String first;
    private String second;
    private String third;

    public String getFirst(){
       return first; 
    }

    public void setFirst(String first){
       this.first=first; 
    }

    ... 

    public Complex withFirst(String first){
       this.first=first;
       return this; 
    }

    public Complex withSecond(String second){
       this.second=second;
       return this; 
    }

    public Complex withThird(String third){
       this.third=third;
       return this; 
    }

}


Complex complex = new Complex()
     .withFirst("first value")
     .withSecond("second value")
     .withThird("third value");

Agora nós temos uma classe única puro que gerencia sua própria inicialização e faz praticamente o mesmo trabalho que o construtor, exceto que seu muito mais elegante.

Eu costumava construtor na biblioteca de mensagens home-grown. O núcleo biblioteca foi receber dados a partir do fio, coletando-lo com instância Builder, então, uma vez Builder decidiu it've tem tudo de que precisava para criar uma instância de Mensagem, Builder.GetMessage () foi a construção de uma instância de mensagem usando os dados recolhidos a partir do fio.

Quando eu queria usar o XMLGregorianCalendar padrão para o meu XML para objeto mobilização de DateTime em Java, eu ouvi um monte de comentários sobre como peso pesado e pesado era usá-lo. Eu estava tentando comtrol campos XML nos xs:. De data e hora estruturas para gerenciar fuso horário, milissegundos, etc

Então, eu projetei um utilitário para construir um calendário XMLGregorian de um GregorianCalendar ou java.util.Date.

Por causa de onde eu trabalho eu não tenho permissão para compartilhá-lo online sem legal, mas aqui está um exemplo de como um cliente usa. Ele abstrai os detalhes e filtra algumas das implementações de XMLGregorianCalendar que são menos usados ??para xs:. Datetime

XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();

Dado este padrão é mais de um filtro como ele define campos na xmlCalendar como indefinido para que eles sejam excluídos, ainda "constrói"-lo. Eu facilmente adicionadas outras opções para o construtor para criar um xs: data, e xs:. Struct tempo e também para manipular deslocamentos de fuso horário quando necessário

Se você já viu um código que cria e utiliza XMLGregorianCalendar, você iria ver como isso tornou muito mais fácil de manipular.

Confira InnerBuilder, um plugin IDEA IntelliJ que adiciona ação um 'Builder' para o menu Gerar (Alt + Insert) que gera uma classe construtor interior como descrito no Effective Java

https://github.com/analytically/innerbuilder

Um grande exemplo do mundo real é usar quando a unidade testar suas classes. Você usa SUT (teste Em Sistema) construtores.

Exemplo:

Class:

public class CustomAuthenticationService
{
    private ICloudService _cloudService;
    private IDatabaseService _databaseService;

    public CustomAuthenticationService(ICloudService cloudService, IDatabaseService databaseService)
    {
        _cloudService = cloudService;
        _databaseService = databaseService;
    }

    public bool IsAuthorized(User user)
    {            
        //Implementation Details
        return true;

}

Test:

    [Test]
    public void Given_a_User_With_Permission_When_Verifying_If_Authorized_Then_Authorize_It_Returning_True()
    {
        CustomAuthenticationService sut = new CustomAuthenticationServiceBuilder();
        User userWithAuthorization = null;

        var result = sut.IsAuthorized(userWithAuthorization);

        Assert.That(result, Is.True);
    }

sut Builder:

public class CustomAuthenticationServiceBuilder
{
    private ICloudService _cloudService;
    private IDatabaseService _databaseService;

    public CustomAuthenticationServiceBuilder()
    {
        _cloudService = new AwsService();
        _databaseService = new SqlServerService();
    }

    public CustomAuthenticationServiceBuilder WithAzureService(AzureService azureService)
    {
        _cloudService = azureService;

        return this;
    }

    public CustomAuthenticationServiceBuilder WithOracleService(OracleService oracleService)
    {
        _databaseService = oracleService;

        return this;
    }

    public CustomAuthenticationService Build()
    {
        return new CustomAuthenticationService(_cloudService, _databaseService);
    }

    public static implicit operator CustomAuthenticationService (CustomAuthenticationServiceBuilder builder)
    {
        return builder.Build();
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top