ООП - Правильное проектирование объекта с использованием методов изменения реализации и полутвердого состояния. - PullRequest
0 голосов
/ 13 февраля 2019

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

Примите во внимание следующее:

  • Сетка, состоящая из нескольких гексов (плиток).

  • Каждая плитка имеет свой собственный «тип» (Вода / Огонь и так далее)

  • Каждая плитка имеет несколько полей (например, ItemsOnTop), некоторые из которых являются общими для всех типов, в то время как некоторые зависят от типа.
  • Плитки могут изменять свой тип, что меняет реализациюбольшинство методов и сохраняет некоторые (но не все) поля нетронутыми.

Самым простым и наименьшим решением OOPish было бы реализовать один класс Tile, который бы сохранял вид плитки в поле, возможно, используяперечисления, такого как

TileType type = TileType.WATER

, а затем залить тела методов с помощью операторов Switch, проверяющих TileType и соответствующим образом изменяющих поведение.

Другое решение,возможно, более чистый, будет объявлять основной абстрактный класс плиток и извлекать из него все другие типы плиток - такие как WaterTile, Firetile и так далее.Мастер-класс содержит пустые виртуальные методы для всех поведений плиток, таких как ExtinguishTile (), который не переопределяется в WaterTile (не может быть погашен, поэтому вызывается пустой класс ExtinguishTile (), и ничего не происходит), и переопределяется соответствующим поведением в FireTile.class.

Проблема с этим, на первый взгляд лучшим дизайном, состоит в том, что тип тайла может изменить время выполнения.У нас здесь есть объект, для которого мы хотим сохранить состояние (или, по крайней мере, некоторые поля), а также необходимость различной реализации тонны методов для каждого типа плитки.

Что быбыть правильным решением для этого?

Один «обходной путь» будет заключаться в реализации второго дизайна.Если необходимо изменить объект плитки, уничтожьте старый объект плитки, создайте новый объект плитки нужного типа и скопируйте все поля, которые должны быть перенесены.Хотя это звучит немного странно, и, возможно, кто-то более опытный мог бы что-то добавить.

Пожалуйста, укажите мне правильное направление.

Ответы [ 3 ]

0 голосов
/ 13 февраля 2019

Возможно, вам не хватает абстракции, которая упростила бы ваш дизайн.Я бы подумал о том, должен ли Tile иметь все то, что вы описываете, или это можно разумно разделить.

Я думаю, что если мы разделим вашу концепцию Tile на Position иTile.Position - это то, что совсем не меняется, это абстракция определенного места.Это чем имеет a Tile, имеет вещи на нем и т. Д. В основном все вещи, которые ранее были «общими» для плиток.

A Tileэто просто поведение, которое эта позиция на самом деле имеет, так что она может легко перейти к некоторому другому Tile, не копируя весь материал, который фактически связан с Position.

Я не знаком с вашим использованием-кейс, просто идея.

0 голосов
/ 13 февраля 2019

Мне нравится Strategy шаблон дизайна, это потрясающе, это один из этих шаблонов, где вы можете сказать «Круто! Я буду использовать это для всего!».

Однако я не уверен, действительно ли этолучшее решение здесь.

Если и когда вам нужно изменить тип объекта - это убедительный признак того, что пришло время создать новый объект,в противном случае это может нарушить некоторые принципы OOP и SOLID.

Если бы я создал объект Tiger , а теперь мне нужен Sheep - я бы создал новый объект для этоговместо создания универсального объекта Animal со свойством enum AnimalType.Однако свойство TileType должно присутствовать, поскольку оно предоставляет некоторые метаданные об объекте без необходимости использования GetType() Reflection для проверки, с каким типом мы работаем.

Проблема отображения может быть решена с помощью простого отображения в Ctor,Пользовательский маппер, Automapper, или фабричный.Все эти подходы имеют свои плюсы и минусы.

Давайте посмотрим, как мы можем использовать простое отображение в ctor:

public abstract class AbstractHex
{
    string MyProperty1 { get; set; }
    ...
    string MyProperty10 { get; set; }
    ...
    TileType TileType { get; }

    protected Hex(AbstractHex hex = null)
    {
       if (hex != null)
       {
          this.MyProperty1 = hex.MyProperty1;
          // etc 
       }
    }

    ...
    Hex specific abstract or virtual methods
}

public sealed class WaterHex : AbstractHex
{
    // implements props here

    public WaterHex(AbstractHex copyFrom = null) : base(copyFrom)
    {
       TileType = TileTypes.Water;
    }

    ...
    override virtual methods with behavior specific for WaterHex
}

public sealed class FireHex : AbstractHex
{
    // implements props here

    public WaterHex(AbstractHex copyFrom = null) : base(copyFrom)
    {
       TileType = TileTypes.Fire;
    }

    ...
    override virtual methods with behavior specific for FireHex
}

Теперь вы можете сделать что-то вроде:

var fireHex = new FireHex();

// this will copy data and change behavior 
var waterHex = new WaterHex(fireHex);
0 голосов
/ 13 февраля 2019

Взгляните на шаблон проектирования стратегии .Используя этот шаблон, вы можете сохранить экземпляр Tile (контекст), изменяя его поведение во время выполнения, назначая другую реализацию стратегии (Fire, Water, ...).

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