Imagine que você está desenvolvendo um sistema de notificação para uma aplicação que precisa enviar diferentes tipos de notificações para os usuários, como e-mail, SMS e notificações push. Cada tipo de notificação tem uma lógica de envio diferente e requer diferentes configurações.
Se você implementasse a criação dessas notificações diretamente na lógica do sistema, o código se tornaria rígido e difícil de manter, especialmente quando novos tipos de notificações precisarem ser adicionados. Além disso, a criação das instâncias estaria acoplada à lógica da aplicação, o que não é ideal.
O padrão Factory Method é útil neste cenário, pois ele define uma interface para a criação de objetos em uma superclasse, mas permite que as subclasses decidam qual classe concreta instanciar. Isso desacopla o código que usa as classes dos detalhes de sua criação, tornando o sistema mais flexível e fácil de estender.
Aqui está um exemplo de como implementar o padrão Factory Method em Java:
// Interface Notification
public interface INotification {
//1
void notifyUser();
}- Define o contrato (método) que todas as classes concretas devem seguir.
//1
public class EmailNotification implements INotification {
@Override
public void notifyUser() {
System.out.println("Enviando notificação por e-mail📧");
}
}
//1
public class SMSNotification implements INotification {
@Override
public void notifyUser() {
System.out.println("Enviando notificação por SMS💬");
}
}
//1
public class PushNotification implements INotification {
@Override
public void notifyUser() {
System.out.println("Enviando notificação push📲");
}
}- Criação da classe concreta, implementação da interface e definindo seu respectivo funcionamento.
public abstract class NotificationFactory {
public abstract INotification createNotification();
}- Classe abstrata NotificationFactory que define o método de criação.
//1
public class EmailNotificationFactory extends NotificationFactory {
@Override
public INotification createNotification() {
return new EmailNotification();
}
}
//1
public class SMSNotificationFactory extends NotificationFactory {
@Override
public INotification createNotification() {
return new SMSNotification();
}
}
//1
public class PushNotificationFactory extends NotificationFactory {
@Override
public INotification createNotification() {
return new PushNotification();
}
}- Respectiva subclasse concreta para criação de cada implementação disponível.
No sistema de notificação:
public class NotificationService {
public static void main(String[] args) {
//1
NotificationFactory emailFactory = new EmailNotificationFactory();
INotification emailNotification = emailFactory.createNotification();
//2
NotificationFactory smsFactory = new SMSNotificationFactory();
INotification smsNotification = smsFactory.createNotification();
//3
NotificationFactory pushFactory = new PushNotificationFactory();
INotification pushNotification = pushFactory.createNotification();
//4
emailNotification.notifyUser();
smsNotification.notifyUser();
pushNotification.notifyUser();
//5
}
}- Cria uma factory para e-mail.
- Cria uma factory para SMS.
- Cria uma factory para push.
- Utilizando a implementação concreta definida na subclass.
- Exemplo acima é simples, porém estaríamos instanciando a classe concreta dinamicamente, com base em interações do usuário, não instanciando todas de uma vez.
-
Interface
INotification: Define o contrato que todas as notificações devem seguir. Isso garante que cada tipo de notificação implementa o métodonotifyUser. -
Classes concretas (
EmailNotification,SMSNotification,PushNotification): Implementam a interfaceINotificatione contêm a lógica específica para enviar cada tipo de notificação. -
Classe
NotificationFactory: É uma classe abstrata que define o métodocreateNotification. Este método é um "factory method" que as subclasses devem implementar para criar instâncias deINotification. -
Subclasses de
NotificationFactory: Cada uma das subclasses (EmailNotificationFactory,SMSNotificationFactory,PushNotificationFactory) implementa o métodocreateNotificationpara retornar uma instância do tipo apropriado de notificação. -
Desacoplamento: O uso do padrão Factory Method permite que o sistema de notificação seja estendido facilmente. Se um novo tipo de notificação precisar ser adicionado, basta criar uma nova classe concreta que implementa
INotificatione uma nova factory correspondente, sem modificar o código existente.
Factory Method é útil quando:
- Você deseja permitir que subclasses decidam qual classe concreta instanciar.
- Você precisa de flexibilidade para adicionar novos tipos de objetos sem modificar o código existente.
- Você deseja evitar acoplamento rígido entre a lógica de aplicação e a lógica de criação de objetos.
- Java Logging API: A API de logging do Java (
java.util.logging) usa o padrão para criar instâncias deLoggereHandler. O método estático Logger.getLogger() e os métodos de fábrica de Handler permitem criar e configurar loggers e manipuladores específicos. - Apache HttpClient: Utiliza para criar instâncias de
HttpClient. A classeHttpClientBuilderfornece métodos de fábrica para configurar e construir instâncias de HttpClient com diferentes configurações e políticas. - JUnit: O framework de testes JUnit utiliza o padrão Factory Method para criar instâncias de testes. A classe
TestFactoryou métodos de fábrica em implementações de teste permitem a criação de diferentes tipos de testes e suas configurações. - Jackson (Lib de parse JSON): Utilizado para criar instâncias de
ObjectMapper. A classe ObjectMapper fornece métodos de fábrica para criar e configurar objetos para serialização e desserialização de JSON.
Esse padrão é frequentemente utilizado em frameworks onde as bibliotecas precisam criar objetos, mas a aplicação deve decidir a classe concreta a ser usada. Por exemplo, em bibliotecas GUI (interfaces gráficas de usuário), onde o framework pode precisar criar janelas ou botões, mas a aplicação decide a aparência e o comportamento exatos desses componentes.