Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initiate a subclass object from a string

Consider a multiplayer fighting game, where the client selects a fighter by sending to the server a string that contains the name of the fighter the client just selected.

Consider this "fighters" class structure in the server:

Fighter
   Foo extends Fighter
   Bar extends Fighter
   Qux extends Fighter

For instance the client sent this "Bar" string, how can I initiate a subclass according to this string?

The way I did it is an enum like so:

public enum Champion {
    Foo {
        @Override
        public Fighter getNew() {
            return new Foo();
        }
    },

    Bar {
        @Override
        public Fighter getNew() {
            return new Bar();
        }
    },

    Qux {
        @Override
        public Fighter getNew() {
            return new Qux();
        }
    };

    public abstract Fighter getNew();

    public static boolean contains(String fighter) {
        for (Champion c : Champion.values()) {
            if (c.name().equals(fighter)) {
                return true;
            }   
        }
        return false;
    }
}

Then I am getting an object like so:

public Fighter getFighter(String fighterName) {
    if(Champion.contains(fighterName)) {
        return Champion.valueOf(fighterName).getNew();
    } else { 
       return null; 
    } // For example sake only :)
}

Is this the correct way to deal with this encounter? is there a better way? I remember there is a known pattern to deal with such encounters, but cannot remember exactly which.
If you have an idea please let me know!

like image 999
julian Avatar asked Apr 15 '26 22:04

julian


1 Answers

Using factories, a solution could look like this:

interface FighterFactory {
    Fighter create();
}

Keep a Map<String, FighterFactory> variable in your facade. Each Fighter type has to be registered in this map with its Factory.

private Map<String, FighterFactory> fighters;

public void register(String name, FighterFactory factory) {
  fighters.put(name, factory);
}

In each Fighter type embed an inner class Factory e.g.

class Foo extends Fighter {

  //code for Foo

  public static class Factory implements FighterFactory {
    public Foo create() {
      return new Foo();
    }
  }
}

// and in your Facade

register("Foo", new Foo.Factory());

To instantiate a new Fighter do a get to the Fighter-Map and execute create on its result.

FighterFactory factory = fighters.get(nameFromClient);
// null handling, if nameFromClient is not a Fighter type;
return factory.create();

This design has some advantages over reflection:

  • it is secure
  • the create method can do more than a constructor

and over the single factory method (one method handles all Fighter types):

  • if the create method gets more complex (e.g. more params to create the instance) than your changes to the code are located at the Fighter class and not at the factory method.

and over the enum approach:

  • the only feature of Enum that is used is Iterating over its values. Keeping a Map of values replaces the Iterating with a Map lookup
  • the information how to construct a Fighter resides located near the Fighter and not in the Champion Enum
  • you can even decide that FighterFactories are Top-Level classes, what is not possible with the enum inner class Factories.
like image 93
CoronA Avatar answered Apr 17 '26 10:04

CoronA