C# имеет абстрактные классы и интерфейсы, он должен также иметь “mixins”?

Время от времени я сталкиваюсь со случаем, где я хочу, чтобы набор классов все обладал подобной логикой. Например, возможно, я хочу обоих a Bird и Airplane смочь к Fly(). Если бы Вы думаете "стратегическая модель", я согласился бы, но даже со стратегией, иногда невозможно постараться не копировать код.

Например, скажем, следующее применяется (и это очень похоже на реальную ситуацию, с которой я недавно встретился):

  1. Оба Bird и Airplane должен содержать экземпляр объекта, который реализует IFlyBehavior.
  2. Оба Bird и Airplane потребность спросить IFlyBehavior экземпляр к Fly() когда OnReadyToFly() назван.
  3. Оба Bird и Airplane потребность спросить IFlyBehavior экземпляр к Land() когда OnReadyToLand() назван.
  4. OnReadyToFly() и OnReadyToLand() являются частными.
  5. Bird наследовался Animal и Airplane наследовался PeopleMover.

Теперь, скажем, мы позже добавляем Moth, HotAirBalloon, и 16 других объектов, и скажем, они все следуют за тем же шаблоном.

Мы теперь испытываем необходимость в 20 копиях следующего кода:

private IFlyBehavior _flyBehavior;

private void OnReadyToFly()
{
    _flyBehavior.Fly();
}

private void OnReadyToLand()
{
    _flyBehavior.Land();
}

Две вещи мне не нравится приблизительно это:

  1. Это не очень DRY (те же девять строк кода повторяются много раз). Если мы обнаружили ошибку или добавили a BankRight() кому: IFlyBehavior, нам были бы нужны к propogate изменения во всех 20 классах.

  2. Нет никакого способа осуществить те все 20, классы последовательно реализуют эту повторяющуюся внутреннюю логику. Мы не можем использовать интерфейс, потому что интерфейсы только разрешают общедоступным участникам. Мы не можем использовать абстрактный базовый класс, потому что объекты уже наследовали базовые классы, и C# не позволяет множественное наследование (и даже если бы классы уже не наследовали классы, то мы могли бы позже хотеть добавить новое поведение, которое реализует, скажем, ICrashable, таким образом, абстрактный базовый класс не всегда будет эффективным решением).

Что, если...?

Что, если C# имел новую конструкцию, сказать pattern или template или [заполняют Вашу идею здесь], который работал как интерфейс, но позволил Вам помещать модификаторы частного или защищенного доступа на участников? Необходимо было бы все еще обеспечить реализацию для каждого класса, но если класс, реализованный PFlyable шаблон, у Вас, по крайней мере, был бы способ осуществить тот каждый класс, имел необходимый шаблонный код для вызова Fly() и Land(). И с современным IDE как Visual Studio Вы смогли бы автоматически сгенерировать код с помощью команды "Implement Pattern".

Лично, я думаю, что имело бы больше смысла просто разворачивать значение интерфейса покрыть любой контракт, не хотел ли внутренний (частный/защищающий) или внешний (общественность), но я предложил добавить совершенно новую конструкцию сначала, потому что люди, кажется, очень непреклонны по отношению к значению слова "интерфейс", и я, чтобы семантика стала фокусом ответов людей.

Вопросы:

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

Обновление

Из комментария AakashM я полагаю, что уже существует название функции, которую я запрашиваю: Mixin. Так, я предполагаю, что мой вопрос может быть сокращен к: "C# должен разрешить Mixins?"

31
задан devuxer 23 February 2010 в 19:44
поделиться

7 ответов

Описанную вами проблему можно решить с помощью шаблона "Посетитель" (все можно решить с помощью шаблона "Посетитель", будьте осторожны!)

Шаблон "Посетитель" позволяет вы перемещаете логику реализации к новому классу. Таким образом, вам не нужен базовый класс, а посетитель отлично работает с разными деревьями наследования.

