Is a switch statement appropriate here, taking an enum?

orchid :

Today I was making a Tetris clone in Java, and when time came to implement the block spawning mechanism, I wrote this switch statement that takes an enum. I've been told to avoid switch statements when possible, but I'm not sure if avoiding one here is possible unless I completely overhaul my original inheritance based design choice.

To implement the different type of blocks, I decided to make an abstract super class called Block and make a child class for every specific type. I also made an enum that marks a block's type. For instance, a ZBlock extends Block object would have Type.ZBlock enum assigned to its Type field variable.

private void spawnBlock(Type type){
        switch(type){
            case I:
                currentBlock = new IBlock();
                break;
            case L:
                currentBlock = new LBlock();
                break;
            case J:
                currentBlock = new JBlock();
                break;
            case Z:
                currentBlock = new ZBlock();
                break;
            case S:
                currentBlock = new SBlock();
                break;
            case T:
                currentBlock = new TBlock();
                break;
            default:
                currentBlock = new OBlock();
        }
    }

When I starting thinking about ways to not use a switch statement here, I couldn't think of anything other than getting rid of all of my child classes and programming the behaviors of the different blocks in a single non-abstract Block class. But it might become a mess since the different blocks have different shapes and wall kick data. So is the switch statement an ok choice here? If not, how can I improve my code and why?

GhostCat salutes Monica C. :

I've been told to avoid switch statements when possible

That is correct, but what you are doing here might be okay.

The point is: you don't want to have such switch statements all over the place. They are bad because they couple things together. And when you repeat the "same" switch pattern over and over again, that can turn into a maintenance nightmare. Because changes to your enum type ... means that you will have to look at each switch to determine if it needs to be adapted.

So, when you are able to hide such switching from most of your code (by doing it only in a few, ideally, one place) you are okay.

Nonetheless, a better approach could be to replace that whole switch statement with this one-liner:

currentBlock = type.createBlock();

In other words: why not put the knowledge directly into that enum class itself? Of course, that has other implications, such as a potential violation of the single responsibility principle.

But it feels quite natural that the enum that denotes the different types of blocks also provides a means to create such blocks (given the fact that the type is the only information that determines what kind of Block subclass you need).

And note: you don't necessarily move the switch into the enum. You can fully avoid switching here:

enum Type {
  I(IBlock::new), L(LBlock::new), ...;

  private Supplier<? extends Block> supplier;
  private Type(Supplier<? extends Block> supplier) {
    this.supplier = supplier;
  }

  public Block getBlock() {
    return supplier.get();
  }

( I didn't run the above through a compiler, so beware of typos )

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=138668&siteId=1