7

I want to avoid using tagged classes and big if-else blocks or switch statement and use polymorphism with a class hierarchy instead, which I believe is better practice.

For example, something like the below, where the choice of executed method is dependent only one one field of an object of type Actor.

switch(actor.getTagField())
{
    case 1: actor.act1(); break;
    case 2: actor.act2(); break;
    [...]
}

would become

actor.act();

and the act method would be overridden in subclasses of Actor.

However, the most obvious way to decide at runtime which subclass to instantiate looks awfully similar to the original:

Actor newActor(int type)
{
    switch(type)
    {
        case 1: return new Actor1();
        case 2: return new Actor2();
        [...]
    }
}

so it seems like nothing has really been gained; the logic has just been moved.

What is a better way to do this? The only way I can come up with involved implementing a factory class for each subclass of Actor, but this seems rather cumbersome for such a simple problem.

Am I overthinking this? It just seems like there's no point making the original change if I just do pretty much the same thing elsewhere.

3 Answers 3

4

Question is "if" you need a factory. The factory is meant to manage the creation of instances an not so much the behavior of related instances.

Otherwise, you're just looking at basic inheritance. Something like..

class Actor{
  public void act(){
    System.out.println("I act..");
  }
}

class StuntActor extends Actor {
  public void act(){
    System.out.println("I do fancy stunts..");
  }
}

class VoiceActor extends Actor {
  public void act(){
    System.out.println("I make funny noises..");
  }
}

To Use, you can just instantiate the type of actor you need directly.

Actor fred = new Actor();
Actor tom = new VoiceActor();
Actor sally = new StuntActor();

fred.act();
tom.act();
sally.act();

Output:

I act..
I make funny noises..
I do fancy stunts..

EDIT:

If you need to centralize the creation of the Actors..aka vis a Factory, you will not be able to get away from some kind of switching logic--in which case..i'll typically use an enumeration for readability:

public class Actor{
  public enum Type{ REGULAR, VOICE, STUNT }

  public static Actor Create(Actor.Type type){
    switch(type) {
      case VOICE:
        return new VoiceActor();
      case STUNT:
        return new StuntActor();
      case REGULAR:
      default:
        return new Actor();
    }
  }

  public void act(){
    System.out.println("I act..");
  }
}

Usage:

Actor some_actor = Actor.Create(Actor.Type.VOICE);
some_actor.act();

Output:

I make funny noises..
Sign up to request clarification or add additional context in comments.

6 Comments

Sorry, to clarify, I want to choose which subclass to instantiate at runtime, not at compile time as in your example. This requires a large switch or if-else block, which I wanted to avoid. I'm starting to think that isn't necessary because it would only be needed once at creation.
Hmm..actually the factory has little to do w/ compile vs runtime instantiation per se, rather the responsibility and control of the creation of Actors. For example if the above creation of the Actors happened in a Event, would it not be runtime?
What you're doing is "deciding" to centralize the creating of the Actors..ie you want the Actors coming from one place, in which case..you can't get around the switching...because that's a decision of the end user.
You're right to point out that ..if you put the switching logic at the method level ..then that switching logic would need to be duplicated should the Actor get additional methods; ie if Actors learn to Sing().
Yes, that's what I was missing because of my over-simple example. Thanks for your help, I ended up using an if-else block in my actual code. An enumeration wasn't really suitable for my specific project, but that's still a good idea in general.
|
3

Switch statements aren't pure evil. It's really duplication that you're looking to eliminate with better design. Often times you'll find the same switch statement show up in different (far away) places in your code - not necessarily doing the same thing, but switching on the same data. By introducing polymorphism, you pull those switches together as different methods of the same object.

This does two things, first it reduces several switches to one switch inside of a factory and it pulls together spread out logic that probably depends on similar data. That data will turn into member variables in your objects.

It's also worth noting that you don't always end up with a switch statement under the hood of your factory. Maybe you could scan the classpath at startup and build a HashMap of types that implement an interface. For example, consider an implementation of a socket protocol like SMTP. You could have objects named HeloCommand, MailFromCommand, etc... and find the right object to handle the message by matching the socket command to the class name.

2 Comments

I actually did consider doing something like that, but then started reading about how reflection should be a last resort.
Reflection is a powerful tool and there are ways to use it without sacrificing performance. The example I described in my answer would only use reflection once at start up and use a HashMap after that.
2

I believe that you can do it with Abstract factory pattern...

This is a example:

abstract class Computer {
    public abstract Parts getRAM();
    public abstract Parts getProcessor();
    public abstract Parts getMonitor();
}

class Parts {
    public String specification;
    public Parts(String specification) {
        this.specification = specification;
    }
    public String getSpecification() {
        return specification;
    }
}

We have two class that extends Computer

class PC extends Computer {
    public Parts getRAM() {
        return new Parts("512 MB");
    }
    public Parts getProcessor() {
        return new Parts("Celeron");
    }
    public Parts getMonitor() {
        return new Parts("15 inches");
    }
}

class Workstation extends Computer {
    public Parts getRAM() {
        return new Parts("1 GB");
    }
    public Parts getProcessor() {
        return new Parts("Intel P 3");
    }
    public Parts getMonitor() {
        return new Parts("19 inches");
    }
}

And finally we have,

public class ComputerType {
    private Computer comp;
    public static void main(String[] args) {
        ComputerType type = new ComputerType();
        Computer computer = type.getComputer("Workstation");
        System.out.println("Monitor: "+computer.getMonitor().getSpecification());
        System.out.println("RAM: "+computer.getRAM().getSpecification());
        System.out.println("Processor: "+computer.getProcessor().getSpecification());
    }    

    public Computer getComputer(String computerType) {
        if (computerType.equals("PC"))
            comp = new PC();
        else if(computerType.equals("Workstation"))
            comp = new Workstation();
        return comp;
    }    
}

1 Comment

I did look this up beforehand, but I thought it was probably unnecessarily complicated for what I wanted to do. I'm not sure.

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.