Подводя итог:

  1. Нет необходимости добавлять новые функции ко всем различным типам
  2. Вызов посетителя может быть перенаправлен до корня каждой иерархии классов

Для справки см. Шаблон посетителей

3
ответ дан 27 November 2019 в 22:52
поделиться

Мы не можем использовать методы расширения для этого

    public static void OnReadyToFly(this IFlyBehavior flyBehavior)
    {
          _flyBehavior.Fly()
    }

Это имитирует желаемую функциональность (или миксины)

2
ответ дан 27 November 2019 в 22:52
поделиться

Visual Studio уже предлагает это в «бедной форме» с фрагментами кода. Кроме того, с инструментами рефакторинга в стиле ReSharper (и, возможно, даже с собственной поддержкой рефакторинга в Visual Studio) вы значительно продвинетесь в обеспечении согласованности.

[РЕДАКТИРОВАТЬ: Я не думал о методах расширения, этот подход продвигает вас еще дальше (вам нужно только сохранить _flyBehaviour как частную переменную). Это делает остальную часть моего ответа, вероятно, устаревшей ...]

Однако; просто ради обсуждения: как это можно улучшить? Вот мое предложение.

Можно представить себе что-то вроде следующего, которое будет поддерживаться будущей версией компилятора C #:

// keyword 'pattern' marks the code as eligible for inclusion in other classes
pattern WithFlyBehaviour
{
   private IFlyBehavior_flyBehavior;

   private void OnReadyToFly()
   {
      _flyBehavior.Fly();
   }

   [patternmethod]
   private void OnReadyToLand()
   {
      _flyBehavior.Land();
   }     
}

Что вы могли бы использовать тогда, например:

 // probably the attribute syntax can not be reused here, but you get the point
 [UsePattern(FlyBehaviour)] 
 class FlyingAnimal
 {
   public void SetReadyToFly(bool ready)
   {
      _readyToFly = ready;
      if (ready) OnReadyToFly(); // OnReadyToFly() callable, although not explicitly present in FlyingAnimal
   }

 }

Будет ли это улучшением? Наверное. Это действительно того стоит? Может быть ...

2
ответ дан 27 November 2019 в 22:52
поделиться

Вы только что описали аспектно-ориентированное программирование.

Одной из популярных реализаций AOP для C# является PostSharp (основной сайт, похоже, не работает, однако, это прямая страница "About").


В продолжение комментария: Я не уверен, что PostSharp поддерживает это, но я думаю, что вы говорите о этой части AOP:

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

1
ответ дан 27 November 2019 в 22:52
поделиться

Можете ли вы добиться такого поведения, используя новый ExpandoObject в .NET 4.0?

0
ответ дан 27 November 2019 в 22:52
поделиться

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

  1. Пусть объекты Bird и Plane реализуют свойство для объекта IFlyBehavior для интерфейса IFlyer

     открытый интерфейс IFlyer 
     {
    public IFlyBehavior FlyBehavior 
    } 
     
    public Bird: IFlyer 
     {
    public IFlyBehaviour FlyBehavior {get; set;} { {1}}} 
     
    public Airplane: IFlyer 
     {
    public IFlyBehaviour FlyBehavior {get; set;} 
    } 
     
  2. Создайте класс расширения для IFlyer

     public IFlyerExtensions 
     {
     
    public void OnReadyToFly (этот флаер IFlyer) 
     {{{1 }} flyer.FlyBehavior.Fly (); 
    } 
     
    public void OnReadyToLand (этот флаер IFlyer) 
     {
    flyer.FlyBehavior.Land (); 
    } 
    } 
     
0
ответ дан 27 November 2019 в 22:52
поделиться

Черты Scala были разработаны для решения такого рода сценариев. Также есть некоторые исследования, чтобы включить черт в C # .

ОБНОВЛЕНИЕ: Я создал свой собственный эксперимент, чтобы иметь ролей в C # . Взглянем.

0
ответ дан 27 November 2019 в 22:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: