Перекрестное наследование в C # - PullRequest
1 голос
/ 04 ноября 2011

Я ищу лучший способ моделирования сценария в C #, который я бы назвал "перекрестным наследованием".

Во-первых, у меня есть ориентированный граф, узлы которого могут быть двух разных типов: Разъем и Компонент .Я создаю интерфейс IObjectBase , который содержит свойства, которые есть и у Connector, и у Component, например список IObjectBase-Children.Затем я создаю интерфейсы для Соединителей и Компонентов, которые унаследованы от IObjectBase:

public interface IObjectConnector : IObjectBase ...
public interface IObjectComponent : IObjectBase ...

Реализация выглядит аналогично, я создаю абстрактный класс для BaseObject и наследую его для классов Соединителя и Компонента:

public abstract class ObjectBase : IObjectBase ...
public class ObjectConnector : ObjectBase, IObjectConnector ...
public class ObjectComponent : ObjectBase, IObjectComponent ...

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

Итак, мне нужноиметь 4 разных объекта: ComponentConfig , ComponentEdit , ConnectorConfig , ConnectorEdit

Я начал с размышлений о дальнейшем наследованииIObjectConnector и IObjectComponent:

public interface IObjectConnectorEditor : IObjectConnector
public interface IObjectConnectorConfigurator : IObjectConnector
...

и создание объектов при наследовании от существующих компонентов и соединителей:

public class ObjectConnectorEditor : ObjectConnector, IEditor
...
public class ObjectComponentEditor: ObjectComponent, IEditor

Проблема теперь заключается в следующем: мне нужно реализовать интерфейс IEditor в обоихКлассы редактора, которые были бы очень избыточными.Но я не могу создать реализацию IEditor, от которой объекты будут наследовать, потому что я могу наследовать только от одного класса в C #, поэтому следующее не будет работать:

public abstract class Editor : IEditor ...
public class ObjectConnectorEditor: ObjectConnector, Editor

Что бы вы предложили как лучшийархитектурное решение этой проблемы?

Заранее спасибо,
Фрэнк

Ответы [ 3 ]

3 голосов
/ 04 ноября 2011

Помните об одиночной ответственности.Вы хотите, чтобы ваши объекты были всеми информационными контейнерами, соединителями и редакторами?

Я бы предложил другой дизайн, где эти обязанности разделены, с иерархией классов, которая представляет Объекты, Соединители, Компоненты и Редакторы.

1 голос
/ 04 ноября 2011

Прежде всего, улучшите ваше наименование: в этом контексте Object не имеет смысла, назовите его Node. Вы используете свои узлы в двух разных контекстах, поэтому разумно иметь интерфейс IEditorNode и интерфейс IConfiguratorNode. Если они используют несколько методов, разумно иметь суперинтерфейс, поэтому они объявляются только один раз. Пока что имеем:

interface INode {
}

interface IEditorNode : INode {
  int f(); // Needed later
}

interface IConfiguratorNode : INode {
}

Эти интерфейсы служат двойной цели: они позволяют взаимозаменяемо использовать Component s и Connector s и ограничивают доступные методы для Editor и Configurator тем, что им действительно нужно видеть , Обратите внимание, что нет необходимости использовать интерфейсы, если ни одна из этих причин не имеет значения.

У вас есть два типа узлов: Connector и Component. Если у них нет конкретных методов, которые используются в редакторе или в конфигураторе, но не в обоих, просто определите их как классы и заставьте их реализовывать как IEditorNode, так и IConfiguratorNode. В противном случае может иметь смысл различать их через интерфейсы:

interface IEditorComponent : IEditorNode {
}

interface IConfiguratorComponent : IConfiguratorNode {
}

Заставьте класс Component реализовать оба этих интерфейса:

class Component : IEditorComponent, IConfiguratorComponent {
}

Класс Connector аналогичен.

К сожалению, C # не поддерживает множественное наследование реализации, поэтому ваша единственная альтернатива факторизации вместе общих функций Editor или Configurator состоит в том, чтобы поместить их в отдельные классы, присвоив Component и Connector закрытый атрибут каждый из этих классов и делегировать реализацию:

class EditorHelper {
  public int f() {
    return 42;
  }
}

class Connector : IEditorConnector, IConfiguratorConnector {
  EditorHelper eh;

  public Connector() {
    eh = new EditorHelper();
  }

  public int f() {
    return eh.f();
  }
}
1 голос
/ 04 ноября 2011

Я согласен с ответом Polity, но если вы действительно хотите подделать множественное наследование в C #:

Создайте Component, Connector, Edit и Config как отдельные классы.Также создайте IComponent, IConnector, IEdit и IConfig как отдельные, соответствующие интерфейсы.

Теперь вы можете создать ComponentConfig, реализующий IComponent и IConfig, и просто делегировать методы интерфейса частным экземплярам Component и Config.

class ComponentConfig : IComponent, IConfig 
{
    private IComponent component;
    private IConfig config;

    public ComponentConfig() {
        component = new Component();
        config = new Config();
    }

    public ComponentMethod() { 
        return component.Componentmethod(); 
    }

    public ConfigMethod() {
        return config.ConfigMethod();
    }

}

См. http://en.wikipedia.org/wiki/Delegation_pattern

...