将布尔值包装在对象中

4

我有一些代码需要重构。我有许多方法需要传入多个相同类型的参数,例如:

public void foo(String name, String street, boolean b1, boolean b2) { ... }

等等。因为不同的对象只能通过名称来区分,我希望将它们包装在对象(枚举)中,以便我可以利用语言的类型系统(在这种情况下是Java)。

public class Name {
    private String value;
    public String getValue() { return value; }
    // ...
}

这样我可以强制调用代码传递特定类型的对象。这将确保不会意外混淆方法参数的顺序,从而在运行时不会产生意外行为

foo(new Name("John"), new Street("Broadway"), new B1(true), new B2(false);

这使得重构更加安全,只要您想,就可以在系统中一直使用对象,其中的数据,即字符串,始终是安全的。只有在需要时,通过调用getValue()才能获取它。
现在,对于包装字符串的对象,很容易理解,因为实例可能处于许多状态中。
但是布尔类型的包装器怎么办?它们只有TRUE或FALSE两种状态。实现看起来有点奇怪:
public enum Quanto {

    YES() {
        protected boolean isQuanto() {
            return true;
        }
    },
    NO() {
        protected boolean isQuanto() {
            return false;
        }
    };

    protected abstract boolean isQuanto();

}

更奇怪的是我发现调用代码看起来像这样:

public void doStuff(Quanto quanto) {
    if(quanto.isQuanto()) {
        // ...
    }
}

从技术上讲当然没有关系,但感觉不太对劲...您有没有发现“更好”的处理方式?

编辑:让我不满的是,在上面的例子中,可能会有比YESNO更多的值,比如MAYBE...?!

谢谢!


你的代码让我想到了这个反面模式:http://www.martinfowler.com/bliki/AnemicDomainModel.html - SteveD
1
基本上,它非常过程化而不是面向对象的。 - SteveD
这并不一定是件坏事... - me22
1
请查看Robert C. Martin的《Clean Code Tip #12: 消除布尔参数》(http://www.informit.com/articles/article.aspx?p=1392524)。 - Pascal Thivent
4个回答

9
我建议将布尔值包装成语义化的枚举类型。换句话说:
public void doStuff(boolean isActive, boolean wrapResult);

成为

public enum State {ACTIVE, INACTIVE};
public enum ResultMode {WRAPPED, UNWRAPPED};

public void doStuff(State state, ResultMode resultsMode);

1
这个。简洁、简单、简明。 - CPerkins
是的。但这正是我提出的,不是吗?您只需放弃isQuanto方法,并让客户端代码执行if(q == Quanto.YES)... - raoulsson
在我看来,doStuff(State.ACTIVE, Mode.UNWRAPPED)doStuff(Active.YES, Wrapped.NO) 更自然且更易于理解。 - ChssPly76

4

基本上你正在做的是命名参数,所以我建议每个函数都有一个类,每个参数都有对应的成员变量。

调用时应该看起来像这样:

foo(new foo_arguments().Name("John").Street("Broadway").B1(true).B2(false));

然后你可以在函数内使用arguments.Name()等参数。

这样做的额外好处是,让你可以给出默认参数而不需要大量重载函数,并且让参数可以以任意顺序指定,所以这个也可以工作:

foo(new foo_arguments().Street("Sesame").Name("Monster"));

所需类:

public class foo_arguments {
    private string _name = "John Doe";
    public foo_arguments Name(string name) { _name = name; return this; }
    public string Name() { return _name; }

    private string _street = "Pennsylvania Ave NW";
    public foo_arguments Street(string street) { _street = street; return this; }
    public string Street() { return _street; }

    private string _b1 = false;
    public foo_arguments B1(string b1) { _b1 = b1; return this; }
    public boolean B1() { return _b1; }

    private string _b2 = true;
    public foo_arguments B2(string b2) { _b2 = b2; return this; }
    public boolean B2() { return _b2; }
}

作为附注,在C#中,您可以使用自动属性和对象初始化程序来实现非常优雅的操作。

根据源代码的外观,有望识别出一个或多个对象,其意义比foo_arguments更深刻。我不知道上面示例中的布尔值是做什么用的,但也许你可以创建一个Person类,包含姓名、街道、b1和b2等属性。尝试识别您想要的对象,而不是传递基本类型。 - Buhb

2

这些方法是否总是采用相同的参数集?如果是,也许您想创建一个存储所有数据的数据对象,并将此对象仅提供给函数。也许您甚至可以在以后将函数移动到对象中。 具有许多输入参数的方法有时暗示着缺少一个适合所有这些参数的对象。

我不会费心将一个布尔值封装在一个对象中。对我来说,这似乎不对。


1

为什么不为传递到方法中的不同值创建封装对象,而不是使用布尔枚举?这样,这些值就可以通过getter和setter进行访问,并且具有您想要的信息:

public class Encapsulator {

    private boolean isSelected;
    private boolean isAwake;


    public boolean isAwake() {
        return isAwake;
    }

    public void setIsAwake(boolean isAwake) {
        this.isAwake = isAwake;
    }

    public boolean isSelected() {
        return isSelected;
    }

    public void setIsSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }
}

这样,当您访问数据时,非常清楚对象中的特定元素执行一项或另一项操作。它还减少了方法的参数集,这是根据Martin Fowler的代码异味。


我个人喜欢参数耦合。当然,你也可以把它搞得一团糟,但是相比其他构造(特别是涉及添加额外类的情况),更喜欢使用额外的参数通常会产生更好的解决方案,在我看来。 - eljenso
问题在于当有很多参数被传递时 - 这就是该模式旨在解决的问题。如果只有2或3个(甚至4个),或者它们只被传递一次,那就完全是另一种情况了。 :) - aperkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接