我知道你可以通过使用带有策略或命令模式的多态来删除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() = "id2_2" || arg.getId() = "id3_1") {
//do something
}
if (arg.getId().startsWith("id1") || arg.getId().startsWith("id4")) {
//do something
}
基本上,代码根据产品的ID执行一些字段填充操作。它使用startsWith()检查产品是否来自某些产品组,然后执行填充,检查产品是否具有特定ID,然后适当地填充字段等。
我不确定如何在此处使用策略模式?如果我创建一个策略来处理第一个if语句,那么我该如何处理第二个语句?问题是ifs不是互斥的。我应该将相关的ifs移动到Strategy类吗?任何可以帮我重构的模式都比较容易测试吗?
答案 0 :(得分:1)
您的代码显示的问题更基本:它违反了 Tell!不要问!原则。
您的getId()
对象应该具有一个在此时调用的公共接口方法,而不是方法arg
。要实现的工作可以在实例化时注入arg
个对象,也可以作为参数传递给方法,arg
对象的具体实现决定调用哪个参数。
答案 1 :(得分:0)
您的问题缺乏这些ifs的业务逻辑。 但我可以做出一些假设:
基本上,代码根据产品的id执行一些字段填充操作。它使用startsWith()检查产品是否来自某些产品组,然后执行填充,检查产品是否具有特定ID,然后适当地填充字段等。
如果块是特定的业务逻辑并且彼此独立,我假设所有你。 您可以尝试使用Decorator Pattern。正如维基所说:
装饰器模式是一种设计模式,允许将行为静态或动态地添加到单个对象,而不会影响同一类中其他对象的行为
您的案例可以视为:
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));
}
}
}
...
任何可以帮我重构的模式,以便更容易测试?
在这里,您需要仅测试各个装饰器。
您需要维护所有装饰器的列表并将对象传递给所有装饰器。 这不是模式的正确使用,但有助于解决您的用例(测试复杂性)。
答案 2 :(得分:0)
您可能无法完全摆脱基于if
的代码,但您可以将其整理成更合适的类。
if
是域逻辑,如果在域内有意义,那么您可以编写一个对此分类负责的对象:
interface ArgumentClassifier {
Classification classify(Argument arg);
}
Classification
将是enum
。
此实现将包含您的if
,封装该域逻辑。
ArgumentClassifier
进行单元测试。 ArgumentClassifier
变得庞大而复杂,您可能可以使用复合模式将其分解。ArgumentClassifier
的新实现。现在您可以使用策略模式:
Map<Classification, Handler> strategies = ...;
ArgumentClassifier classifier = new SimpleArgumentClassifier();
private Foo handleArg(Argument arg) {
return strategies
.get(classifier.classify(arg))
.handle(arg);
}
这一切是否值得,取决于您的具体需求。
如果:
...然后将if
逻辑保留在原来的位置可能更明智。
另一种分类方法是从关于被分类对象的谓词构建二进制数或字符串。例如:
int classification(Person p) {
return 0
| ( p.getGender() == Gender.MALE ? 1 : 0 )
| ( p.isEmployed() ? 2 : 0 )
| ( p.age >= 18 ? 4 : 0 );
}
对于17岁的失业男性,0b001
,对于17岁的就业女性,0b010
分类为if/else
,依此类推。
您可以直接将它们用作策略图的键(它可以是多对一的)。或者你可以进行进一步的按位操作,将它们缩小到较小的值。
这里有很多范围,但要注意不要忽视简洁和清晰。有时Date
链可以完成工作,清晰有效。