<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rafael Lemes</title>
    <description>The latest articles on DEV Community by Rafael Lemes (@nenhumrafael).</description>
    <link>https://dev.to/nenhumrafael</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F183471%2Fd87d3c06-e18f-48f5-b888-b9e12ce83ca7.jpg</url>
      <title>DEV Community: Rafael Lemes</title>
      <link>https://dev.to/nenhumrafael</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nenhumrafael"/>
    <language>en</language>
    <item>
      <title>Strategy Pattern no Spring Boot Usando Enum</title>
      <dc:creator>Rafael Lemes</dc:creator>
      <pubDate>Fri, 21 Oct 2022 18:44:39 +0000</pubDate>
      <link>https://dev.to/nenhumrafael/strategy-pattern-no-spring-boot-usando-enum-4fg9</link>
      <guid>https://dev.to/nenhumrafael/strategy-pattern-no-spring-boot-usando-enum-4fg9</guid>
      <description>&lt;p&gt;Recentemente me passaram uma Feature que se encaixava muito bem com a utilização do &lt;a href="https://refactoring.guru/pt-br/design-patterns/strategy" rel="noopener noreferrer"&gt;Strategy Pattern&lt;/a&gt;, decidi colocar a mão na massa e logo me deparei com os seguintes desafios: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementar essa solução aproveitando os Recursos de &lt;a href="https://pt.wikipedia.org/wiki/Inje%C3%A7%C3%A3o_de_depend%C3%AAncia" rel="noopener noreferrer"&gt;Injeção de Dependência&lt;/a&gt; do Spring com &lt;a href="https://pt.wikipedia.org/wiki/Invers%C3%A3o_de_controle" rel="noopener noreferrer"&gt;Inversão de Controle&lt;/a&gt; mantendo-a o mais desacoplado possível&lt;/li&gt;
&lt;li&gt;Obter a instância gerenciada do Spring da implementação da Estratégia em tempo de execução&lt;/li&gt;
&lt;li&gt;Não a criar Instâncias da Estratégia que não será utilizada na memória desnecessariamente sobrecarregando a mesma.&lt;/li&gt;
&lt;li&gt;Criar um factory simples e de fácil extensão que irá entregar a instância da Estratégia baseado em um identificador que será passado como parâmetro em uma requisição REST&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como em uma pesquisa rápida, não achei nenhum material em português, resolvi acabar com o medo de escrever e criei um projetinho de exemplo.&lt;br&gt;
Para quem não tem paciência em ler artigos e já quer ver o código é só clicar em: &lt;a href="https://github.com/rafaellemes/enum-strategy-java-spring-boot" rel="noopener noreferrer"&gt;Talk is Cheap. Show me the Code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Antes de Seguirmos, gostaria de deixar claro que esse texto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assume que o leitor já tenha um conhecimento médio ou Familiaridade com Java e Spring Boot&lt;/li&gt;
&lt;li&gt;Não tem a intenção de ensinar os conceitos básico do Design Pattern ou do Spring Boot&lt;/li&gt;
&lt;li&gt;Tem como objetivo ser uma referência direta e rápida de uma maneira de implementar o Pattern&lt;/li&gt;
&lt;li&gt;Explicar o motivo de algumas decisões de Design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos lá.&lt;/p&gt;

&lt;p&gt;Uma característica dessa Feature (Nesse caso é um requisito não funcional ligado ao design da aplicacão), é que essa 'Estratégia' só pode ser usado por seu 'Caso de Uso' correspondente, logo, para evitar o vazamento dessa abstração para outros casos de uso dentro da mesma aplicação, todas as classes e interfaces ficarão dentro do mesmo pacote, e com exceção da Interface 'FooUseCase' que será &lt;em&gt;'public'&lt;/em&gt;, as demais classes terão o modificador de acesso como 'default' de modo a restringir o acesso por outras classes fora do Pacote. &lt;br&gt;
&lt;strong&gt;Obs:&lt;/strong&gt; Esta é só uma forma de implementação não é uma restrição, se não houver esse tipo de 'característica' em sua feature, pode-se deixar todas as classes como &lt;em&gt;'public'&lt;/em&gt;).&lt;br&gt;
Segue abaixo a estrutura de Pacote:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tzk1mhzrz9jmdjr40pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tzk1mhzrz9jmdjr40pr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dito isso, vamos por partes:&lt;/p&gt;

&lt;p&gt;Primeiramente, cria-se uma Interface que irá representar o 'Contrato', em outras palavras, a estrutura/comportamento padrão da 'Estratégia' que será utilizado pelo 'Client'(Classe que irá efetivamente utilizá-la, no nosso caso a Classe que representa o Caso de uso em si)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package br.com.lemes.enumstrategy.usecase.foo;

interface FooStrategy {
    String execute();
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Em seguida, as classes que implementarão cada Estratégia:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package br.com.lemes.enumstrategy.usecase.foo;

import org.springframework.stereotype.Component;
@Component
final class FooStrategyImpl1 implements FooStrategy{

