java 依赖注入如何避免像验证器那样隐藏对有状态对象的依赖[关闭]

zc0qhyus  于 2023-03-21  发布在  Java
关注(0)|答案(1)|浏览(109)

已关闭。此问题为opinion-based。当前不接受答案。
**想要改进此问题吗?**请更新此问题,以便editing this post可以用事实和引文来回答。

2天前关闭。
Improve this question
我想听听你对这种情况的看法,代码很简单,应该很容易遵循:

@Component
class MyService {
    
    AusiliaryService ausService;

    public MyService(AusiliaryService ausService) {
        ...
    }

    public boolean isAccountValid(Account account) {

     AccountValidator accountValidator = new AccountValidator(account);
     boolean isValid = accountValidator.isValid(account);
     if(isValid) {
        ausService.openAccount(account);
     }
    }
}

注意AusiliaryService也是spring管理的,我们使用的是构造函数注入。

在这个类中,AccountValidatorMyService依赖项,但它没有出现在构造函数中,所以我们可以说这个依赖项的可见性以某种方式隐藏
由于我们是硬编码
AccountValidator accountValidator = new AccountValidator(account);
我们在编写单元测试MyService时也可能有一些潜在的问题,除非你认为这样的任何单元测试也应该影响AccountValidator,这对我来说是一个有效的思路。
解决这两个“问题”的一个可能的方法是使用工厂,从MyService中删除示例化AccountValidator对象的责任。

@Component
class MyService {
    
    AusiliaryService ausService;
    AccountValidatorFactory accountValidatorFactory;

    public MyService(AusiliaryService ausService, AccountValidatorFactory accountValidatorFactory;) {
        ...
    }

    public boolean isAccountValid(Account account) {

     AccountValidator accountValidator = accountValidatorFactory.getValidator(account);
     boolean isValid = accountValidator.isValid(account);
     if(isValid) {
        ausService.openAccount(account);
     }
    }
}

请注意,AccountValidatorFactory也是Spring管理的,我们使用的是构造函数注入。
如果在代码中经常使用这种模式,这种解决方案的一个缺点可能是这样的工厂类的激增。
在这种情况下,你的方法是什么?你更喜欢哪种解决方案?是否有可能使AccountValidator也能帮助解决这个问题,那将如何做到?

xqnpmsa8

xqnpmsa81#

简单的答案是,你可以让AccountValidator成为一个托管的spring bean。你可以用创建这个类(MyService)的方式来实现这一点,或者你如何让你描述的工厂类托管-通过添加@Component。
如果你需要保留语义,例如,如果这个bean不是线程安全的,你需要每次创建新的,你可以让它成为一个原型bean。
这里有一个例子,你可以看到它是以一种使用spring库而没有你自己的工厂的方式注入的。它将打印:

Example.MyValidator(id=0)
Example.MyValidator(id=1)
Example.MyValidator(id=2)

下面是代码:

package org.example;

import lombok.ToString;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        SpringApplication.run(Example.class, args);
    }

    @Autowired
    MyService myService;
    @Autowired
    ConfigurableApplicationContext context;

    @Bean
    ApplicationRunner applicationRunner() {
        return args -> {
            for (int i = 0; i < 3; i++) {
                System.out.println(myService.state());
            }
            context.close();
        };
    }

    @Service
    static class MyService {
        private final ObjectProvider<MyValidator> myValidators;

        MyService(ObjectProvider<MyValidator> myValidators) {
            this.myValidators = myValidators;
        }

        String state() {
            return myValidators.getObject().toString();
        }
    }

    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Component
    @ToString
    static class MyValidator {
        private static final AtomicInteger counter = new AtomicInteger();
        private final int id;

        MyValidator() {
            this.id = counter.getAndIncrement();
        }
    }
}

相关问题