Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding Nested If Else / Switches - Java

I am reviewing some code(Java) and making changes based on a business logic flow chart. The current code relies on a large amount of if statement's - something I want to try and move away from. I have been reading about Polymorphism and being trying to wrap my head around how to apply it to my situation. I can make it work for a single level of conditionals, but struggling to extend it further across multiple conditional levels. The code will be executed at run time, with this 'Logic' method being passed the variables from a previous step.

Contrived Example: We have 2 Zoo's, 'Zoo A' and 'Zoo B', and 'Home'. Each of these is a 'Place'. In each Zoo we have 4 'locations', 'North', 'South', 'East' and 'West'. 'Home' only has one location. We want to assign a 'destination' to a person on where they should go based on a few variables. These variables are: 'Place', which correlate to our places (Zoo A, Zoo B, Home). 'Direction', which correlate to our locations, (N,S,E,W). Flow Chart:

                |----- | 'HOME'
                |Place?| ----- >  *destination = 'home'*
                |----- |
     Zoo A          |                               Zoo B
    |---------------|----------------------------------------|
|----------|                                        |----------| 
|Direction?|                                        |Direction?| 
|----------|                                        |----------|
    |    North                                          |    North
    ----------- *destination = 'Zoo A North'            ----------- *destination = 'Zoo B North'
    |    East                                           |    East
    ----------- *destination = 'Zoo A East'             ----------- *destination = 'Zoo B East'
    |    South                                          |    South
    ----------- *destination = 'Zoo A South'            ----------- *destination = 'Zoo B South'
    |    West                                           |    West
    ----------- *destination = 'Zoo A West'             ----------- *destination = 'Zoo B West'

So If Person X has a Place of Zoo A and a Direction of South they should have a Destination of 'Zoo A South'

I have code that is currently pretty ugly using If statements:

if(Place = 'HOME')
    destination = 'HOME'
if(Place = 'Zoo A')
    if(Direction = North)
        destination = 'Zoo A North')
    if(Direct = East)
        destination = 'Zoo A East')
    ...
if(Place = 'Zoo B')
    if(Direction = North)
        destination = 'Zoo B North')
    if(Direct = East)
        destination = 'Zoo B East')
    ...

I could turn this in to nested switches with the variables as ENUMs. But I am trying to avoid the if - else / switch reliance as I have a bad habit of falling into it. I experimented with using a Factory Design to generate Place classes, then used Polymorphism on each location and destination but it started to get overly complicated in my head. Is it even worth moving away from if/switches? Am I just trying to over engineer it?

Any suggestions on how to tackle logic flows like this? Thanks

like image 577
NathanS Avatar asked Oct 22 '22 20:10

NathanS


1 Answers

This could be modelled like this:

  1. Use a root class Place, with method calculateDestination(Person). Place can consist of other places inside of it.
  2. Create Place subclasses for Zoo and ZooQuadrant (naturally, as these are actual places).
  3. The Person object has values for currentPlace and currentDirection

Now you'd instantiate objects of these classes to represent your situation:

zooA = new Zoo("ZooA");
zooA.addChild(new ZooQuadrant(Direction.SOUTH));
... so on for every quadrant ...
... same for zooB ...
home = new Place("Home");
world = new Place("World");
world.addChild(home);
world.addChild(zooA);
world.addChild(zooB);

When you want to get the destination, you would call world.calculateDestination(myPerson)

calculateDestination(Person) is the polymorphic method. Each level in the inheritance hierarchy will override it according to the specific semantics of that class.

  1. Place will have a generic implementation that will test if the Person instance is currently at that node (by testing against the Person's value for currentPlace) and if not, it will call calculateDestination on each of its children and return that.
  2. Zoos will need to check if currentPlace == this, and if so, call calculateDestination on each of its quadrants and combine any positive result with its own to return this.name + quadrantResult.
  3. Each ZooQuadrant just needs to check if the currentDirection is equivalent to its own direction, and return a value accordingly.

Note: this is just to illustrate how polymorphism may work, there may be better implementations. Also, here we're using both polymorphism and recursion, the two are independent.


EDIT:

As for whether the added complexity is warranted, that depends! Here we're working with a simple example with an object graph that is quite small. As soon as you have tens of zoos, have to add more quadrants in those zoos, or extra levels of decisions need to be made (if for example each quadrant has subquadrants), the nested if-else-if method (procedural) gets really hairy really quickly, whereas the object oriented approach remains maintainable and understandable.

Like everything, if you foresee that decisions are going to get that complex, go with the OO approach. Otherwise, keeping it simple trumps beauty every time: use the right tools for the right problems.

like image 159
Ezequiel Muns Avatar answered Nov 09 '22 05:11

Ezequiel Muns