Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace switch statement with a map (for different data-types)

I am trying to replace the following switch statement with a map.

public ObjGeometry geometry(final Iterable<String> lines) throws IllegalArgumentException {
    final List<Vector3f> vertices = Lists.newArrayList();
    final List<Vector2f> textures = Lists.newArrayList();
    final List<Vector3f> normals = Lists.newArrayList();
    final List<Face> faces = Lists.newArrayList();

    for (final String line : lines) {
      final List<String> lineElements = Arrays.asList(line.split(" "));
      final String token = lineElements.get(0);

      switch (token) {
        case VERTEX:
          final Vector3f vertex = createVertex(lineElements);
          vertices.add(vertex);
          break;
        case TEXTURE:
          final Vector2f texture = createTexture(lineElements);
          textures.add(texture);
          break;
        case NORMAL:
          final Vector3f normal = createNormal(lineElements);
          normals.add(normal);
          break;
        case FACES:
          final Face face = createFace(lineElements);
          faces.add(face);
          break;
      }
    }
    return ObjGeometry.from(vertices, textures, normals, faces);
  }

The methods #createVertex,#createTexture,#createNormaland #createFace transforming a string to a special dataType (Vector2f, Vector3f or Face)

What i want to do is... to build a Map Map<TOKEN, Parser>, so I can iterate over that map and call the right parser for a special token (btw. TOKEN is a String like "v", "vt", "vn", or "f").

What i have is:

  • abstract classExtractVector3f with method #vectorFrom(List<String>) - can transform a List<String> to Vector3f
  • abstract classExtractVector2f with method #vectorFrom(List<String>) - can transform a List<String> to Vector2f
  • abstract classExtractFace with method #faceFrom(List<String>) - can transform a List<String> to Face
  • an interface LineParser

    public interface LineParser<T> {
      T apply(final List<String> lineElements);
    }
    
  • a class to parse a vertex

    public class ParseVertex extends ExtractVector3f implements LineParser<Vector3f> {
      @Override
      public Vector3f apply(final List<String> lineElements) {
        return vectorFrom(lineElements);
      }
    }
    
  • a class to parse a normal

    public class ParseNormal extends ExtractVector3f implements LineParser<Vector3f> {
      @Override
      public Vector3f apply(final List<String> lineElements) {
        return vectorFrom(lineElements);
      }
    }
    
  • a class to parse a texture

    public class ParseTexture extends ExtractVector2f implements LineParser<Vector2f> {
      @Override
      public Vector2f apply(final List<String> lineElements) {
        return vectorFrom(lineElements);
      }
    }
    
  • and a class to parse a face

    public class ParseFace extends ExtractFaces implements LineParser<Face> {
      @Override
      public Face apply(final List<String> lineElements) {
        return faceFrom(lineElements);
      }
    }
    

The next step would be to build the map... something like

    Map<String, LineParser> parsers = Maps.newHashMap();
        parsers.put("v", new ParseVertex());
        parsers.put("vt", new ParseTexture());
        parsers.put("vn", new ParseNormal());
        parsers.put("f", new ParseFace());

But now i get in trouble. LineParser in this map is a raw type and raw-types should be avoid.

My Question is: How can i build a map, with POJOs in it - which return different data-types on the method which is defined via Interface?

like image 820
SleepyX667 Avatar asked Jun 08 '26 20:06

SleepyX667


1 Answers

I would not do this, actually. Even if the map is a good idea, done this way you lose the exact type of the objects you build. What are you gonna do with them after that ? How do you know to which collection adding them ? You can't, unless the parsers have access to all collections to pick the right one. This suggests the parsers should know your main class, or methods of this class.

I would just use lambdas :

Consumer<List<String>> handleTexture = args -> textures.add(parseTexture(args))
Consumer<List<String>> handleVertice = args -> vertices.add(parseVertice(args))
...
Map<String, Consumer<List<String>> handlers = new HashMap<>();
handlers.put("vf", handleTexture);
handlers.put("v", handleVertice);
...

for (final String line : lines) {
      final List<String> lineElements = Arrays.asList(line.split(" "));
      final String token = lineElements.get(0);
      handlers.get(token).accept(lineElements.sublist(1, lineElements.size()));)
}

If you want to do it with Java 7 it is syntactically less nice, but the principle is the same. Create a interface like Consumer with a method accept (you can change all the names if you want to make the class more specific to your project, like Parser and parse, just keep the same signature for the method) :

public class Consumer<T> {
     public void accept(T t);
}

Then, in your code

Consumer<List<String>> handleTexture = new Consumer<List<String>>() { 
    @Override
    public void accept(List<String> args) {
        textures.add(parseTexture(args));
    }
}

The rest is just the same !

like image 153
Dici Avatar answered Jun 11 '26 09:06

Dici