Объединение двух интерфейсов в один - PullRequest
0 голосов
/ 16 октября 2008

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

Я делаю это, разбивая свой проект (игру) на несколько сборок:

GameName.Engine
GameName.Rules
GameName.Content
GameName.Gui

Сборка GameName.Engine содержит несколько интерфейсов, поэтому другие части программы не должны зависеть от какой-либо конкретной реализации. Например, у меня есть интерфейс GameName.Engine.ICarryable, который в основном реализован классом GameName.Content.Item (и его подклассами). У меня также есть объект, позволяющий Actor подобрать ICarryable: PickupAction. Previously, для этого требовался предмет, но это раскрывает ненужные методы и свойства, где на самом деле нужны были только методы, необходимые для его поднятия и переноса. Вот почему я создал интерфейс ICarryable.

Теперь все хорошо, так что на мой вопрос. GameName.Gui должен зависеть только от GameName.Engine, а не от какой-либо реализации. Внутри GameName.Gui у меня есть MapView объект, который отображает Map и любые IRenderable объекты на нем.

IRenderable - это просто интерфейс, который предоставляет изображение и некоторые строки, описывающие объект. Но для MapView также необходим объект для реализации ILocateable, поэтому он может видеть свое местоположение и знать, когда он изменяется с помощью события LocationChanged, внутри ILocateable.

Эти два интерфейса реализуются как объектами Item, так и Actor. Которые, опять же, определены в GameName.Content. Поскольку нужны оба интерфейса, у меня есть два варианта:

  1. Заставьте GameName.Gui зависеть от GameName.Content и потребовать Entity (базовый класс Item и Actor).

  2. Создайте интерфейс внутри GameName.Engine, который выглядит следующим образом:

    interface ILocateableRenderable : ILocateable, IRenderable
    {
    }
    

    А затем заставьте мои объекты Actor и Item реализовать этот интерфейс вместо двух по отдельности.

У кого-нибудь есть предложения, какой метод лучше? Уместно ли создавать интерфейс без методов или свойств, который обеспечивает реализацию только двух других интерфейсов?

Пояснение: MapView работает на Map, который состоит из Entity объектов. Я не хочу открывать Entity объекты для MapView, нужно только знать их местоположение (ILocateable) и как их визуализировать (IRenderable).

Ответы [ 4 ]

2 голосов
/ 16 октября 2008

Во всех случаях объекты IRenderable также должны были бы реализовывать ILocateable, поэтому я делаю то, что предложил @Sklivvz, и делаю наследование IRenderable (это то, что он вызывается при обсуждении интерфейсов?) Из ILocateable:

public interface IRenderable : ILocateable
{
  // IRenderable interface
}

Этот шаблон интенсивно используется в .NET. Например, интерфейс ICollection реализует IEnumerable, добавляя дополнительные методы. Это прямая аналогия тому, что сделал бы приведенный выше код.

Спасибо @ Sklivvz.

2 голосов
/ 16 октября 2008

У вас, кажется, два противоречивых требования:

Внутри GameName.Gui I иметь объект MapView, который отображает a Карта и любые IRenderable объекты на нем.

и

Но для MapView также необходим объект реализовать ILocateable , чтобы он мог увидеть его местоположение и знать, когда его изменено с помощью события LocationChanged, внутри ILocateable.

Итак, если MapView нужен только IRenderable, он должен принять IRenderable и затем проверить, реализует ли класс также ILocateable. В этом случае используйте его методы.

public void Whatever(IRenderable renderable)
{
   if (renderable is ILocateable)
   {
      ((ILocateable) renderable).LocationChanged += myEventHandler;
   }

   // Do normal stuff
}

С другой стороны, если вам всегда нужно, чтобы он был ILocateable и IRenderable, то вам действительно нужно создать производный интерфейс одним из двух способов. Или

interface IMappable: IRenderable, ILocateable {}

public void Whatever(IMappable mappable)
{
   mappable.LocationChanged += myEventHandler;

   // Do normal stuff
}    

или

interface IRenderable: ILocateable
{
  // IRenderable interface
}


public void Whatever(IRenderable renderable)
{
   renderable.LocationChanged += myEventHandler;

   // Do normal stuff
}  

в зависимости от того, каков ваш код на данный момент.

0 голосов
/ 16 октября 2008

Опция 2 нарушает принцип разделения интерфейсов . По сути, клиент не должен видеть методы, которые ему не нужны ... никаких толстых интерфейсов. Если объект не может просто реализовать какой-либо из этих интерфейсов, вам следует объединить их в один интерфейс. Однако, если имеет смысл, чтобы объект был просто ILocatable без необходимости быть IRenderable, сохраняйте интерфейсы отличными. Я не вижу проблем с этим.

Вы можете создать базовый класс по умолчанию (реализующий оба), который будет производным, если вы обнаружите, что это общий сценарий / 80%

0 голосов
/ 16 октября 2008

То, что вы могли бы рассмотреть, но я думаю, я не эксперт в этой области, это разделить движок на два: рендер и менеджер местоположения. Затем вы можете добавить ILocateable объекты ко второму и IRenderable объекты к первому. Однако, если вы думаете, что это не имеет никакого смысла и / или слишком сложно, создание этого объединенного интерфейса не кажется слишком плохим. Другим вариантом может быть добавление суперинтерфейса к ILocateable и IRenderable, например IGameObject, который вы бы приняли в своем классе движка, а затем проверка точного типа для отправки объектов на карту, объекта местоположения или обоих.

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