Template Method ou Chain of Responsibility?

5 respostas
D

Pessoal

Tenho que fazer o seguinte:

Minha aplicação tem que gerar documentos, que podem ser de dois tipos. Um completo e um resumido, com praticamente as mesmas informações.

Estou pensando em o que é melhor fazer neste caso. Implementar isto utilizando Template Method, mais ou menos desta forma:

public abstract class GeraDocumento {
   public final void gera(){
      geraSecao1();
      geraSecao2();
      geraSecao3();
   }

   public abstract void geraSecao1();
   public abstract void geraSecao2();
   public abstract void geraSecao3();
}

public class GeraDocumentoResumido extends GeraDocumento {
   public void geraSecao1(){
      // faz alguma coisa
   }

   public void geraSecao2(){
      // nao faz nada, o resumido nao tem esta secao...
   }

   public void geraSecao3(){
      // faz alguma coisa
   }
}

public class GeraDocumentoCompleto extends GeraDocumento {
   public void geraSecao1(){
      // faz alguma coisa
   }

   public void geraSecao2(){
      // faz alguma coisa
   }

   public void geraSecao3(){
      // faz alguma coisa
   }
}

Na minha opinião, isto parece bom, a não ser pelo fato de que na classe GeraDocumentoResumido, alguns métodos estarão em branco (só estarão lá porque precisam ser implementados, pois são abstratos na classe pai) e não farão nada…

Por outro lado, se usasse o Chain of Responsibility, eu poderia definir o que é chamado, mais ou menos assim:

public class GeraSecao1 extends GeraDocumento {
   private GeraDocumento sucessor;

   public void gera(){
      // faz alguma coisa

      if(sucessor != null)
         sucessor.gera();
   }

   public void setSucessor(GeraDocumento sucessor){
      this.sucessor = sucessor;
   }
}

public class GeraSecao2 extends GeraDocumento {
   private GeraDocumento sucessor;

   public void gera(){
      // faz alguma coisa

      if(sucessor != null)
         sucessor.gera();
   }

   public void setSucessor(GeraDocumento sucessor){
      this.sucessor = sucessor;
   }
}

public class GeraSecao3 extends GeraDocumento {
   private GeraDocumento sucessor;

   public void gera(){
      // faz alguma coisa

      if(sucessor != null)
         sucessor.gera();
   }

   public void setSucessor(GeraDocumento sucessor){
      this.sucessor = sucessor;
   }
}

public class GeraDocumentoCompleto {
   public void gera(){
      GeraDocumento g1 = new GeraSecao1();
      GeraDocumento g2 = new GeraSecao2();
      GeraDocumento g3 = new GeraSecao3();

      g1.setSucessor(g2);
      g2.setSucessor(g3);
   }
}

public class GeraDocumentoResumido {
   public void gera(){
      GeraDocumento g1 = new GeraSecao1();
      GeraDocumento g3 = new GeraSecao3();

      g1.setSucessor(g3);
   }
}

Isto também parece bom, pois me permite chamar somente aquilo que eu quero, na ordem que eu quero, mas o ponto negativo é que algumas seções são diferentes (pequenas diferenças) entre a versão completa e a resumida. A mesma versão apresenta informações diferentes, em alguns casos (poucos, mas existem) e, neste caso, eu teria que criar muitas classes a mais e talvez duplicar código ou fazer um monte de condicionais.

O que vocês acham a respeito? O que vocês fariam neste caso?

Obrigado

5 Respostas

pablosaraiva

Eu utilizaria o Template Method.

Não há problema algum com métodos que não fazem nada quando eles realmente não tem nada a fazer. :slight_smile:

tnaires
Que tal usar decorator?
interface Documento {
	void gerarConteudo();
}
Os tipos de documento:
class DocumentoCompleto implements Documento {
	DocumentoCompleto() {

	}

	void gerarConteudo() {
		// Gera conteúdo completo.
	}
}

class DocumentoResumido implements Documento {
	DocumentoResumido() {

	}

	void gerarConteudo() {
		// Gera conteúdo resumido.
	}
}
Representa uma seção do documento:
abstract class SecaoDocumento extends Documento {
	private Documento documento;

	SecaoDocumento(Documento documento) {
		this.documento = documento;
	}

	protected documento() {
		return this.documento;
	}
}
As seções que decorarão o documento:
class Secao1 extends SecaoDocumento {
	Introducao(Documento documento) {

	}

	public void gerarConteudo() {
		// Gera a seção 1
		super.documento().gerarConteudo();
	}
}

class Secao2 extends SecaoDocumento {
	Desenvolvimento(Documento documento) {

	}

	public void gerarConteudo() {
		// Gera a seção 2
		super.documento().gerarConteudo();
	}
}

class Secao3 extends SecaoDocumento {
	Conclusao(Documento documento) {

	}

	public void gerarConteudo() {
		// Gera a seção 3
		super.documento().gerarConteudo();
	}
}
USO:
// Documento completo com as 3 seções.
Documento documento = new Secao1(new Secao2(new Secao3(new DocumentoCompleto())));
documento.gerarConteudo();

// Documento resumido com apenas duas seções.
Documento documento = new Secao3(new Secao1(new DocumentoResumido()));
documento.gerarConteudo();
As seções podem ser geradas na ordem que você quiser, na quantidade que você precisar, e os detalhes específicos dos documentos podem ser gerados nas classes que os representam.
ctosin

Se você quiser utilizar o template method, nada impede também que você declare métodos como não abstratos e não os sobrescreva nas subclasses. Daí você pode sobrescrever só os que realmente precisam ser redefinidos.

Abraço

tnaires

É uma opção também, mas se a ordem das seções importar talvez não seja a melhor.

sergiotaborda

Um documento é composto por seções. Isto deve indicar o uso do padrão composite. Em particular a composição em arvore.
documento tem secções que tem paragrafos, etc… o nivel de detalhe vc escolhe. mas pelo menos documento e secção tem que ter.

Contudo o que se pretende é escrever as seções de forma diferente. Então precisamos de uma composição de escritores que comporão o documento.

public class Document {

    public void addSection(Section section);
    public void removeSection(Section section);
    public Section root();
}


public interface DocumentAppender {

  public void appendTo (Document document);
}

public class DocumentWriter { // composite de DocumentAppender

  public DocumentWriter  addAppender(DocumentAppender  appender){
     this.appenders.add(appender);
     return this; // chain method, aka interface fluente
  }

  public Document write(){
          Document doc = new Document();
 
          this.appenderTo(doc);

         return doc;
  }

  public void appendTo (Document document){

       for(DocumentAppender appender : appenders ){
           appender.appendTo(document);
     }
  }

}


public void Secao1Appender implements DocumentAppender {

  public void appendTo (Document document){
         Secao s = new Secao("titulo");
        s.setText("olá");
         
        document. addSection(s);
  }

}

public class DocumentoCompletoWriter extends DocumentWriter{

     DocumentoCompletoWriter (){
       // define como vai ser a estrutura
     this.addAppender(new Secao1Appender())
    .addAppender(new Secao2Appender())
    .addAppender(new Secao3Appender())
    }
}

// uso 

Document doc = new DocumentoCompletoWriter ().write();
Criado 11 de dezembro de 2009
Ultima resposta 17 de dez. de 2009
Respostas 5
Participantes 5