Java 当一个类有许多依赖项,但基于某些条件只需要使用其中的一些依赖项时的设计方法
我有多个实现接口并返回对象的类Java 当一个类有许多依赖项,但基于某些条件只需要使用其中的一些依赖项时的设计方法,java,spring,java-8,Java,Spring,Java 8,我有多个实现接口并返回对象的类 public interface DataFetcher { Data getData(Info info); } public class Data { private String name; private String value; } @Component public class DataPointA implements DataFetcher { @Override public Data getDat
public interface DataFetcher {
Data getData(Info info);
}
public class Data {
private String name;
private String value;
}
@Component
public class DataPointA implements DataFetcher {
@Override
public Data getData(Info info) {
//..Do some processing
return new Data("SomeName", valueComputed);
}
}
现在我有大约20个数据点,它们实现了DataFetcher类并返回数据对象
我将所有数据点自动关联到一个类,并根据某些条件使用某些数据点
@Component
public class DataComputer {
@Autowired
private DataPointA dataPointA;
@Autowired
private DataPointB dataPointB;
.
.
.
public void computeData(String inputType, Info info) {
List<DataFetcher> dataFecthers;
switch(inputType) {
case "typeA" : dataFecthers = ImmutableList.of(dataPointA, dataPointB);
break;
.
.
.
case "typeD" : dataFecthers = ImmutableList.of(dataPointE, dataPointF, dataPointG);
break;
}
dataFetcher.forEach(dataPoint -> {
//Do some processing with dataPoint.getData(info)
})
}
}
@组件
公共类数据计算机{
@自动连线
私有数据点a数据点a;
@自动连线
私有数据点b数据点b;
.
.
.
公共无效计算数据(字符串输入类型,信息){
列出数据表;
开关(输入类型){
案例“typeA”:dataFecthers=ImmutableList.of(dataPointA,dataPointB);
打破
.
.
.
案例“类型化”:dataFecthers=ImmutableList.of(dataPointE、dataPointF、dataPointG);
打破
}
dataFetcher.forEach(数据点->{
//使用dataPoint.getData(info)执行一些处理
})
}
}
可以看出,DataComputer类将有一个完整的依赖项列表,这些依赖项可能变得不可管理。此外,基于inputType使用的数据点在手之前已知,因此可以提取出来。这是我的尝试:
@Component
public class DataComputationPointDecider {
@Autowired
private DataPointA dataPointA;
@Autowired
private DataPointB dataPointB;
.
.
.
@Bean
public Map<String, List<DataFetcher>> getDataComputationPoints() {
return new ImmutableMap.Builder<String, List<DataFetcher>>()
.put("typeA", ImmutableList.of(dataPointA, dataPointB))
.put("typeD", ImmutableList.of(dataPointE, dataPointF, dataPointG))
.build();
}
}
@组件
公共类数据计算点决策器{
@自动连线
私有数据点a数据点a;
@自动连线
私有数据点b数据点b;
.
.
.
@豆子
公共地图getDataComputationPoints(){
返回新的ImmutableMap.Builder()
.put(“typeA”,不可变列表(dataPointA,dataPointB))
.put(“类型化的”,不可变的list.of(dataPointE,dataPointF,dataPointG))
.build();
}
}
然后,我的数据计算机依赖性降低:
@Component
public class DataComputer {
@Autowired
private Map<String, List<DataFetcher>> dataComputationPoints;
public void computeData(String inputType, Info info) {
List<DataFetcher> dataFecthers = dataComputationPoints.get(inputType);
dataFetcher.forEach(dataPoint -> {
//Do some processing with dataPoint.getData(info)
})
}
}
@组件
公共类数据计算机{
@自动连线
私有地图数据点;
公共无效计算数据(字符串输入类型,信息){
List dataFecthers=dataComputationPoints.get(inputType);
dataFetcher.forEach(数据点->{
//使用dataPoint.getData(info)执行一些处理
})
}
}
有更好的设计方法吗?您考虑过使用Factory模式吗?这允许您根据特定条件提交对象实例请求。我看不出您的方法有任何重大错误。但我建议还有一个选择 您可以让
DataFetcher
决定或说出它可以处理的输入类型,而不是维护将inputType
映射到DataFetcher
列表的映射
但这需要将DataFetcher的接口更改为
public interface DataFetcher {
boolean canHandle(String inputType);
Data getData(Info info);
}
这些实现看起来像
@Component
public class DataPointA implements DataFetcher {
@Override
boolean canHandle(String inputType) {
return "typeA".equals(inputType);
}
@Override
public Data getData(Info info) {
//..Do some processing
return new Data("SomeName", valueComputed);
}
}
然后,您可以将所有数据获取程序
作为一个列表注入(不需要为每个列表添加一个@Autowired
字段),并将其作为
@Autowired
List<DataFetcher> dataFetchers;
...
dataFetchers.stream()
.filter(dataFetcher -> dataFetcher.canHandle(inputType))
.forEach(dataFetcher.getData(info));
@Autowired
列出数据获取程序;
...
dataFetchers.stream()
.filter(dataFetcher->dataFetcher.canHandle(inputType))
.forEach(dataFetcher.getData(info));
优势:
在当前方法中,如果添加新的DataFetcher
实现,则需要添加@AutoWired
字段/成员并修改(getDataComputationPoints)映射。但是,有了它,DataFetcher
可以处理的输入类型是由它自己指定的,因此您只需要为新的输入类型添加新的类
参考
更新:
缺点
输入类型是在类中指定的,这意味着您无法轻松找到给定输入类型的数据获取程序列表
(数据点)
如果您需要删除对inputType的支持,那么您需要再次访问每个实现(从canHandle
中删除该inputType)。在您的方法中,只需删除一个映射条目
嗯,说得好。在工厂模式的情况下,工厂将负责返回给定输入类型的列表
,而不是直接对其进行自动接线。如果我能正确理解你的意图,这将是我思想的延伸。我喜欢你指出的优点。我一直在考虑传递输入类型的另一种方法。我没有在代码中说明特定数据点需要使用哪些数据点,而是在考虑是否可以将其扩展到配置文件中包含这些数据的方式,如果需要交换数据点,则需要更少的java代码更改。对于文件{“typeA”:[“DataPointA”,“DataPointB”],“typeB”:[“DataPointC”,“DataPointE”]}中的ex,它有其优点和缺点。在配置中维护java类名(如DataPointA
)是很脆弱的。如果后来有人决定给它重命名怎么办?即使在这种情况下,您也需要将字符串DataPointA
映射到DataPointA
对象,这不是简单的(使用反射?任何其他方式?)True。不需要将配置文件名映射为类名。那就简单多了。一种方法是使用一个枚举,该枚举具有与配置文件中的名称列表匹配的字符串,以及一个映射器,该映射器为每个枚举值返回正确的实现。这样,更改类名是安全的。然后您无论如何都需要更改该枚举(这是java代码):)然后每次添加新的实现时,您都需要1)更改配置2)向枚举添加条目,很容易忘记其中一项或两项。此外,您还需要处理configFair中的拼写错误。我编辑了我的答案,添加了这种方法的缺点。但是要删除类型的实现,只需转到该实现并更改canHandle
。很难完全删除一个类型