策略模式没有春季靴子usando枚举
#java #springboot #strategypattern #枚举

最近通过了一项非常适合使用Strategy Pattern的功能,我决定将我的母亲放在面团中,很快遇到了以下挑战:

  • 通过利用春季的Injeção de Dependência功能Inversão de Controle,实现此解决方案,使其尽可能灰心
  • 获得执行时间策略实施弹簧的托管实例
  • 不创建不会在内存不必要的构件中使用的阶层实例。
  • 创建一个简单且狂热的扩展工厂,以基于标识符的标识符提供策略实例,该标识符将作为parano通过重置请求中的parano

在一项粗略的调查中,我在葡萄牙没有发现任何材料,我决定结束对写作的恐惧并创建一个示例项目。
对于那些没有耐心阅读文章并且已经想看到的方式的人,只有点击:Talk is Cheap. Show me the Code

在我们关注之前,我想清楚地表明:

  • 假设读者已经对Java和Spring Boot有了母亲的知识或熟悉
  • 没有意图教授模式或春季靴设计的概念
  • 它旨在以实现模式的方式成为直接和根源的参考
  • 解释某些设计决策的原因

来吧。

此功能的功能(在这种情况下是链接到应用程序设计的功能要求),这是很快就可以通过其“相应用例”使用此“策略”,以避免使用其他用例的抽象在同一应用程序中,所有类和接口都将在同一软件包内,除了将是'public''的“ foooresecase”接口',其他类将其访问修改器为'默认值'限制包装外其他类的访问权限。
注意:,这是一种实现形式,不是限制,如果其功能中没有这种“特征”,则可以将所有类(如'public'')留下来)。 。
以下是包装结构:

Image description

也就是说,让我们去零件:

首先,创建一个接口,将代表“合同”,换句话说,客户将使用的“策略”的标准结构/行为(将有效地使用它的类,在我们的情况下,代表用例本身)

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

interface FooStrategy {
    String execute();
}

接下来,将实施每种策略的类:

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";
     }
 }
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";
     }
 }
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";
     }
 }

接下来,让我们创建将实现它的用户酶接口及其具体类:

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

public interface FooUseCase {
    String call(String id);
}
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<? extends FooStrategy> impl) {
            this.id = id;
            this.impl = impl;
        }

        final String id;
        final Class<? extends FooStrategy> impl;

        public static Optional<FooEnumStrategy> findById(String id){


            Objects.requireNonNull(id);
            Supplier<Stream<FooEnumStrategy>> fooEnumStrategyStream
                    = () -> Stream.of(values())
                    .filter(strategy -> strategy.id.equals(id));


            if(fooEnumStrategyStream.get().count() > 1){
                fooEnumStrategyStream
                        .get()
                        .findFirst()
                        .ifPresent(strategy -> {
                            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);
        }
    }

}

Adjecta§ãμes导入:

  • 创建 enum _的设计决定将用作工厂作为“内部类”,带有“私人”访问修饰符,因为如上所述,这些“染色”应在用例。也可以通过将其修饰符保持为_'default'使其无法访问包装外部的其他类以满足限制。
  • ,也可以在单独的文件中创建此_enum _。

Image description

  • 所有具体类均标记为',这是为了确保它们不会扩展它们。每个“策略”都必须有自己的规则,如果依赖另一个策略,则想法是以组成的形式解释了这种依赖,尽管需求是相当改进的。仍然可以应用Proxy Pattern。 例子:
@Component
final class FooStrategyImpl1 implements FooStrategy{

     @Override
     public String execute() {
         return "Foo Strategy Impl 1";
     }
 }
  • lombok的笔记本 @requiregsconstructor 在编译时创建一个构造函数,它作为parano收到的parano被声明为最终的变量,迫使依赖的注入器以构建实现类别的所有依赖性, 。这是实施控制倒置以确保班级状态的一种方式。 值得注意的是:使用 @Autowired 声明差异仅表示弹簧注入依赖性,这并没有反向控制,并且可能会使您的测试更复杂

Image description

  • 决定在母亲©todo findbyid fooenumstrategy 的母亲中使用可选枚举一个不存在的ID,因此,如果没有“肩带”,则取决于实现“用例”的类,以决定处理的最佳方法。在有关示例的情况下,它正在启动A Illegargelguexcection ,如下所示,但可能是业务错误,或者将404返回给客户端休息,最后是该功能的功能问=)。
@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();
    }
  • 确定 no 使用 enum 的自然顺序,随着项目的发展,正在添加新的“肩带”,请错误地创建开发人员的风险 enum 的两个项目具有相同的标识符,生成了我们所谓的“沉默错误”,因为枚举可以为使用的情况提供错误的压力,换句话说是一个错误通常会编译,并且可以被认为为时已晚,因为它不会明确明确。因此,为了避免这种情况, enum 的整体findbyid 对parano通过的ID的数量元素进行了验证,如果有更多' Runtime Xception '描述重复哪个标识符的错误和指向。 可能有两种不同的标识符使用相同的“策略”,这不一定是错误,在这种情况下,我想不到太多的事情=/ 例子:
public static Optional<FooEnumStrategy> findById(String id){


            Objects.requireNonNull(id);
            Supplier<Stream<FooEnumStrategy>> fooEnumStrategyStream
                    = () -> Stream.of(values())
                    .filter(strategy -> strategy.id.equals(id));


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


            return fooEnumStrategyStream.get().findFirst();
        }
  • 母亲©todo getBean applicationContext 您在下面的段落中找到:
 public FooStrategy getImpl(ApplicationContext context) {

            return context.getBean(impl);
        }

也接受代表bean名称的字符串的一部分,这也是实现此“工厂”的一种方式。使用@qualifier笔记将代理模式应用于“策略”而不必弄乱枚举将是更奇怪的,但是它将不再是“键入安全”,从而使您可以通过任何bean甚至是错误的名称,在执行时间中的 classcascastexception beansxception 的错误中。无论如何,仍然可以应用代理模式,但需要通过作为代理类的成员来改变枚举(不是世界上最好的,但它是今天的KKK)。
例如:

IMPL4("Foo2", ProxyFooStrategyImpl2.class),

很好,就是这样。我说这很粗鲁,但我意识到我写了太多。斗酒。 - \ o/ -

遵循链接以供参考: