Ainda não tem acesso? Estude com a gente! Matricule-se
Ainda não tem acesso? Estude com a gente! Matricule-se

Não seria mais fácil tornar o set das propriedades publico?

Mesmo que se queira manter o encapsulamento na classe NotaFiscal, não vejo razão para fazer o mesmo na classe NotaFiscalBuilder. De um jeito ou de outro, ela tem funções públicas que setam suas propriedades, o que dá na mesma que ter um set público.

Feito isso, você poderia fazer simplesmente:

        NotaFiscalBuilder builder = new NotaFiscalBuilder()
        {
            RazaoSocial = "Caelum",
            Cnpj = "123.456.789/0001-10",
            Data = DateTime.Now,
            Observacoes = "Nada a observar..."
        };

        builder.addItem(item1);
        builder.addItem(item2);

Bem mais simples, e com bem menos código, e sem necessidade de usar Method Chaining.

Outro detalhe é que métodos como o ComItem, que aqui eu chamei de AddItem, geralmente seriam alimentados via foreach, a partir de alguma lista, e não um por um, na mão, como é feito no exemplo. Isso por si só já quebraria a corrente.

1 resposta

A idéia do padrão builder é encapsular a lógica de construção de um objeto complexo e é comum se utilizar o padrao method chaining para tornar o código um pouco mais dinamicamente verboso.

Porém, isso é uma combinação de padrões e padrões devem ser usados somente se fizer sentido! Em alguns casos, o method chaining não faz sentido, então pode ser que não seja uma boa idéia utilizá-lo.

Sobre os atributos públicos, é questão de boa prática você não deixar seus atributos públicos para edição, mas também é uma questão de caso a caso.

Você pode deixar um atributo String publico, mas se quiser fazer operações adicionais antes de armazenar, o adequado é torná-lo privado e utilizar o setter. Faça uma combinação de modo que se adeque ao seu caso.

class NotaFiscalBuilder {
  public string razaoSocial;
  private string cnpj;
  private string observacoes = "";
  public void WithCnpj(string cnpj) {
    this.cnpj = cnpj.trim();
  }
  public void AddObservacao(string obs) {
    this.observacoes += obs + "\n";
  }
}

NotaFiscalBuilder builder = new NotaFiscalBuilder()
{
  razaoSocial = "Caelum"
};
builder.WithCnpj("123.456.789/0001-10");
builder.AddObservacao("Uma observação");
builder.AddObservacao("Outra observação");

Sobre o chain quebrar um foreach, diria que é possível sim fazer. Digamos que o builder esteja todo com chain:

NotaFiscalBuilder builder = new NotaFiscalBuilder()
  .WithRazaoSocial("Caelum")
  .WithCnpj("123456789000110");
foreach (NotaFiscalItem item in items) {
  builder.AddItem(item)
    .AddObservacao("Item adicionado: "+item);
}
builder.AddObservacao("Uma obs")
  .AddObservacao("Outra obs");
NotaFiscal nf = builder.Build();

Repare que podemos quebrar o method chaining quando quisermos, pois o atributo já está salvo nos atributos privados do builder.

Concluindo: no final do dia, você deve seguir o que for mais adequado ao seu caso particular e aos padrões de desenvolvimento da equipe. Os design patterns são soluções prontas para problemas comuns, e não precisam ser utilizados em todos os casos, podendo inclusive ser problemático utilizar padrões inadequadamente.

Espero ter conseguido esclarecer a idéia e resolver sua dúvida.

Qualquer coisa, continue em contato!

Um abraço e bons estudos.