     @Override
     public String execute() {
         return "Foo Strategy Impl 1";
     }
 }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package br.com.lemes.enumstrategy.usecase.foo;

import org.springframework.stereotype.Component;

@Component
final class FooStrategyImpl2 implements FooStrategy{

     @Override
     public String execute() {
         return "Foo Strategy Impl 2";
     }
 }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package br.com.lemes.enumstrategy.usecase.foo;

import org.springframework.stereotype.Component;

@Component
final class FooStrategyImpl3 implements FooStrategy{

     @Override
     public String execute() {
         return "Foo Strategy Impl 3";
     }
 }



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Em seguida, vamos criar a Interface do UseCase e sua Classe Concreta que irá Implementa-la:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package br.com.lemes.enumstrategy.usecase.foo;

public interface FooUseCase {
    String call(String id);
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package br.com.lemes.enumstrategy.usecase.foo;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;

@Service
@RequiredArgsConstructor
final class FooUseCaseImpl implements FooUseCase {

    @NonNull
    private final ApplicationContext context;


    @Override
    public String call(String id){

        Objects.requireNonNull(id);
        FooEnumStrategy fooEnumStrategy = FooEnumStrategy
                .findById(id)
                .orElseThrow(IllegalArgumentException::new); //Could be a Business Exception

        FooStrategy impl = fooEnumStrategy.getImpl(context);

        return impl.execute();
    }



    private enum FooEnumStrategy{

        IMPL1("Foo1", FooStrategyImpl1.class),
        IMPL2("Foo2", FooStrategyImpl2.class),
        IMPL3("Foo3", FooStrategyImpl3.class);

        FooEnumStrategy(String id, Class&amp;lt;? extends FooStrategy&amp;gt; impl) {
            this.id = id;
            this.impl = impl;
        }

        final String id;
        final Class&amp;lt;? extends FooStrategy&amp;gt; impl;

        public static Optional&amp;lt;FooEnumStrategy&amp;gt; findById(String id){


            Objects.requireNonNull(id);
            Supplier&amp;lt;Stream&amp;lt;FooEnumStrategy&amp;gt;&amp;gt; fooEnumStrategyStream
                    = () -&amp;gt; Stream.of(values())
                    .filter(strategy -&amp;gt; strategy.id.equals(id));


            if(fooEnumStrategyStream.get().count() &amp;gt; 1){
                fooEnumStrategyStream
                        .get()
                        .findFirst()
                        .ifPresent(strategy -&amp;gt; {
                            throw new RuntimeException(
String.format("Same id For two or more different Strategy: %s ", strategy.id));
                        });
            }


            return fooEnumStrategyStream.get().findFirst();
        }

        public FooStrategy getImpl(ApplicationContext context) {

            return context.getBean(impl);
        }
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Considerações Importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foi uma decisão de design criar a &lt;em&gt;Enum _que servirá como Factory como uma 'Inner Class' com o modificador de acesso 'private', uma vez que, como fora supracitado, essas 'Estratégias' só devem ser usadas dentro do Caso de Uso. Também seria possível criar essa _Enum _em um arquivo separado mantendo o modificador dela como _'Default'&lt;/em&gt; mantendo-a inacessível para outras classes fora do pacote de forma a atender a restrição.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcq8ax6lvbhnzinixk0mm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcq8ax6lvbhnzinixk0mm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Todas as classes concretas estão marcadas como &lt;em&gt;'final'&lt;/em&gt; que é para garantir que não haja extensibilidade das mesmas. Cada 'Estratégia' deve ter sua própria regra, e se por ventura uma estratégia depender de outra, a ideia é que essa dependência seja explicitada em forma de composição, embora seja bem improvável a necessidade disso, mas dessa maneira ainda é possível aplicar o &lt;a href="https://refactoring.guru/design-patterns/proxy" rel="noopener noreferrer"&gt;Proxy Pattern&lt;/a&gt;.
Exemplo:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Component
final class FooStrategyImpl1 implements FooStrategy{

