在setter方法中做了别的事情被认为有副作用吗?

时间:2013-03-01 04:40:54

标签: java oop

最近我读过一些文章说有副作用的方法并不好。所以我只想问一下我在这里的实施是否可归类为副作用。

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

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

  • 如果SecurityGuard只有validNames,他只允许名单上的客户。
  • 如果SecurityGuard只有invalidName,他只允许名字不在列表中的客户。
  • 如果SecurityGuard根本没有列表,他就允许每个人。

为了强制执行逻辑,在每个列表的setter上,如果新列表有值,我会重置另一个列表。

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方法有一个隐式动作,它会重置另一个列表。

问题是我在这里做什么可能被认为有副作用?这是不是很糟糕,所以我们必须改变它?如果是的话,我该如何改进呢?

提前致谢。

6 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:3)

想象一下,警卫只有一个SetAdmissionPolicy接受了AdmissionPolicy定义的引用:

interface AdmissionPolicy {
    boolean isAcceptable(String customerName) {
}

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

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

以这种方式查看,setInvalidNamessetValidNames都可以实现为:

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

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

通过这样的实现,很明显每种方法只是“设置”一件事;这样的实现是我期望像你这样的课程表现的方式。

如上所述,你的班级的行为,我认为充其量是可疑的。问题不在于添加被接受的项目会清除被拒绝的项目,而是当传入列表为空时的行为取决于较早的状态以相当奇怪的方式。如果除了弗雷德之外的每个人都可以访问,那么调用setValidNames就不会产生任何效果,但是如果将其设置为仅允许George访问,那么同一个调用应该授予每个人访问权限。此外,尽管setValidNames将从invalidNames中删除包含在有效名称列表中的任何人,反之亦然,但考虑到命名函数的方式,设置一个列表的事实也不会出乎意料从其他列表中删除所有人有些意外(使用空列表的不同行为尤其如此)。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

public void setNames(List<String> names) {
       this.names = names == null ? Collections.emptyList() : Collections.unmodifiableList(names);
}