最近通过了一项非常适合使用Strategy Pattern的功能,我决定将我的母亲放在面团中,很快遇到了以下挑战:
- 通过利用春季的Injeção de Dependência功能Inversão de Controle,实现此解决方案,使其尽可能灰心
- 获得执行时间策略实施弹簧的托管实例
- 不创建不会在内存不必要的构件中使用的阶层实例。
- 创建一个简单且狂热的扩展工厂,以基于标识符的标识符提供策略实例,该标识符将作为parano通过重置请求中的parano li>
在一项粗略的调查中,我在葡萄牙没有发现任何材料,我决定结束对写作的恐惧并创建一个示例项目。
对于那些没有耐心阅读文章并且已经想看到的方式的人,只有点击:Talk is Cheap. Show me the Code。
在我们关注之前,我想清楚地表明:
- 假设读者已经对Java和Spring Boot有了母亲的知识或熟悉
- 没有意图教授模式或春季靴设计的概念
- 它旨在以实现模式的方式成为直接和根源的参考
- 解释某些设计决策的原因
来吧。
此功能的功能(在这种情况下是链接到应用程序设计的功能要求),这是很快就可以通过其“相应用例”使用此“策略”,以避免使用其他用例的抽象在同一应用程序中,所有类和接口都将在同一软件包内,除了将是'public''的“ foooresecase”接口',其他类将其访问修改器为'默认值'限制包装外其他类的访问权限。
注意:,这是一种实现形式,不是限制,如果其功能中没有这种“特征”,则可以将所有类(如'public'')留下来)。 。
以下是包装结构:
也就是说,让我们去零件:
首先,创建一个接口,将代表“合同”,换句话说,客户将使用的“策略”的标准结构/行为(将有效地使用它的类,在我们的情况下,代表用例本身)
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导入: p>
- 创建 enum _的设计决定将用作工厂作为“内部类”,带有“私人”访问修饰符,因为如上所述,这些“染色”应在用例。也可以通过将其修饰符保持为_'default'使其无法访问包装外部的其他类以满足限制。 ,也可以在单独的文件中创建此_enum _。
- 所有具体类均标记为',这是为了确保它们不会扩展它们。每个“策略”都必须有自己的规则,如果依赖另一个策略,则想法是以组成的形式解释了这种依赖,尽管需求是相当改进的。仍然可以应用Proxy Pattern。 例子:
@Component
final class FooStrategyImpl1 implements FooStrategy{
@Override
public String execute() {
return "Foo Strategy Impl 1";
}
}
- lombok的笔记本 @requiregsconstructor 在编译时创建一个构造函数,它作为parano收到的parano被声明为最终的变量,迫使依赖的注入器以构建实现类别的所有依赖性, 。这是实施控制倒置以确保班级状态的一种方式。 值得注意的是:使用 @Autowired 声明差异仅表示弹簧注入依赖性,这并没有反向控制,并且可能会使您的测试更复杂
- 决定在母亲©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/ -
遵循链接以供参考: