Java 使用模式避免ifs
我知道,通过使用策略或命令模式的多态性,可以删除if块。但是,假设我的代码如下所示:Java 使用模式避免ifs,java,oop,design-patterns,Java,Oop,Design Patterns,我知道,通过使用策略或命令模式的多态性,可以删除if块。但是,假设我的代码如下所示: ArgumentObject arg = getArg(); if (arg.getId() == "id2_1" || arg.getId() == "id3_1" || arg.getId() == "id4_1") { //do something } if (arg.getId() == "id2_1") { //do something } if (arg.getId() = "id
ArgumentObject arg = getArg();
if (arg.getId() == "id2_1" || arg.getId() == "id3_1" || arg.getId() == "id4_1") {
//do something
}
if (arg.getId() == "id2_1") {
//do something
}
if (arg.getId() = "id2_2" || arg.getId() = "id3_1") {
//do something
}
if (arg.getId().startsWith("id1") || arg.getId().startsWith("id4")) {
//do something
}
基本上,代码根据产品的id执行一些字段填充操作。它检查产品是否来自具有startsWith()的某组产品,然后执行填充,检查产品是否具有特定id,然后适当填充字段,等等
我不知道如何在这里使用策略模式?如果我创建了一个策略来处理第一个If语句,那么如何处理第二个语句呢?问题在于,国际单项体育联合会并不是相互排斥的。我是否应该将相关的ifs转移到策略类?有没有什么模式可以帮助我重构它,使其更易于测试?您的代码揭示的问题更为基本:它违反了Tell!不要问!原则
您的
arg
对象不应该有一个方法getId()。待完成的工作可以在实例化时注入到arg
对象中,或者可以将它们作为参数传递给方法,并且arg
对象的具体实现决定调用哪个参数…您的问题缺少这些ifs的业务逻辑。
但我可以从以下方面做出一些假设:
基本上,代码根据产品的id执行一些字段填充操作。它检查产品是否来自具有startsWith()的某组产品,然后执行填充,检查产品是否具有特定id,然后适当填充字段,等等
我假设所有模块都是特定的业务逻辑,并且彼此独立。
你可以试试这个装饰图案。正如维基所说:
Decorator模式是一种设计模式,它允许静态或动态地将行为添加到单个对象,而不影响同一类中其他对象的行为
您的案例可以被视为:
public Product{
private String id;
//other fields
}
ProductDecoratorInterface{
Product populateItem(Product product);
}
ProductXYZDecorator{
@Override
public Product populateItem(Product product){
// check the id for 1,2,3
//do the field population
}
}
ProductABCDecorator{
@Override
public Product populateItem(Product product){
// check the id for 1,3
//do the field population
}
}
您的主类将如下所示:
public Mainclass{
List<ProductDecoratorInterface> decorators;
public void someFn(Product product){
decorators.stream.forEach(i -> i.populateItem(product));
}
}
public类{
列出装饰师;
公共产品{
decorators.stream.forEach(i->i.populateItem(产品));
}
}
}
有没有什么模式可以帮助我重构它,让它更容易测试
在这里,您只需要测试单个装饰器
您需要维护所有decorator的列表,并将对象传递给所有decorator。
这不是对模式的正确使用,但有助于解决您的用例(测试复杂性)。如果完全基于
的代码,您可能无法摆脱,但您可以将其整理成更合适的类
如果
是域逻辑,并且在域中有意义,那么您可以编写一个单独负责此分类的对象:
interface ArgumentClassifier {
Classification classify(Argument arg);
}
分类
将是一个枚举
这个实现将包含if
s,封装该域逻辑
- 您可以单独对
ArgumentClassifier
进行单元测试李>
- 如果您的
ArgumentClassifier
变得又大又复杂,那么您可以使用复合模式将其分解
- 如果您以后找到不同的分类方法(例如,规则引擎、数据库查找等),您可以编写
ArgumentClassifier
的新实现
现在,您可以使用策略模式:
Map<Classification, Handler> strategies = ...;
ArgumentClassifier classifier = new SimpleArgumentClassifier();
private Foo handleArg(Argument arg) {
return strategies
.get(classifier.classify(arg))
.handle(arg);
}
这为失业的17岁男性提供了0b001
分类,为就业的17岁女性提供了0b010
分类,依此类推
您可以直接将它们用作策略图的键(可以是多对一)。或者,您可以进一步进行逐位操作,将它们减少到一个较小的集合
这里有很多范围,但注意不要忽略简单性和清晰性。有时,一系列的if/else
完成了任务,这是明确而有效的。我真的看不出有什么理由在这种情况下尝试使用策略模式。也许如果您以更现实的方式解释业务问题,您可以得到更现实的解决方案。此外,您应该使用equals()
方法而不是==
来比较字符串。您的代码可以减少:第二个和第三个if
在将代码复制到第一个if
中时是多余的,类中有大约100个if语句,我必须为它编写单元测试。我认为将一些代码提取到策略中会使测试变得更小、更容易编写,并且只需在其他测试中使用逻辑测试策略……是的,它们是冗余的,因为这只是一个示例。在应用程序中,每个if都是必需的。嗯,我们的体系结构中的参数对象只是一个普通的传输对象,不包含任何业务逻辑,并且始终是相同的。通过填写UI表单生成。然后,基于此对象,创建包含不同数据库调用数据的新数据传输对象。然后,来自数据库的响应对象被合并到最终的响应对象中,以便进一步使用。我无法在arg对象中添加任何逻辑:(您的建议似乎仍然类似于策略模式,对于不同的对象,我会有不同的行为,对于操作对象的不同策略也是一样。@eek“我们架构中的参数对象只是一个简单的传输对象,不包含任何业务逻辑”在这种情况下,您应该通过一个工厂来创建包装器对象,该对象具有处理每个ID所需的逻辑。注意,我已经从ArgumentObject
的名称中删除了“Object”一词。所有对象都是对象,因此在类名中包含“Object”一词是重复的。
int classification(Person p) {
return 0
| ( p.getGender() == Gender.MALE ? 1 : 0 )
| ( p.isEmployed() ? 2 : 0 )
| ( p.age >= 18 ? 4 : 0 );
}