2

I should use polymorphism over conditionals but can I use it in below case ?

Below code returns a Screen object depending on contents of jsonObject.

public static Screen getNextScreen(){

        JSONObject jsonObject = RatingsUtils.getCurrentJsonObjectFromServer();

        if(isNextProgramScreen(jsonObject)) {
            ParentRatingsObject parentRatingsObject = JsonBusinessObjectFactory.createParentRatingsObject(jsonObject);
            return new NextProgramScreen(parentRatingsObject);
        } 
        else if(isTimerScreen(jsonObject)) {
            ChildWithParentRatingsObject childWithParentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
            return new TimerScreen(childWithParentRatingsObject);
        } 
        else if(isNextContestantPreJudgeScreen(jsonObject)) {
            ChildWithParentRatingsObject childWithParentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
            return new NextContestantPreJudgingScreen(childWithParentRatingsObject);
        } 
        else if(isNextContestantJudgeScreen(jsonObject)) {
            ChildWithParentRatingsObject childWithParentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
            return new TimerScreen(childWithParentRatingsObject);
        } 
        else {
            return null;
        }
}
4
  • 4
    I think that depends on what the is...Screen() functions do. Commented Jul 14, 2011 at 18:54
  • I agree with @Matt. we need to know what you do to check return object inside is...Screen() functions. Commented Jul 14, 2011 at 19:10
  • The is...Screen() functions check a value attribute within the jsonObject, so if the attribute is equal to a pre-defined string value then it returns true. Commented Jul 14, 2011 at 19:12
  • @user470184 In that case you can get rid of all the is...() methods by creating a type enum and using a HashMap<String,JSonObjectType>. You can then have a single getJSonObjectType() method that looks the enum value up in the map. And you can replace your if...else chain with a switch-case construct. Commented Jul 14, 2011 at 19:50

3 Answers 3

4

Absolutely. I've been taking the polymorphic approach more often myself, and I really like it. Because of Java the language, this will look kind of bloated. We really need lambda expressions to do this properly!

private static List<ScreenProvider> screenProviders = screenProviders();

public static Screen getNextScreen(JSONObject jsonObject) {
    for (ScreenProvider screenProvider : screenProviders) {
        if (screenProvider.supports(jsonObject)) {
            return screenProvider.getScreen(jsonObject);
        }
    }
    return null;
}

interface ScreenProvider {
    boolean supports(JSONObject jsonObject);
    Screen getScreen(JSONObject jsonObject);
}

private static List<ScreenProvider> screenProviders() {
    return Arrays.asList(
            new ScreenProvider() {
                public boolean supports(JSONObject jsonObject) {
                    return isNextProgramScreen(jsonObject);
                }

                public Screen getScreen(JSONObject jsonObject) {
                    ParentRatingsObject parentRatingsObject = JsonBusinessObjectFactory.createParentRatingsObject(jsonObject);
                    return new NextProgramScreen(parentRatingsObject);
                }
            },
            new ScreenProvider() {
                public boolean supports(JSONObject jsonObject) {
                    return isTimerScreen(jsonObject);
                }

                public Screen getScreen(JSONObject jsonObject) {
                    ChildWithParentRatingsObject childWithParentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
                    return new TimerScreen(childWithParentRatingsObject);
                }
            },
            new ScreenProvider() {
                public boolean supports(JSONObject jsonObject) {
                    return isNextContestantPreJudgeScreen(jsonObject);
                }

                public Screen getScreen(JSONObject jsonObject) {
                    ChildWithParentRatingsObject childWithParentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
                    return new NextContestantPreJudgingScreen(childWithParentRatingsObject);
                }
            },
            new ScreenProvider() {
                public boolean supports(JSONObject jsonObject) {
                    return isNextContestantJudgeScreen(jsonObject);
                }

                public Screen getScreen(JSONObject jsonObject) {
                    ChildWithParentRatingsObject childWithParentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
                    return new TimerScreen(childWithParentRatingsObject);
                }
            }
    );
}

A huge benefit of this approach is the simplicity of your getNextScreen() method. With some thought, it may be possible to make the screenProviders() method more compact--perhaps by adding an abstract class that implements ScreenProvider and pulls out some of the work.

Sign up to request clarification or add additional context in comments.

2 Comments

@Matt: I'll have to ask you to defend that statement. "Polymorphism: ... the ability to create a variable, a function, or an object that has more than one form ... the ability of objects belonging to different types to respond to method, field, or property calls of the same name, each one according to an appropriate type-specific behavior." The implementations of ScreenProvider exhibit classic polymorphism.
It's definitely not the straightforward polymorphism OP might have been after. On the other hand this doesn't really matter as it is a really neat solution.
3

At some place you will have to determine if your JSON object represents particular type of screen. So you will have to do those if comparisons somewhere. I don't quite see much improvements for your factory method in terms of polymorphism, but you can provide some utility method that returns screen type as enum and build switch statement with it. It may look slightly nicer. But overall improvement value of it will be limited.

2 Comments

It will be limited until you need type-dependent branching somewhere else. Then it suddenly becomes a good idea, especially if you then go on and add another type...and so on. Most of the advantages of well-organized code are only visible in motion.
@biziclop +1 for visible in motion. Much will depend on future of this code, though. Hard to predict future...
1

EDIT: I completely changed my answer as suggested by @Mike Deck.

Using Enum and polymorphism:

public Enum ScreenType {
   NEXT_PROGRAM() {
     @Override public ScreenType generateScreen() {
        ParentRatingsObject parentRatingsObject = JsonBusinessObjectFactory.createChildWithParentRatingsObject(jsonObject);
        return new NextProgramScreen(parentRatingsObject);
     }
   },
   TIMER() {@Override...},
   NEXT_CONSTESTANT_PREJUDGE() {@Override...},
   NEXT_CONSTESTANT_JUDGE() {@Override...};

   public static ScreenType getScreenType(JSONObject jsonObject) {
      // basically rewrite your methods isXXXXXXX(jsonObject) here
   }

   public abstract Screen generateScreen();
}

And there is basically nothing left to be done in your original method:

public Screen generateScreen() {
   JSONObject jsonObject = RatingsUtils.getCurrentJsonObjectFromServer();     
   ScreenType screenType = ScreenType.getScreenType(jsonObject));
   return screenType.generateScreen();
}

1 Comment

An enum can be used polymorphically. You can create an abstract method in the enum itself and then each of the enum instances can implement it differently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.