春豆映射与命令模式

时间:2019-02-11 17:23:57

标签: java spring dependency-injection

我有ProductHandler类具有不同的实现,例如ABCProductHandlerDEFProductHandler等。这些是使用命令模式从ProductServiceImpl类中调用的如modern replacements所示。

ProductServiceImpl类:

@Service
public class ProductServiceImpl implements ProductService {

    private Map<ProductType,ProductHandler> productHandlersMap = 
                                         new EnumMap<>(ProductType.class);

    private ABCProductHandler abcProductHandler;

    private DEFProductHandler defProductHandler;

    //....10 other product handlers goes here

    @Autowired
    public ProductServiceImpl(ABCProductHandler abcProductHandler, 
                                  DEFProductHandler defProductHandler, .....) {
        this.abcProductHandler = abcProductHandler;
        this.defProductHandler = defProductHandler;
        //....10 other product handlers goes here
    }

    @PostConstruct() 
    public void init() {
        productHandlersMap.put(ProductType.ABC, abcProductHandler);
        productHandlersMap.put(ProductType.DEF, defProductHandler);
        //....10 other product handlers goes here
    }

    @Override
    public ProductDetails calculateProductPrice(ProductType productType) {
        productHandlersMap.get(productType).calculate();
        //..some otehr code goes here
        return productDetails;
    }
}

但是,我对上面的ProductServiceImpl类不满意,因为有许多带有样板代码的productHandlersMap.put调用。

现在,我的问题是,有什么方法可以轻松加载productHandlersMap

@Service
public class ProductServiceImpl implements ProductService {

    private Map<ProductType,ProductHandler> productHandlersMap = 
                        new EnumMap<>(ProductType.class);

    @PostConstruct() 
    public void init() {
         //How to laod productHandlersMap easily with 
         // different ProductHandler types here?
    }

    @Override
    public ProductDetails calculateProductPrice(ProductType productType) {
        productHandlersMap.get(productType).calculate();
        //..some other code goes here
        return productDetails;
    }
}

2 个答案:

答案 0 :(得分:2)

Spring可以自动将实施接口I的bean的不同实现自动连接到类型为Map<String, I>的属性,其中键是bean的 name 和重视bean实例。由于您已经有一个针对每个ProductHandler实现的枚举,因此可以利用它:

public enum ProductType {
    ABC(ProductType.ABC_BEAN_NAME),
    DEF(ProductType.DEF_BEAN_NAME);

    public static final String ABC_BEAN_NAME = "abcProductHandler";
    public static final String DEF_BEAN_NAME = "defProductHandler";

    private String beanName;

    ProductType(String beanName) { this.beanName = beanName; }

    public String beanName() { return beanName; }
}

然后,在ProductHandler工厂类中或通过@Configuration@Service批注定义不同的@Component实现:

@Service(ProductType.ABC_BEAN_NAME)
public class ABCProductHandler implements ProductHandler {

    // ...
}

@Service(ProductType.DEF_BEAN_NAME)
public class DEFProductHandler implements ProductHandler {

    // ...
}

现在,在您的ProductServiceImpl bean中,只需自动连接Map<String, ProductHandler>

@Service
public class ProductServiceImpl implements ProductService {

    private final Map<String, ProductHandler> productHandlersMap;

    @Autowired
    public ProductServiceImpl(Map<String, ProductHandler> productHandlersMap) {
        this.productHandlersMap = productHandlersMap;
    }

    @Override
    public ProductDetails calculateProductPrice(ProductType productType) {
        productHandlersMap.get(productType.beanName()).calculate();
        //..some otehr code goes here
        return productDetails;
    }
}

这样,您就可以让Spring完成所有的注入工作,甚至不需要使用@PostConstruct方法。

请注意在productType.beanName()方法内使用calculateProductPrice。这样可以确保您使用正确的bean来计算价格。

答案 1 :(得分:1)

您可以创建spring配置组件

@Configuration
public class CollectionConfig {

    @Bean
    public ProductHandler  getABC() {
        return new ABCProductHandler(ProductType.ABC);
    }

    @Bean
    public ProductHandler  getDEF() {
        return new DEFProductHandler(ProductType.DEF);
    }

    @Bean
    public ProductHandler  getXYZ() {
        return new XYZProductHandler(ProductType.XYZ);

    }
 

    // other factory methods

}

然后:

    @Service 
public class ProductServiceImpl implements ProductService { 
private Map<ProductType,ProductHandler> productHandlersMap = new EnumMap<>(ProductType.class); 

 @Autowired(required = false)
 private List<ProductHandler> beanList;

 @PostConstruct() 
public void init() { 
    beanList.foreach(b->
    productHandlersMap.put(b.getType(), b))
     }
}