Есть ли лучший способ вызвать переопределенный метод производного класса из его абстрактного базового класса? - PullRequest
6 голосов
/ 14 сентября 2011

У меня есть абстрактный базовый класс, из которого происходит множество классов.Я хочу, чтобы производные классы могли переопределять виртуальный метод, определенный в базовом классе, но в базовом классе есть сложная логика, которая определяет, «включен» ли переопределенный метод в любой конкретный момент.код - одно из возможных решений - например:

public abstract class AbstractBaseClass
{
  public bool IsMethodEnabled { get; set; }

  public virtual void DerivedMethod() { }

  public void Method()
  {
    if (IsMethodEnabled)
      DerivedMethod();
  }
}

public class DerivedClass : AbstractBaseClass
{
  public override void DerivedMethod()
  {
    Console.WriteLine("DerivedMethod() was called.");
  }
}

В приведенном выше примере IsMethodEnabled является сокращением для более сложной логики, которая определяет, следует ли вызывать DerivedMethod - это код, который я хочуинкапсулирован в базовый класс, так что мне не нужно воспроизводить его в каждом производном классе.

Дизайн работает так, как задумано.Если я запускаю этот пример кода:

AbstractBaseClass a1 = new DerivedClass() { IsMethodEnabled = false };
AbstractBaseClass a2 = new DerivedClass() { IsMethodEnabled = true };

a1.Method();
a2.Method();

... Я вижу ровно один вызов DerivedMethod, как и ожидалось.

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

Другими словами, пахнет ли код выше?

Ответы [ 6 ]

4 голосов
/ 14 сентября 2011

Я согласен с Ридом, что это разумная реализация.

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

4 голосов
/ 14 сентября 2011

Это вполне разумная реализация.

Основные изменения, которые я бы предложил:

  • Сделать виртуальный метод, который реализует функциональность protected вместо public
  • Используйте для этого более подходящие названия. Возможно, что-то более похожее на public void Method() и protected virtual void OnMethod()
1 голос
/ 14 сентября 2011

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

public interface IDoSomething {
  void Method();
}

public class ConditionallyDoSomething : IDoSomething {
  private IDoSomething _wrapped;

  public ConditionallyDoSomething(IDoSomething wrapped) {
    _wrapped = wrapped;
  }

  public bool IsMethodEnabled { get; set; } // could be quite complex...

  public void Method() {
    if (IsMethodEnabled) {
      _wrapped.Method();
    }
  }
}

public class DoSomething : IDoSomething {
  public void Method() {
    // do something...
  }
}

Таким образом, вы можете макетировать IDoSomething s и тестировать каждую часть (принятие решений и функциональность) отдельно. Но это оправдано только в том случае, если у вас действительно есть сложная логика в обоих видах поведения, которая выиграет от такого разделения. Я просто пытаюсь дать альтернативу другим отличным ответам здесь. В конечном счете, это зависит от вашего конкретного сценария.

1 голос
/ 14 сентября 2011

Он не «пахнет» больше, чем другие Методы шаблона , которые не нравятся некоторым людям.Я склонен согласиться с некоторыми замечаниями, сделанными здесь .Особенно эти два:

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

  • Сложно поддерживать - поддерживая пару фрагментовКод, который широко использовал метод шаблона, может быть сложным.Такая система может быстро стать хрупкой.Изменения на любом уровне могут помешать работе выше или ниже этого уровня в методах шаблона.При добавлении новой функциональности часто возникает ощущение непредсказуемости, поскольку сложно предсказать, как поведение изменится во всех случаях.Вы также часто склонны создавать более тонкие настройки, разделяя алгоритмические части класса шаблона и вставляя больше слоев, что усугубляет проблему.

В общем, я думаю, что у вас естьбыть очень осторожным с Template Method и держать вещи простыми и сфокусированными.

0 голосов
/ 14 сентября 2011

Это зависит от того, насколько дорогим IsMethodEnabled (на самом деле), если предположить, что он не сгенерирован компилятором, как показано, и будет ли IsMethodEnabled часто меняться, и существуют ли сотни методов с таким небольшим битом «включен»логики и того, является ли Method () действительно критичным к производительности путем.

0 голосов
/ 14 сентября 2011

Вы можете просто пометить все методы, которые должны быть переопределены, как абстрактные, а те методы, которые могут быть переопределены как виртуальные.Смотрите C # Абстрактные классы

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