Лично мне почти никогда не нужно писать абстрактные классы.
В большинстве случаев я вижу, что абстрактные классы используются (неправильно), потому что автор абстрактного класса использует шаблон «Шаблонный метод».
Проблема с «Методом шаблона» заключается в том, что он почти всегда несколько повторяется - «производный» класс знает не только о «абстрактном» методе своего базового класса, который он реализует, но и о открытых методах базовый класс, хотя в большинстве случаев ему не нужно вызывать их.
(чрезмерно упрощенный) пример:
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
Итак, здесь автор этого класса написал общий алгоритм и намеревается, чтобы люди использовали его, «специализируя» его, предоставляя свои собственные «зацепки» - в данном случае, метод «сравнения».
Итак, предполагаемое использование примерно так:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
Проблема в том, что вы неправильно связали вместе два понятия:
- Способ сравнения двух элементов (какой элемент должен идти первым)
- Метод сортировки элементов (т.е. быстрая сортировка или сортировка слиянием и т. Д.)
В приведенном выше коде, теоретически, автор метода «сравнения» может повторно вызвать обратно метод «Сортировки» суперкласса ... даже на практике они никогда не захотят или не будут нуждаться сделать это.
Цена, которую вы платите за эту ненужную связь, заключается в том, что трудно изменить суперкласс, а в большинстве языков OO его невозможно изменить во время выполнения.
Альтернативный метод - вместо этого использовать шаблон проектирования «Стратегия»:
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
Итак, обратите внимание: все, что у нас есть, это интерфейсы и конкретные реализации этих интерфейсов. На практике вам больше ничего не нужно для создания ОО высокого уровня.
Чтобы «скрыть» тот факт, что мы реализовали «сортировку имен» с использованием класса «QuickSort» и «NameComparator», мы все равно могли бы написать где-нибудь фабричный метод:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
В любой раз, когда у вас есть абстрактный класс, вы можете сделать это ... даже если между базовым и производным классом существуют естественные реентерабельные отношения, обычно стоит сделать их явными.
Одна заключительная мысль: все, что мы сделали выше, это «составление» функции «NameSorting» с помощью функции «QuickSort» и функции «NameComparison» ... на функциональном языке программирования этот стиль программирования становится четным более естественный, с меньшим количеством кода.