【问题标题】:Chain style method design链式方法设计
【发布时间】:2011-04-25 15:22:13
【问题描述】:

我写了一个简单的类来演示链式方法设计:

public class Cal {

    private Cal(){}

    private boolean isCheckArguments = false;

    public static Cal useAbs() {
        return new Cal(){   
          @Override int check(int i) {
                return Math.abs(i);
            }};
    }

    public static Cal useNormal() {
        return new Cal();
    }   

    public Cal checkArguments() {
        isCheckArguments =true;
        return this;
    }

     int check(int i){ return i;}

     public int plus(int i, int j) {    
        if(isCheckArguments && i<j){
            throw new IllegalArgumentException("i<j!");
        }
        return check(i+j);
    }
}

所以客户端代码可以是:

Cal cal = Cal.useAbs().checkArguments();
int sum = cal.plus(100,2);//IllegalArgumentException occurs

Cal cal2 = Cal.useAbs();
int sum2 = cal.plus(-100,2);//98

Cal cal3 = Cal.useNormal();
int sum3 = cal.plus(-100,2);//-98

我的问题是:这是一个合理的设计吗?比较:int plus(int a, int b, boolean useAbs, boolean checkArguments)。谢谢!

【问题讨论】:

    标签: java oop


    【解决方案1】:

    听起来你想要一个流畅的接口来构建一个服务类。番石榴做类似的事情。你会做这样的事情:

    public interface Cal {
       int plus(int a, int b);
    }
    
    public class CalBuilder {
        class InternalCal implements Cal {
           boolean useAbs;
           boolean checkArgs;
           public int plus(int a, int b) {
              if(checkArgs) {
                 // blah, blah blah
              }
              if(useAbs) {
                 // doodle bug, doodle darn
              }
              return a+b; // whatevs
           }
        }
        boolean absSet=false;
        InternalCal holder=new InternalCal();
        public CalBuilder useNormal() {
            if(absSet) { throw new IllegalArgumentException(); } // already called
            holder.useAbs=false;    
            absSet=true;
            return this;
        }
    
        public CalBuilder useAbs() {
            if(absSet) { throw new IllegalArgumentException(); } // already called
            holder.useAbs=false;    
            absSet=true;
            return this;
        }
    
        public CalBuilder checkArguments() {
           if(holder.checkArgs) { throw new IllegalArgumentException(); }
           holder.checkArgs=true;
           return this;
        }
    
        public Cal build() {
           return holder;
        }
    }
    

    用法如下所示:

    Cal cal=new CalBuilder().useAbs().checkArguments().build();
    int sum=cal.plus(1,2);
    

    【讨论】:

    • 很确定CalBuilder 应该是一个static 类,那么它会被这样调用:Cal cal = new Cal.Builder()...
    • 当然,您可以通过多种方式做到这一点。但是,您通常不希望构建器是静态的,因为那样您将无法从多个线程中调用它,并且您会遇到与彼此的状态发生冲突的事情。
    • 我想你误会了我。构建器对象不会是静态的,Builder class 会是静态的。见Effective Java, Item 2(和/或this answer
    • 是的,我误解了你的意思。 :)
    【解决方案2】:

    这叫做fluent interface,对我来说看起来很合理。

    我可能建议更改课程名称:CalcCal 更明显是计算器(可能是日历而不是计算器)。

    【讨论】:

    • Calculator 会更好。完整的单词通常更容易阅读。如果你想命名它的一个实例calc,那很好,但不要命名那个类型。
    【解决方案3】:

    最好的设计是简单的设计。你正在做的是混淆代码。它比“plus(a, b, useAbs, checkArguments)”更难阅读和修复错误。此外,在 useNormal 中,您返回的对象与“new Cal ()”相同,因为您覆盖了 check(int) 方法并使用 super.check(int) 返回父级的实现

    【讨论】:

    • 是的。在 post 中,new Cal() 在 useNormal 中返回。
    【解决方案4】:

    恕我直言,这种“链接”方法在 Java 中并不常用,除非是构建器或本质上是构建器的东西。

    您在这里描述的实际上是一个计算器构建器。

    您可能应该有一个 CalculatorBuilder 类,您可以对其进行实例化、设置多项内容(例如 useAbs、checkArguments 等),并最终调用“build”。 Build 将返回一个计算器,除了初始化的状态外,它对它是如何构建的一无所知。

    另外,我个人不喜欢混合了构建器风格的逻辑(例如,“useAbs”)和影响底层对象状态的东西(例如 checkArguments)的设计。我会说选一个。要么生成一个默认计算器并稍后设置所有内容,要么让一个构建器设置所有内容,然后创建其功能和行为无法更改的实例。

    【讨论】:

    • 构建器模式通常用于构造不可变对象和/或用许多(可能是可选的)参数替换构造器。我在这里没有看到任何一个。
    • 我不同意——至少从他的例子来看,每个计算器的运行方式似乎完全不同,让计算器改变其状态没有任何好处
    猜你喜欢
    • 2014-05-29
    • 2016-01-21
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    相关资源
    最近更新 更多