在实施新功能时,我在两个无关的地方遇到了 flag参数抗模式。我想将其作为一个仔细研究这个反图案的机会。
一个简单的例子
假设我们要通过API方法加载所有PDF文档。看起来像这样:
public interface DocumentService {
Document[] loadDocuments();
}
它开始闻起来
在下一步中,我们需要一种加载所有TIFF文档的方法。由于实现的差异可能只是一行代码,因此结果可能是这样的:
public interface DocumentService {
/**
* @param loadTiff true: loads TIFF documents - false: loads PDF documents
*/
Document[] loadDocuments(boolean loadTiff);
}
也可以通过额外的块进行非常快速修改实现:
public Document[] loadDocuments(boolean loadTiff) {
int format = 1; // load PDF documents by default
if (loadTiff) {
format = 2; // except when TIFFs are requested
}
...
}
但是,如果我们看一下此API的客户端,我们已经注意到了令人不愉快的气味:
Document[] documents = documentService.loadDocuments(false);
在此代码点,您能说出false
的含义,而不查看该方法的定义?如果我们将其更改为true
?
气味开始发臭
在下一步中,我们还需要一种加载RTF文档的方法。同样,实现的差异仅是一行代码。但是,我们如何在不重新实现逻辑的情况下重复使用现有方法?标志参数妨碍了我们! boolean
参数只能采用两个状态,即true
或false
。 (好吧,聪明的Java开发人员将其更改为Boolean
参数,并将true
,false
和null
作为选择)。
但是,标志参数也向我们展示了其他东西:这里违反了单一责任原则。实际上,该方法目前具有多个责任。一方面,它知道如何加载PDF文档。另一方面,它还知道如何加载TIFF文档。或更一般:这样的方法做两种不同的事情 - 一次在true
上,一次在false
上。
可能的解决方案
自发地,我可以想到两种摆脱标志参数的简单方法。
选项1:我们为文档类型定义一个接口以查询信息。然后,我们可以通过使用文档类型作为参数来概括该方法。但是,有可能不必要的over-generalization of the business logic。
public interface DocumentService {
Document[] loadDocuments(DocumentType documentType);
}
选项2:我们为每个可能的域上下文创建一个特定方法。
public interface DocumentService {
Document[] loadPdfDocuments();
Document[] loadTiffDocuments();
Document[] loadRtfDocuments();
}
在两种情况下,从客户的方面,这都会产生可读的代码:
Document[] documents = documentService.loadDocuments(DocumentType.PDF);
Document[] documents = documentService.loadPdfDocuments();
那设定器方法呢?
如果我们严格,那么我们也必须更改
public class Action {
private boolean enabled;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
public class Action {
private boolean enabled;
public void enable() {
this.enabled = true;
}
public void disable() {
this.enabled = false;
}
}
但是,我们不必比这里的教皇更重要。如果我必须在
之间进行选择
action.setEnabled(actionCheckBox.isChecked());
和
if (actionCheckBox.isChecked()) {
action.enable();
} else {
action.disable();
}
对于客户端代码,我会留在第一个。
进一步阅读
- 马丁·福勒(Martin Fowler)的FlagArgument
- bob叔叔的Clean Code Tip: Eliminate Boolean Arguments
我希望这篇文章对您有帮助。欢迎发表评论。有关更多Java的内容,请在Twitter上关注我。