Как соблюсти принцип единой ответственности при реализации функциональности чанков, как в Minecraft?

Я пытаюсь создать функциональность для чанка (например, чанка, который мы видим в Minecraft, который состоит из блоков 16x16x256).

Как мне это сделать, соблюдая принцип единой ответственности?

Функциональность включает в себя такие вещи, как загрузка, сохранение, создание сетки и отслеживание всех блоков.

Нужно ли мне

  1. Создайте класс Chunk, состоящий из множества классов, каждый из которых имеет одну из этих функций, например загрузку или сохранение. Затем инициируйте занятия в классе Chunk. Что мне делать, когда этим классам нужна функциональность класса Chunk, не нарушит ли это SRP?

  2. Создайте всю функциональность в классе Chunk


person filipot    schedule 03.01.2017    source источник
comment
Полезный ответ дать невозможно, вопрос слишком абстрактный. Ваше описание звучит так, как будто ваш существующий класс нарушает SRP, поэтому его необходимо разложить на отдельные обязанности. Делать поля общедоступными звучит как BadThing(tm), так что не делайте этого. Как этого избежать зависит от контекста. У тебя есть код?   -  person sisyphus    schedule 04.01.2017
comment
Нет кода для показа. Я просто пытаюсь понять SRP. Я думаю, что мой вопрос сводится к следующему: как правильно разложить класс? Как им управлять? Мне нужен общий совет. Но, как вы сказали, это зависит от контекста. Так что я не знаю.   -  person filipot    schedule 04.01.2017


Ответы (1)


Итак, если мы говорим об общих указателях, то для начала есть несколько простых вещей.

  • ваш класс Chunk должен заниматься только своей основной обязанностью — управлять всем, что содержится в классе Chunk.
  • похоже, вам нужен ChunkGroup, потенциально используя шаблон адаптера на фрагменте, который объединяет «сеть» из Chunk.
  • должны существовать некоторые средства сохранения объектов Chunk. Это не входит в обязанности самого Chunk, но может включать внутренний класс, который позволяет преобразовывать Chunk во что-то, что можно (де)сериализовать.

Итак, я ожидал увидеть что-то вроде

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;
     }
 }

Что следует отметить в отношении SRP здесь:

  • Интерфейс Chunk содержит только методы, относящиеся к основной функциональности, которую должен выполнять Chunk.
  • адаптер извлекает функциональность для создания «сети» объектов Chunk в отдельный класс. Существуют разные способы управления такими сетями, вы можете изменить их позже. Вы можете сделать это, не изменяя принцип работы самого Chunk.
  • ChunkNetwork может иметь разные методы в зависимости от вашего использования, и это может сообщить вам, как вы решите реализовать интерфейс, но ключевым моментом является то, что управление сетью находится за пределами самого Chunk.
  • то, как объекты Chunk представлены в XML или JSON (или что-то еще, на диске, в файле), является отдельной проблемой от того, что такое API Chunk. Однако с такими библиотеками, как Jackson, мы часто можем обойтись без определения отдельного класса для маршалинга/демаршаллинга. Однако, если ваши реализации Chunk станут сложными, вы можете захотеть это сделать.

В конечном счете, SRP говорит, что у любого отдельного класса должна быть одна причина для изменения. Поэтому вам нужно посмотреть на свою функциональность и понять, почему она может измениться. Типичные причины:

  • изменения в основной функциональности. Для вашего домена могут потребоваться дополнительные/измененные функции. Это было бы эквивалентно изменению интерфейса Chunk.
  • изменения в том, как устроены основные объекты, чтобы обеспечить представление высокого уровня в системе (возможно, вы решите представить сеть из Chunk с использованием древовидной структуры). Эквивалентно изменению (или повторной реализации) класса ChunkList.
  • изменения в том, как происходит настойчивость. Это может быть изменение аннотаций, которые используют библиотеки сохранения (например, Jackson, JPA, JAXB и т. д.), или это может быть совершенно новая структура класса, представляющая «Объект передачи данных».

Как правило, если вы обнаружите, что описываете класс, используя более одного предложения или используя союз («и», «но», а также знаки препинания, такие как запятые), то вы, вероятно, видите нарушение принципа единой ответственности. . Еще одно эмпирическое правило состоит в том, чтобы взять каждое из этих предложений/союзов и попытаться изолировать их в один класс. Затем посмотрите, не нарушает ли этот класс SRP. Промойте и повторяйте, пока ничто не нарушит SRP.

Звучит просто, но, как и в шахматах, на изучение уходят минуты, а на освоение уходит целая жизнь.

person sisyphus    schedule 05.01.2017