Muitas pessoas acham que métodos hook e o padrão Template Method são a mesma coisa. A grande diferença é que os métodos hook são uma técnica para permitir a extensão de comportamento e o Template Method, como um padrão, é uma solução para um problema mais específico.
Seria correto dizer que o Template Method utiliza métodos hook em sua solução. O que é importante perceber é que o conceito de métodos hook é mais geral e inclusive é utilizado por outros padrões, como Factory Method, Strategy, Bridge e Observer.
A herança é uma das principais funcionalidades de linguagens orientadas a objetos.
É a partir dela que é possível grande parte do potencial de reuso.
O problema é que muita gente que diz isso para por aí ainda busca o reuso através da herança de forma errada:
O primeiro pensamento que normalmente temos ao se estender uma classe é reutilizar a lógica da superclasse na subclasse. Será que isso é mesmo verdade? O que uma subclasse pode reutilizar da superclasse?
A utilização dos métodos da superclasse pela subclasse é equivalente ao reuso de funções na programação estruturada, não sendo grande novidade. O potencial de reuso possível com herança está em outro local!
Ele pode estar sim no reuso de código da superclasse, porém não é com a subclasse chamando métodos da superclasse, mas com a superclasse chamando código da subclasse.
Quando um método da superclasse chama um método que pode ou deve ser implementado na subclasse, isso permite que um mesmo algoritmo possa ser reutilizado com passos alterados.
Essa flexibilidade aumenta o potencial de reutilização, pois permite a sua adaptação para necessidades mais específicas.
Outro local onde o código pode ser reusado é nas classes que utilizam uma variável com o tipo da superclasse. Nesse caso, como as instâncias das subclasses podem ser atribuídas a essa variável, é possível adaptar o comportamento segundo a instância utilizada. Resumindo em apenas uma palavra: polimorfismo!
O objetivo de diversos padrões de projeto é mostrar como a herança pode ser utilizada em um design orientado a objetos para permitir adaptação de comportamento e consequentemente um maior reuso.
Exemplo: o algoritmo de ordenação de listas pode ser reutilizado em diversas aplicações para ordenação de listas de diversas classes. Isso só é possível pois todas as classes compartilham a mesma abstração do tipo Comparable.
Herança do jeito certo: quebrando classe única com método grande e complexo
No desenvolvimento de aplicações desktop em Java Swing, ao se criar uma nova tela para aplicação, uma dúvida comum é se essa classe deve estender a classe JPanel ou simplesmente utilizá-la.
A extensão de JPanel pode fazer sentido a princípio, visto que a nova classe será um painel (obedecendo a famosa regra "é um") e que pode ser adicionada em uma interface gráfica.
Por outro lado, se pensarmos nos princípios da herança, uma subclasse deve poder ser utilizada no lugar de sua superclasse.
Raciocinando dessa forma, é muito fácil pensar em diversas situações em que a classe com a tela de uma aplicação não faria sentido de ser utilizada no lugar de um painel genérico.
Isso também quebraria o contrato da superclasse, visto que diversos métodos, como para adição de componentes e configuração de layout deixariam de fazer sentido.
Somente verificar se a subclasse é uma superclasse pode não ser suficiente.
Antes de utilizar herança verifique se o polimorfismo faz sentido, ou seja, se qualquer subclasse pode ser utilizada no lugar da superclasse. Em caso negativo, isso é um indício de que a herança está sendo utilizada de forma inadequada. Esse é conhecido como o Princípio de Substituição de Liskov, que defende que se uma classe é um subtipo de outra, então os objetos dessa classe podem ser substituídos pelos objetos do subtipo sem que seja necessário alterar as propriedades do programa.
Herança do jeito certo: criando superclasse para duas classes com código duplicado
Um importante uso que pode ser feito da herança é para permitir a especialização de comportamento. Dessa forma, a superclasse pode fornecer uma base para uma determinada funcionalidade, a qual invoca um método que somente é definido pela superclasse.
Esse método funciona como um ponto de extensão do sistema é chamado de método hook ou operação gancho, (do inglês, hook method).
Conceito de um método hook
A superclasse possui um método principal público que é invocado por clientes.
Esse método delega parte de sua execução para o método hook, que é um método abstrato que deve ser implementado pela subclasse. Ele funciona como um gancho no qual uma nova lógica de execução para a classe pode ser acoplada.
Cada subclasse o implementa provendo uma lógica diferente. Como essa lógica pode ser invocada a partir do mesmo método público, definido na superclasse, os métodos hook permitem que o objeto possua um comportamento diferente de acordo com a subclasse instanciada.
O principal padrão que utiliza métodos hook como técnica é o Template Method. Este padrão é aplicável quando se deseja definir um algoritmo geral, que estabelece uma série de passos variáveis para cumprir um requisito da aplicação. Como seus passos podem variar, é desejável que a estrutura da implementação forneça uma forma para que eles sejam facilmente substituídos.
Essa prática é muito utilizada em frameworks para permitir que as aplicações possam especializar seu comportamento para seus requisitos.
Nesse caso, o framework provê algumas classes com métodos hook methods que precisam ser especializadas pela aplicação.
Sendo assim, a aplicação deve estender essa classe e implementar o método hook de forma a inserir o comportamento específico de seu domínio.