3

最近看了一些文章,说有副作用的方法不好。所以我只想问我这里的实现是否可以归类为有副作用。

假设我有一个SecurityGuardwhich 检查他是否应该允许客户去俱乐部。

SecurityGuard要么只有validNames 列表或invalidNames 列表,而不是两者。

  • 如果SecurityGuard只有validNames,他只允许名字在列表中的客户。
  • 如果SecurityGuard只有无效名称,他只允许名称不在列表中的客户。
  • 如果SecurityGuard根本没有列表,他允许所有人。

因此,为了执行逻辑,在每个列表的设置器上,如果新列表具有值,我会重置另一个列表。

class SecurityGaurd {
    private List<String> validNames = new ArrayList<>();
    private List<String> invalidNames = new ArrayList<>();

    public void setValidNames(List<String> newValidNames) {
        this.validNames = new ArrayList<>(newValidNames);
        // empty the invalidNames if newValidNames has values
        if (!this.validNames.isEmpty()) {
            this.invalidNames = new ArrayList<>();
        }
    }

    public void setInvalidNames(List<String> newInvalidNames) {
        this.invalidNames = new ArrayList<>(newInvalidNames);
        // empty the validNames if newInvalidNames has values
        if (!this.invalidNames.isEmpty()) {
            this.validNames = new ArrayList<>(); //empty the validNames
        }
    }


    public boolean allowCustomerToPass(String customerName) {
        if (!validNames.isEmpty()) {
            return validNames.contains(customerName);
        }
        return !invalidNames.contains(customerName);
    }
}

所以在这里你可以看到 setter 方法有一个隐式操作,它重置了另一个列表。

问题是我在这里所做的可以被认为有副作用吗?是不是太糟糕了以至于我们不得不改变它?如果是,我该如何改进?

提前致谢。

4

6 回答 6

6

好吧,setter 本身有副作用(该实例中的值在函数结束后被修改)。所以,不,我不会认为这是需要改变的坏事。

于 2013-03-01T04:44:09.497 回答
3

想象一下,守卫刚刚有一个SetAdmissionPolicy接受对AdmissionPolicy定义的引用:

interface AdmissionPolicy {
    boolean isAcceptable(String customerName) {
}

并将守卫的admissionPolicy字段设置为传入的引用。守卫自己的allowCustomerToPass方法简称为admissionPolicy.isAcceptable(customerName);.

鉴于上述定义,我们可以想象三个实现 的类AdmissionPolicy:一个将在其构造函数中接受一个列表,并为列表中isAcceptable的每个人返回 true,另一个也将在其构造函数中接受一个列表,但isAcceptable它只对人返回 true不在名单上。第三个将简单地无条件返回true。如果俱乐部偶尔需要关闭,可能还有第四个实现无条件返回 false。

以这种方式查看,setInvalidNames并且setValidNames都可以实现为:

public void setAdmissionPolicyAdmitOnly(List<String> newValidNames) {
    admissionPolicy = new AdmitOnlyPolicy(newValidNames);
}

public void setAdmissionPolicyAdmitAllBut(List<String> newInvalidNames) {
    admissionPolicy = new AdmitAllButPolicy(newInvalidNames);
}

通过这样的实现,很明显每种方法都只是“设置”一件事。这样的实现是我期望像你这样的类的行为方式。

但是,我认为您所描述的班级的行为充其量是可疑的。问题不在于添加承认的项目会清除被拒绝的项目,而是传入列表为空时的行为以一种相当奇怪的方式取决于早期状态。如果允许除 Fred 之外的每个人访问,那么将 setValidNames 调用为 none 应该没有任何效果,但如果它设置为只允许 George 访问,那么同一个调用应该授予每个人访问权限,这几乎不是直观的。setValidNames此外,虽然从包含在有效名称列表中的任何人中删除并不意外,invalidNames反之亦然,但考虑到函数的命名方式,设置一个列表会删除所有人来自另一个列表有点出乎意料(空列表的不同行为使其尤其如此)。

于 2013-12-29T20:03:27.780 回答
1

尽管它没有任何副作用,但开发人员假设 getter 和 setter 除了获取和设置变量之外可能没有任何底层代码。因此,当另一个开发人员试图维护代码时,他可能会忽略您的 Bean 代码并执行与您在 setter 中所做的相同的检查 - 可能是您所称的样板代码

于 2013-03-01T04:52:11.257 回答
0

我不认为这是副作用。您正在维护对象的基本假设。我不确定它是不是最好的设计,但它肯定是一个有效的设计。

于 2013-03-01T04:44:32.883 回答
0

在这种情况下,我认为更改其他链表不会产生副作用,因为范围在此类内。

但是,根据您的描述,最好设计一个linkedList(称为nameList)和一个boolean(isValid)来区分白名单和黑名单。这样很明显,任何时候都只能填写一种类型的列表。

于 2013-03-01T04:52:22.670 回答
0

我认为没关系。例如,如果你希望你的类是不可变的,那么最好的地方就是 setter:

public void setNames(List<String> names) {
       this.names = names == null ? Collections.emptyList() : Collections.unmodifiableList(names);
}
于 2013-03-01T05:04:58.483 回答