Chain Method Design

I wrote a simple class to demonstrate the design of the chaining method:

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); } } 

Thus, the client code can be:

 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 

My question is: is this a reasonable design? compared to: int plus(int a, int b, boolean useAbs, boolean checkArguments) . Thanks!

+7
source share
4 answers

It looks like you need a free interface to create a service class. Guva does similar things. You would do something like this:

 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; } } 

Usage will look like this:

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

This is called a free interface , and it looks pretty reasonable to me.

One thing I can propose to change is the class name: Calc is more of a calculator than Cal (which can be a calendar, not a calculator).

+4
source

The best design is simple design. What you do is code obfuscation. It is more difficult to read and correct errors than "plus (a, b, useAbs, checkArguments)". Also, in useNormal, you are returning the same object as the β€œnew Cal ()” because you are overriding the check (int) method and returning the parent implementation with super.check (int)

+2
source

IMHO, this "chaining" approach is not used, as a rule, in Java, except when builders or things are essentially builders.

What you describe here is actually a calculator calculator.

You should probably have the CalculatorBuilder class that you create, install a few things (for example, useAbs, checkArguments, etc.) and ultimately call it "build". Build will return a calculator that does not know anything about how it was created, except for the state in which it was initialized.

In addition, I personally do not like the design that mixes the builder style logic (for example, "useAbs") and everything that affects the state of the underlying object (for example, checkArguments). I would say choose one. Either generate a default calculator, or install it later, or create everything to create everything, and then create instances whose functionality and behavior cannot be changed.

+1
source

All Articles