Если мы предположим, что нежелательно, чтобы базовый класс был чистым интерфейсным классом, и с помощью 2 примеров, приведенных ниже, что является лучшим подходом, используя определение класса абстрактного или виртуального метода?
Преимущество «абстрактной» версии состоит в том, что она, вероятно, выглядит чище и заставляет производный класс давать многообещающую осмысленную реализацию.
Преимущество «виртуальной» версии заключается в том, что она может быть легко загружена другими модулями и использована для тестирования, не добавляя связки базовых фреймворков, как того требует абстрактная версия.
Абстрактная версия:
public abstract class AbstractVersion
{
public abstract ReturnType Method1();
public abstract ReturnType Method2();
.
.
public abstract ReturnType MethodN();
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
Виртуальная версия:
public class VirtualVersion
{
public virtual ReturnType Method1()
{
return ReturnType.NotImplemented;
}
public virtual ReturnType Method2()
{
return ReturnType.NotImplemented;
}
.
.
public virtual ReturnType MethodN()
{
return ReturnType.NotImplemented;
}
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
return ReturnType.NotImplemented
? Шутки в сторону? Если вы не можете отклонить не реализованный тип во время компиляции (вы можете использовать абстрактные методы), по крайней мере, выведите исключение.Ответы:
Мой голос, если бы я потреблял ваши вещи, был бы за абстрактные методы. Это идет вместе с «провалиться рано». Во время объявления может быть трудным добавить все методы (хотя любой приличный инструмент рефакторинга сделает это быстро), но, по крайней мере, я сразу же знаю, в чем проблема, и исправлю ее. Я бы предпочел это, чем отлаживать 6 месяцев и изменения 12 человек позже, чтобы понять, почему мы внезапно получаем не реализованное исключение.
источник
Виртуальная версия подвержена ошибкам и семантически неверна.
Аннотация говорит: «Этот метод здесь не реализован. Вы должны реализовать его, чтобы этот класс работал»
Виртуальный говорит: «У меня есть реализация по умолчанию, но вы можете изменить меня, если вам нужно»
Если вашей конечной целью является тестируемость, то интерфейсы, как правило, являются лучшим вариантом. (этот класс делает х, а не этот класс топором). Возможно, вам придется разбить ваши классы на более мелкие компоненты, хотя для того, чтобы это работало хорошо.
источник
Это зависит от использования вашего класса.
Если методы имеют разумную «пустую» реализацию, у вас есть много методов, и вы часто переопределяете только некоторые из них, тогда использование
virtual
методов имеет смысл. НапримерExpressionVisitor
, реализовано таким образом.В противном случае, я думаю, вы должны использовать
abstract
методы.В идеале у вас не должно быть методов, которые не реализованы, но в некоторых случаях это лучший подход. Но если вы решите сделать это, такие методы должны выдавать
NotImplementedException
, а не возвращать какое-то специальное значение.источник
Я бы посоветовал вам пересмотреть определение отдельного интерфейса, который реализует ваш базовый класс, а затем следовать абстрактному подходу.
Код изображения, подобный этому:
Это решает эти проблемы:
Наличие всего кода, который использует объекты, полученные из AbstractVersion, теперь может быть реализовано для получения интерфейса IVersion. Это означает, что они могут быть легче протестированы модульно.
Выпуск 2 вашего продукта может затем реализовать интерфейс IVersion2, чтобы обеспечить дополнительные функциональные возможности, не нарушая код существующих клиентов.
например.
Также стоит прочитать об инверсии зависимостей, чтобы этот класс не содержал жестко закодированных зависимостей, которые мешают эффективному модульному тестированию.
источник
Внедрение зависимостей зависит от интерфейсов. Вот короткий пример. В классе Student есть функция CreateStudent, для которой требуется параметр, реализующий интерфейс «IReporting» (с методом ReportAction). После создания ученика он вызывает ReportAction для конкретного параметра класса. Если система настроена на отправку электронного письма после создания учащегося, мы отправляем его в конкретный класс, который отправляет электронное письмо в своей реализации ReportAction, или мы можем отправить другой конкретный класс, который отправляет выходные данные на принтер в его реализации ReportAction. Отлично подходит для повторного использования кода.
источник