Refactor boolean based flow

时间:2016-10-20 19:11:13

标签: java design-patterns boolean refactoring api-design

Our application is built somewhat around a Context class with many primitive fields and booleans The Context is passed around in almost all of the flow, and decisions are taken based on the boolean flags. Now in order to implement new functionality, a new boolean was added to the context and it has to be checked in 10 different places. The general flow has structure similar to this example.

public void handle(context) {
  if (context.isBig())
     drawBigThing(context)
  else 
     drawSmallThing(context)
  //more code
  ...... handleColor(context) //somewhere deeper in the flow/stack

}

private void handleColor(context) {
  if (context.isBig())
     takeMoreColor(context.getColor())
  else
     takeLessColor(context.getColor())
}

As you see in different parts of the code we look back at the same flag but take different decisions based on it. Now if i add context.isVeryBig() you can see how this might explode.

What are some ideas (on java 8 tools) to refactor the boolean flag that is queried from methods/classes with different responsibilities but still interested in the same flag?

One idea would be to make the context smarter, not hold boolean flags but a State/Strategy for each of the responsibilities, but this leaks the responsibilities in the context (maybe somehow they can be decoupled?), i will still have the IFs but at least they will be grouped in one place and before the flow starts

1 个答案:

答案 0 :(得分:1)

It is a conceptual question. So, maybe I don't answer completely to it.

One idea would be to make the context smarter, not hold boolean flags but a State/Strategy for each of the responsibilities, but this leaks the responsibilities in the context (maybe somehow they can be decoupled?)

Using Strategy seems to be a good idea. Nevertheless, I would not make the context too clever. Context should contain data or the name is misleading and its responsibilities too.

You wonder if context and processing can be decoupled. You can and I think it should.

i will still have the IFs but at least they will be grouped in one place and before the flow starts

I agree. Why not in a factory class in order to communicate by contract and be more flexible in the returned implementation .

I propose you two ways of tackling the subject.

1) A flexible processing with full implementation of the handling by concrete handlers

The interface :

public interface FlowHandler{

     void handle(Context context);
}

and as implementations as needed (according to your booleaans which make the behavior of the flow to change) :

public class  BigFlowHandler implements FlowHandler {

     public void handle(Context context){
           drawBigThing(context);
           takeMoreColor(context.getColor())
    }
}

That design is flexible since it allows each implementation to handle concerns as it wishes.

Nevertheless, if operations should be of the same nature and executed in the same order for all handlers, you could use another design which pushes in this direction.

2) A predefined process which constraints steps to handle and factorize the processing of the steps

You can do it by declaring a concrete method (handle(Context context) in a concrete class (FlowHandler) which chains needed operations for any executed flow. The called operations relies on factory methods implemented by strategies.
The concrete class (FlowHandler) answers to the the WHAT question (the steps of the process and their order), the second answers to the HOW (the implementation of the steps of the process)

public interface HandlerOperations{
 drawThing(Context context);
 takeColor(Context context);
}

public class FlowHandler{

     public FlowHandler(HandlerOperations handlerOperations){
            this.handlerOperations = handlerOperations;
     }

     public void handle(Context context){
           handlerOperations.drawThing(context);
           handlerOperations.takeColor(context);
    }
}