How to do Single Responsibility Principle when implementing Chunk functionality like in Minecraft?

232 views Asked by At

I'm trying to create functionality for a Chunk (Like the chunk we see in Minecraft that consists of 16x16x256 blocks).

How should i go about doing that while achieving the Single Responsibility Principle?

Functionality includes things like loading, saving, creating a mesh and keeping track of all the blocks.

Should i

  1. Create a Chunk class consisting of many classes, each with one of these functionalities like loading or saving. Then initiate the classes in the Chunk class. What do i do when these classes needs functionality from the Chunk class, won't that break SRP?

  2. Create all functionality in the Chunk class

1

There are 1 answers

0
sisyphus On BEST ANSWER

So, if we're talking general pointers here then there's some simple things to start with.

  • your Chunk class should only deal with its primary responsibility - managing whatever is contained within a Chunk.
  • it sounds like you need a ChunkGroup, potentially using an Adapter pattern on the Chunk, which aggregates a 'network' of Chunks.
  • some means of persisting Chunk objects should exist. This is not the responsibility of the Chunk itself, but may involve an inner class which allows converting a Chunk into something which can be (de)serialised.

So, I'd be expecting to see something like

interface Chunk {
    String doTheThing();
}

interface ChunkNetwork extends Chunk {
     add(Chunk chunk);
};

final class ChunkList implements ChunkNetwork {
    private List<Chunk> theChunks;

    add(Chunk chunk) {
        theChunks.add(chunk);
    }

    String doTheThing() {
        StringBuilder builder = new StringBuilder();
        for (Chunk chunk : theChunks) {
            builder.append(chunk.doTheThing());
        }
        return builder.toString();
    }
}

final class JsonChunk implements Chunk {
     @JsonProperty
     private final int someField;

     @Override
     public String doTheThing() {
         String.format("%d", someField);
     }

     @JsonCreator
     public JsonChunk(int fieldValue) {
         someField = fieldValue;
     }
 }

The points to note about the SRP here:

  • the Chunk interface only contains methods which relate to the core functionality which a Chunk is supposed to do.
  • an adapter extracts the functionality for creating a 'network' of Chunk objects into a separate class. There are different ways of managing such kinds of networks, you may want to change that later. You can do so without altering how the Chunk itself works.
  • the ChunkNetwork may have different methods depending on your usage, and that may inform how you choose to implement the interface, but the key point is that network management is outside of the Chunk itself.
  • how Chunk objects are represented in XML or JSON (or whatever, on disk, in a file) is a separate concern from what the API of the Chunk is. However, with libraries like Jackson we can often get away without defining a separate class for marshalling/unmarshalling. If your Chunk implementations become complex you may want to however.

Ultimately, the SRP says that any individual class should have one reason to change. So you need to look at your functionality and look at why it might change. Typical reasons are:

  • changes in core functionality. Your domain may require extra / changed functionality. This would be equivalent to a change in the Chunk interface.
  • changes in how the core objects are arranged to provide a high level representation within the system (maybe you decide to represent a network of Chunks using a tree structure). Equivalent to a change in (or reimplementation of) the ChunkList class.
  • changes in how persistence occurs. This could be a change in annotations which persistence libs (e.g. Jackson, JPA, JAXB, etc) use or it could be a whole new class structure representing a 'Data Transfer Object'.

As a rule of thumb, if you find yourself describing a class using more than a single sentence or using a conjunction ('and', 'but', also punctuation like commas) then you're probably seeing a violation of the Single Responsibility Principle. A further rule of thumb is to take each one of those sentences / conjunctions and try to isolate them into a single class. Then see if that class violates the SRP. Rinse & repeat until nothing violates the SRP.

Sounds simple but, like chess, it takes minutes to learn but a lifetime to master.