对于接口方法的布尔参数,在接口中使用常量是一种好的做法还是代码味道?

Is having Constants in Interfaces for boolean parameters of Interface methods a good practice or code smell?

所以我有以下界面:

public interface RoleService {
    boolean INCLUDE_DEACTIVE_OBJECTS = true;
    boolean EXCLUDE_DEACTIVE_OBJECTS = false;
    Set<? extends BaseBusinessObject> getOwnedBusinessObjectsOf(final Employee employee, final boolean includeDeactiveObjects);
}

在上层某处,示例用法如下..

if (someCondition) {
    ownedBusinessObjects = roleService.getOwnedBusinessObjectsOf(employee, RoleService.INCLUDE_DEACTIVE_OBJECTS);
} else {
    ownedBusinessObjects = roleService.getOwnedBusinessObjectsOf(employee, RoleService.EXCLUDE_DEACTIVE_OBJECTS);
}

因此,我相信当我说 INCLUDE_DEACTIVE_OBJECTS.[=16= 时,比传递 true(或 false)这样的值更容易阅读方法调用]

但我不确定,这是不是很愚蠢?这是反模式、代码异味还是某种违反最佳实践的行为?

我认为这类似于以某种方式避免 Magic Numbers,但它有用还是相当混乱?

因为这使 API 更具可读性,所以很好。潜在价值是什么并不重要,重要的是您传递一个触发特定行为的标志。如果您选择的语言只允许位置参数传递,那么这样的常量会使调用更具可读性。比较:

roleService.getOwnedBusinessObjectsOf(employee, RoleService.INCLUDE_DEACTIVE_OBJECTS);
roleService.getOwnedBusinessObjectsOf(employee, true);

"Get owned business objects of employee… what?" 没有人知道“true”在这里是什么意思,命名的值要好得多。


另一种选择是当您选择的语言支持调用时命名参数(此处,Python):

roleService.getOwnedBusinessObjectsOf(employee, include_deactivated=True);

API 应在此处强制执行命名参数(再次:Python):

def getOwnedBusinessObjectsOf(employee, *, include_deactivated): ...

在 Objective-C 中,您将使用显式方法命名做类似的事情:

[roleService getOwnedBusinessObjectsOf:employee includingDeactivated:YES]

IMO,将常量字段放在接口中肯定是不好的。我来自 C# 背景,它甚至不允许 Interface 中的 const 字段。即使在 Java 中,您也有 Constant Interface(又是反模式),其唯一目的是包含多个 classes 可以共享的常量。你不完全是在做那些事情。仍然只是参考。

So instead of passing values such as true (or false), I believe it is much easier to read the method call when I say INCLUDE_DEACTIVE_OBJECTS

选项 1

要在保持可读性的同时替换常量,您可以定义 2 个方法来指示是否包含停用对象(我会选择 'Inactive'。有停用动词但没有停用形容词)对象。

getOwnedBusinessObjectsIncludingInactive(...)
getOwnedBusinessObjectsExcludingInactive(...)

这些只是 RoleService class 中的包装方法,private通过传递 truefalse 调用方法来检索实际数据分别。

选项 2

有一个单独的 class 在一个地方定义应用程序级常量。

getOwnedBusinessObjectsOf(employee, Constants.INCLUDE_INACTIVE_OBJECTS)
getOwnedBusinessObjectsOf(employee, Constants.EXCLUDE_INACTIVE_OBJECTS)

如果只是为了可读性,在你的摘要中class,你可以用静态变量来保存这些布尔值,你可以用ClassName.AttributeName

赋值
public YourClass
{
    public bool static INCLUDE_DEACTIVE_OBJECTS = true;
    public bool static EXCLUDE_DEACTIVE_OBJECTS = false;

    public static void Main (String args[])
    {

    }
}

任何采用布尔参数的方法都违反了单一职责原则,因为它为 true 做一件事,为 false 做第二件事。

这总是一种代码味道。 Clean Code 告诉我们将方法一分为二:

  1. getOwnedBusinessObjectsIncludingDeactiveObjectsOf(Employee employee);

  2. getOwnedBusinessObjectsExcludingDeactiveObjectsOf(Employee employee);

当然,如果要广泛使用该方法,您可能需要更简洁的名称,例如getAllObjectsgetActiveObjects.

如果您不想在常量命名中使用含义相反的词,您也可以尝试将布尔值包装到枚举中。

public interface RoleService {

    /**
    * Boolean wrapper for easy reading of values in method parameters.
    */
    enum IncludeDeactiveObjects {

        TRUE(true),
        FALSE(false);

        private final boolean flag;

        private IncludeDeactiveObjects(boolean flag) {
            this.flag = flag;
        }

        public boolean get() {
            return flag;
        }
    }

    Set<? extends BaseBusinessObject> getOwnedBusinessObjectsOf(final Employee employee, final IncludeDeactiveObjects includeDeactiveObjects);
}

调用将是:

roleService.getOwnedBusinessObjectsOf(employee, RoleService.IncludeDeactiveObjects.TRUE);
roleService.getOwnedBusinessObjectsOf(employee, RoleService.IncludeDeactiveObjects.FALSE);