     @Override
     public String execute() {
         return "Foo Strategy Impl 1";
     }
 }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;A anotação &lt;em&gt;@RequiredArgsConstructor&lt;/em&gt; do Lombok cria um construtor em tempo de compilação que recebe como parâmetro as variáveis declaradas como final, obrigando o Injetor de dependência a construir a Classe de implementação com todas as suas dependências. Esta é uma maneira de implementar a Inversão de controle garantindo o Estado da Classe. &lt;strong&gt;Vale ressaltar que: Declarar a Variável utilizando o &lt;em&gt;@Autowired&lt;/em&gt; apenas indica para o Spring injetar a Dependência, isso não é inversão de controle e provavelmente deixará seus testes um pouco mais complicado&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxno3rrplogpvksjk8wm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxno3rrplogpvksjk8wm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foi decidido usar &lt;em&gt;Optional&lt;/em&gt; no método &lt;em&gt;findById&lt;/em&gt; da &lt;em&gt;FooEnumStrategy&lt;/em&gt; pois o cliente poderia passar por parâmetro para o &lt;em&gt;Enum&lt;/em&gt;  um id inexistente, dessa forma fica a cargo da classe que implementa o 'Caso de uso' decidir a melhor maneira de lidar caso não haja uma 'Estratégia'. No caso do exemplo em questão, está lançando um &lt;em&gt;IllegalArgumentException&lt;/em&gt; como pode-se ver abaixo, mas poderia ser um erro de negócio, ou retornar um 404 para o Client Rest, enfim, o que a feature pedir =).&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Override
    public String call(String id){

        Objects.requireNonNull(id);
        FooEnumStrategy fooEnumStrategy = FooEnumStrategy
                .findById(id)
                .orElseThrow(IllegalArgumentException::new); //Could be a Business Exception

        FooStrategy impl = fooEnumStrategy.getImpl(context);

        return impl.execute();
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Ao decidir &lt;strong&gt;não&lt;/strong&gt; utilizar a Ordenação natural do &lt;em&gt;Enum&lt;/em&gt;, conforme o projeto for evoluindo e novas 'Estratégias' forem sendo adicionada, corre o risco do desenvolvedor erroneamente criar dois itens do &lt;em&gt;Enum&lt;/em&gt; com o mesmo Identificador, gerando o que chamamos de 'Erro Silencioso' pois o &lt;em&gt;Enum&lt;/em&gt; pode entregar a 'Estratégia' errada para o Caso de Uso, em outras palavras, um bug que compilará normalmente e que pode ser percebido tarde demais pois não dará uma Exceção explicita. Portanto, para evitar isso o método &lt;em&gt;findById&lt;/em&gt; da &lt;em&gt;Enum&lt;/em&gt; faz uma validação da quantidade elementos com o id que foi passado por parâmetro e se houver mais que um lançará uma '&lt;em&gt;RuntimeException&lt;/em&gt;' descrevendo o erro e apontando qual identificador está repetido. 
Pode haver o caso de haver dois identificadores diferentes utilizando a mesma 'Estratégia' e não necessariamente ser um erro, nesse caso não consegui pensar em muito no que fazer =/
Exemplo:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public static Optional&amp;lt;FooEnumStrategy&amp;gt; findById(String id){


            Objects.requireNonNull(id);
            Supplier&amp;lt;Stream&amp;lt;FooEnumStrategy&amp;gt;&amp;gt; fooEnumStrategyStream
                    = () -&amp;gt; Stream.of(values())
                    .filter(strategy -&amp;gt; strategy.id.equals(id));


            if(fooEnumStrategyStream.get().count() &amp;gt; 1){
                fooEnumStrategyStream
                        .get()
                        .findFirst()
                        .ifPresent(strategy -&amp;gt; {
                            throw new RuntimeException(
String.format("Same id For two or more different Strategy: %s ", strategy.id));
                        });
            }


            return fooEnumStrategyStream.get().findFirst();
        }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;O método &lt;em&gt;getBean&lt;/em&gt; da &lt;em&gt;ApplicationContext&lt;/em&gt; que tu encontra no trecho abaixo: &lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

 public FooStrategy getImpl(ApplicationContext context) {

            return context.getBean(impl);
        }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;também aceita como parâmetro uma &lt;em&gt;String&lt;/em&gt; que representa o nome do Bean, Este também é um jeito de implementar esse 'Factory'. Ficaria mais fácil de aplicar um Proxy Pattern na 'Estratégia' utilizando os recursos da anotação @Qualifier sem precisar mexer na Enum, entretanto deixaria de ser 'Type Safe', permitindo passar qualquer bean ou até mesmo um nome errado, resultando em um erro de &lt;em&gt;ClassCastException&lt;/em&gt; ou &lt;em&gt;BeansException&lt;/em&gt; em tempo de Execução. Em todo caso, ainda é possível aplicar o Proxy Pattern mas precisaria alterar a Enum passando como parâmetro do Elemento a Classe do Proxy (não é o melhor dos mundos mas é o que tem pra hoje kkk). &lt;br&gt;
 por exemplo:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

IMPL4("Foo2", ProxyFooStrategyImpl2.class),


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bom, é isso. Disse que iria ser rápido mas percebi que escrevi demais. Paciência. --\O/--&lt;/p&gt;

&lt;p&gt;Segue Links para Referência:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rafaellemes/enum-strategy-java-spring-boot" rel="noopener noreferrer"&gt;Projeto de Exemplo no Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://refactoring.guru/pt-br/design-patterns/strategy" rel="noopener noreferrer"&gt;Strategy Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.devmedia.com.br/inversao-de-controle-x-injecao-de-dependencia/18763" rel="noopener noreferrer"&gt;Injeção de Dependência X Inversão de Controle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://refactoring.guru/design-patterns/proxy" rel="noopener noreferrer"&gt;Proxy Pattern&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>strategypattern</category>
      <category>enum</category>
      <category>springboot</category>
    </item>
  </channel>
</rss>
