Я создаю настольную игру (например, шахматы) на Java, где каждая фигура имеет свой собственный тип (например Pawn
, Rook
и т. Д.). Для графической части приложения мне нужно изображение для каждой из этих частей. Поскольку делать думает, как
rook.image();
нарушает разделение пользовательского интерфейса и бизнес-логики, я создаю отдельного презентатора для каждого фрагмента, а затем сопоставляю типы фрагментов с соответствующими им презентаторами, например
private HashMap<Class<Piece>, PiecePresenter> presenters = ...
public Image getImage(Piece piece) {
return presenters.get(piece.getClass()).image();
}
Все идет нормально. Однако я чувствую, что осмотрительный гуру ООП будет недоволен вызовом getClass()
метода и предложил бы использовать посетителя, например, так:
class Rook extends Piece {
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitRook(this);
}
}
class ImageVisitor implements PieceVisitor<Image> {
@Override
public Image visitRook(Rook rook) {
return rookImage;
}
}
Мне нравится это решение (спасибо, гуру), но у него есть один существенный недостаток. Каждый раз, когда в приложение добавляется новый тип фрагмента, PieceVisitor должен обновляться новым методом. Я хотел бы использовать мою систему в качестве фреймворка для настольной игры, где новые части могут быть добавлены с помощью простого процесса, когда пользователь фреймворка будет только обеспечивать реализацию как фрагмента, так и его презентатора, и просто подключит его к фреймворку. Мой вопрос: есть ли чистое решение ООП без instanceof
и getClass()
т. Д., Которое позволило бы для такого типа расширяемости?
источник
Ответы:
Да, есть.
Позвольте мне спросить вас об этом. В ваших текущих примерах вы находите способы сопоставления типов элементов изображениям. Как это решает проблему перемещения части?
Более мощная техника, чем спрашивать о типе - это следовать Скажи, не спрашивай . Что делать, если каждый кусок взял
PiecePresenter
интерфейс, и это выглядело так:Конструкция будет выглядеть примерно так:
Использование будет выглядеть примерно так:
Идея здесь состоит в том, чтобы не брать на себя ответственность за то, что делают другие люди, не спрашивая об этом и не принимая решения на его основе. Вместо этого держите ссылку на что-то, что знает, что делать с чем-то, и скажите ему, чтобы сделать что-то с тем, что вы знаете.
Это учитывает полиморфизм. Вы не заботитесь о том, что вы говорите. Тебе все равно, что это говорит. Вы просто заботитесь о том, чтобы он мог делать то, что вам нужно.
Хорошая схема , которая держит их в отдельных слоях, следует сказать-не-спросить, и показывает , как не пара слоя к слою незаслуженно находится это :
Он добавляет слой прецедентов, который мы здесь не использовали (и, конечно, можем добавить), но мы следуем тому же шаблону, который вы видите в правом нижнем углу.
Вы также заметите, что Presenter не использует наследование. Он использует композицию. Наследование должно быть последним средством получения полиморфизма. Я предпочитаю проекты, которые предпочитают использовать композицию и делегирование. Это немного больше клавиатурного набора, но это намного больше мощности.
источник
Что об этом:
Ваша модель (классы фигур) имеет общие методы, которые могут вам понадобиться и в другом контексте:
Изображения, которые будут использоваться для отображения определенной фигуры, получают имена файлов по схеме именования:
Затем вы можете загрузить соответствующее изображение без доступа к информации о классах Java.
Я думаю, что вы не должны сосредоточиться на занятиях так много. Скорее думайте с точки зрения бизнес-объектов .
И универсальное решение - это отображение любого вида. ИМХО, хитрость заключается в том, чтобы переместить это отображение из кода в ресурс, который легче поддерживать.
Мой пример делает это отображение по соглашению, которое довольно легко реализовать и позволяет избежать добавления информации, связанной с представлением, в бизнес-модель . С другой стороны, вы можете считать это «скрытым» отображением, потому что оно нигде не выражено.
Другой вариант заключается в том, чтобы рассматривать это как отдельный бизнес-пример со своими собственными MVC-уровнями, включая постоянный уровень, который содержит сопоставление.
источник
Я бы создал отдельный класс UI / view для каждой части, которая содержит визуальную информацию. Каждый из этих классов кусочков имеет указатель на свою модель / бизнес-аналог, который содержит позицию и правила игры фигуры.
Взять хотя бы пешку:
Это позволяет полностью разделить логику и пользовательский интерфейс. Вы можете передать указатель логической части на игровой класс, который будет обрабатывать перемещение фигур. Единственным недостатком является то, что создание экземпляров должно происходить в классе пользовательского интерфейса.
источник
Piece* p
. Как я знаю, что я должен создатьPawnView
для отображения, а неRookView
илиKingView
? Или мне нужно сразу создавать сопутствующее представление или докладчика, когда я создаю новую пьесу? Это было бы в основном решением @ CandiedOrange с инвертированными зависимостями. В этом случаеPawnView
конструктор также может принимать, аPawn*
не простоPiece*
.Я хотел бы подойти к этому, сделав
Piece
generic, где его параметром является тип перечисления, который идентифицирует тип элемента, причем каждый элемент имеет ссылку на один такой тип. Тогда пользовательский интерфейс может использовать карту из перечисления, как и раньше:Это имеет два интересных преимущества:
Во-первых, применимо к большинству статически типизированных языков: если вы настраиваете свою доску в соответствии с типом фигуры, то вы не сможете вставить в нее неправильный тип фигуры.
Во-вторых, и, возможно, более интересно, если вы работаете в Java (или других языках JVM), вы должны заметить, что каждое значение перечисления не просто независимый объект, но оно также может иметь свой собственный класс. Это означает, что вы можете использовать ваши объекты типа фигуры в качестве объектов Стратегии для определения поведения фигуры:
(Очевидно, что реальные реализации должны быть более сложными, но, надеюсь, вы поняли идею)
источник
Я прагматичный программист, и мне действительно все равно, что такое чистая или грязная архитектура. Я считаю, требования и это должно быть обработано простым способом.
Ваше требование заключается в том, что логика вашего шахматного приложения будет представлена на разных уровнях представления (устройствах), таких как веб-приложение, мобильное приложение или даже консольное приложение, поэтому вам необходимо поддерживать эти требования. Вы можете предпочесть использовать очень разные цвета, штучные изображения на каждом устройстве.
Как вы видели, параметр презентатора должен передаваться на каждом устройстве (уровне представления) по-разному. Это означает, что ваш уровень представления будет решать, как представлять каждую часть. Что не так в этом решении?
источник
Есть еще одно решение, которое поможет вам полностью абстрагировать пользовательский интерфейс и доменную логику. Ваша доска должна быть открыта для вашего уровня пользовательского интерфейса, и ваш уровень пользовательского интерфейса может решить, как представлять фигуры и позиции.
Для этого вы можете использовать строку Fen . Строка фенов - это, в основном, информация о состоянии платы и она дает текущие фигуры и их позиции на доске. Таким образом, ваша доска может иметь метод, который возвращает текущее состояние платы через строку Fen, тогда ваш уровень пользовательского интерфейса может представлять доску по своему усмотрению. Вот как на самом деле работают современные шахматные движки. Шахматные движки - это консольные приложения без графического интерфейса, но мы используем их через внешний графический интерфейс. Шахматный движок связывается с графическим интерфейсом через строки и шахматную нотацию.
Вы спрашиваете, что, если я добавлю новый кусок? Нереально, что шахматы представят новую фигуру. Это было бы огромным изменением в вашем домене. Так что следуйте принципу ЯГНИ.
источник