использование абстрактного метода против регулярных методов
Я хотел бы знать разницу между двумя соглашениями:
- Создание абстрактного базового класса с помощью абстрактного метода который будет реализован позже на производных классах.
- Создание абстрактного базового класса без абстрактных методов
но добавление соответствующего метода позже на уровне производных классов.
В чем разница?
6 ответов:
Как и интерфейсы, абстрактные классы предназначены для выражения набора известных операций для ваших типов. Однако, в отличие от интерфейсов, абстрактные классы позволяют реализовать общие/общие функциональные возможности, которые могут использоваться любым производным типом. Например:
public abstract class LoggerBase { public abstract void Write(object item); protected virtual object FormatObject(object item) { return item; } }В этом действительно базовом примере выше я, по существу, сделал две вещи:
- определил контракт, которому будут соответствовать мои производные типы.
- предоставляет некоторые функции по умолчанию, которые могут быть переопределены, если требуемый.
Учитывая, что я знаю, что любой производный тип
LoggerBaseбудет иметь методWrite, я могу его вызвать. Эквивалентом вышесказанного в качестве интерфейса может быть:public interface ILogger { void Write(object item); }Как абстрактный класс, я могу предоставить дополнительную услугу
FormatObject, которую можно опционально переопределить, скажем, если я писалConsoleLogger, например:public class ConsoleLogger : LoggerBase { public override void Write(object item) { Console.WriteLine(FormatObject(item)); } }Помечая метод
FormatObjectкак виртуальный, это означает, что я могу предоставить общую реализацию. Я также могу переопределить его:public class ConsoleLogger : LoggerBase { public override void Write(object item) { Console.WriteLine(FormatObject(item)); } protected override object FormatObject(object item) { return item.ToString().ToUpper(); } }Итак, ключ части:
- Классы должны быть унаследованы.
- Методы
abstractдолжны быть реализованы в производных типах.virtualметоды могут быть переопределены в производных типах.Во втором случае, поскольку вы не добавляете функциональность к абстрактному базовому классу, вы не можете вызвать этот метод при работе с экземпляром базового класса напрямую. Например, если бы я реализовал
ConsoleLogger.WriteSomethingElse, я не мог бы вызвать его изLoggerBase.WriteSomethingElse.
Идея помещения абстрактных методов в базовый класс и последующей реализации их в подклассах состоит в том, что вы можете использовать родительский тип вместо любого конкретного подкласса. Например, вы хотите отсортировать массив. Вы можете определить базовый класс как что-то вроде
Затем вы можете реализовать различные алгоритмы, такие как quicksort, mergesort, heapsort в подклассах.abstract class Sorter { public abstract Array sort(Array arr); }class QuickSorter { public Array sort(Array arr) { ... } } class MergeSorter { public Array sort(Array arr) { ... } }Вы создаете объект сортировки, выбрав алгоритм,
Sorter sorter = QuickSorter();Теперь вы можете пройти
sorterвокруг, не выставляя напоказ тот факт, что под капотом у него плывун. Чтобы отсортировать массив, вы говоритеArray sortedArray = sorter.sort(someArray);Таким образом, детали реализации (какой алгоритм вы используете) отделяются от интерфейса к объекту (тот факт, что он сортирует массив).
Одним из конкретных преимуществ является то, что если в какой-то момент Вы хотите другой алгоритм сортировки, то вы можете изменитьQuickSort(), чтобы сказатьMergeSortв этой единственной строке, не меняя ее больше нигде. Если ты этого не сделаешь включите методsort()в родительский, вы должны опускаться доQuickSorterвсякий раз, когда вызываетеsort(), и тогда изменить алгоритм будет сложнее.
В случае 1) Вы можете получить доступ к этим методам из абстрактного базового типа, не зная точного типа (абстрактные методы являются виртуальными методами).
Смысл абстрактных классов обычно заключается в определении некоторого контракта на базовом классе, который затем реализуется дервиированными классами (и в этом контексте важно признать, что интерфейсы являются своего рода "чистыми абстрактными классами").
Хм, Ну, разница в том, что базовый класс будет знать о первом, а не о втором.
Другими словами, имея абстрактный метод в базовом классе, вы можете написать код в других методах базового класса, которые вызывают этот абстрактный метод.Очевидно, если базовый класс не имеет этих методов... ты не можешь им позвонить...
Абстрактная функция не может иметь никакой функциональности. Вы в основном говорите, что любой дочерний класс должен дать свою собственную версию этого метода, однако он слишком общий, чтобы даже пытаться реализовать в родительском классе. Виртуальная функция, по сути, говорит: Смотрите, вот функциональность, которая может быть или не быть достаточно хороша для дочернего класса. Поэтому, если он достаточно хорош, используйте этот метод, если нет, то переопределите меня и предоставьте свою собственную функциональность...
И, конечно, если вы переопределяете виртуальный метод, вы всегда можете обратиться к родительскому методу, вызвав
base.myVirtualMethod()
Хорошо, когда вы видите такой метод:
То, что у вас действительно есть (за кулисами), - это подпись, подобная этой.A.Foo();Foo(A x);И когда вы вызываете
Теперь, иногда вы хотели бы иметьA.Foo(), вы действительно вызываетеFoo(this), гдеthis- ссылка на объект типа A.Foo(A|B|C|D...), гдеFoo- это метод, который может принимать или тип A, или B, или C, или D. Но вы не хотите беспокоиться о том, какой тип вы передаете, вы просто хотите, чтобы он делал что-то другое на основе о типе, который был передан. Абстрактные методы позволяют вам это делать, это их единственная цель.