С появлением методов расширения абстрактные классы стали менее привлекательными? - PullRequest
8 голосов
/ 16 марта 2009

Одним из интересных аспектов методов расширения в .NET является тот факт, что вы можете применять их к интерфейсам. Мне кажется хорошим, что я могу определить функциональность рядом с интерфейсом, не определяя абстрактный класс, который загромождает сборку.

Я знаю, что абстрактные классы не устарели или что-то еще, но как вы относитесь к использованию этого побочного эффекта в вашем коде?

Пример:

public static class IUserExtensions
{
    public static bool IsCurrentUser(this IUser user)
    {
        return (HttpContext.Current.User != null &&
                HttpContext.Current.User.Identity.Name == user.ID.ToString());
    }
}

public interface IUser {
    int ID { get; set; }
}

Ответы [ 11 ]

7 голосов
/ 16 марта 2009

Что позволяет делать методы расширения, так это сосредоточиться на том, что на самом деле должны делать абстрактные классы. Существует соблазн реализовать «служебный» код в абстрактных классах, потому что он будет использоваться разработчиками, даже если он не является частью дерева логического наследования. Методы расширения позволяют вам присоединять эти служебные методы к интерфейсу, не загромождая абстрактные базовые классы.

EDIT

В частности, я бы применил эти рекомендации.

Наследование

  • НЕ используйте наследование, если поведение является частью логического поведения базового класса
  • НЕ используйте наследование, если поведение носит сквозной характер (относится к вещам вне вашей иерархии объектов). Это потребует дублирования.

Сервисные классы

  • НЕ используйте служебные классы (статические классы), если поведение логически не относится к классу, на которое оно действует
  • НЕ используйте служебные классы, если они изменяют внутреннее состояние объекта, на который он воздействует. Изменение состояния должно быть зарезервировано только для иерархии реализации объекта.

Методы расширения

  • НЕОБХОДИМО использовать то же решение для методов расширения, что и для служебных классов, когда они создают меньшее трение. Если принятие методов расширения кажется менее естественным, не делайте этого.
  • НЕ используйте методы расширения, чтобы добавить утилиту к классам, над которыми у вас нет контроля (например, строки).
  • НЕ ИСПОЛЬЗУЙТЕ методы расширения, когда поведение потребляется классом, который он расширяет. Хотя это можно сделать, он чувствует надуманным
  • НЕ используйте методы расширения только потому, что вы можете. На самом деле не отклоняйтесь от старого доброго ООП, если вам не нужно . Однако при необходимости методы расширения являются относительно бесполезным и безвредным решением.

РЕДАКТИРОВАТЬ 2

Мысль о другом DO для методов расширения

НЕОБХОДИМО использовать методы расширения для обеспечения естественного языка (внутреннего DSL) для определенных реализаций. Вот глупый пример.

int age = getAge();
if (age.IsDraftAge() && !age.IsLegalDrinkingAge())
{
    Console.WriteLine(@"You cannot drink until your birthdate on {0}.
Join the army instead.",
                      age.GetYearWhenGettingDrunkIsOk());
}
4 голосов
/ 16 марта 2009

Я использовал эту функциональность несколько раз - и фактически эта функциональность была создана специально для украшения интерфейсов без их изменения, следовательно, LINQ. Все достоинства LINQ (по крайней мере для объектов) основаны на методах расширения до IEnumerable .

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

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

Мой ответ на ваш заглавный вопрос таков: нет, существование методов расширения не делает абстрактные базовые классы менее привлекательными. Если кто-то считает, что существование методов расширения делает абстрактные базовые классы менее привлекательными, то, во-первых, он неправильно использовал абстрактные базовые классы.

3 голосов
/ 16 марта 2009

Нет, это уменьшит некоторые ошибки в абстрактных классах.

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

2 голосов
/ 16 марта 2009

Мне нравятся методы расширения, потому что он дал мне возможность определять Find (Predicate), Remove (Predicate) и т. Д. Для IList<T>, который я пропустил. Я не думаю, что вы можете сказать, что они заменяют абстрактные классы.

Я также определил методы расширения для добавления общего поведения. Мне очень нравится мой метод расширения ToJson для объекта или ToXml.

Единственная проблема, с которой я столкнулся в вашем примере - я всегда смотрю на IsCurrentUser как на свойство, а не на метод, и, увы, у нас нет свойств расширения. Но я придираюсь

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

1 голос
/ 16 марта 2009

Методы расширения хороши, но вы не можете сравнить их или их использование с абстрактными классами. Не забудьте, что такое инкапсуляция , это один из столпов ООП ...

1 голос
/ 16 марта 2009

Нет, абстрактные занятия не менее привлекательны. Абстрактные классы обеспечивают правильную парадигму для иерархий объектов.

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

0 голосов
/ 16 марта 2009

Если вам не нужен доступ к защищенным полям из метода расширений, тогда это AFAIK путь класс).

0 голосов
/ 16 марта 2009

Я думаю, что есть абсолютно случаи, когда это хорошая идея. Linq - отличный пример преимуществ, но есть и другой: streams.

Причина, по которой потоки являются абстрактным классом, заключается в том, что они должны поддерживать такие методы, как BeginRead, ReadByte и т. Д. Stream может быть интерфейсом, а все эти методы могут быть методами расширения. Это позволило бы сделать такие вещи, как наследование от не потокового класса и добавить функциональность потока, и сделать (более) естественным добавление потенциально полезных потоковых методов, таких как «сброс содержимого потока в буфер памяти».

0 голосов
/ 16 марта 2009

Одним словом: да. В связи с тем, что абстрактные классы накладывают большие ограничения на будущие реализации, общий совет, по-видимому, заключается в создании абстрактных классов, реализующих интерфейсы (например, DBDataReader: IDataReader). Пока метод не требует доступа к внутреннему состоянию объекта, я не вижу причин, чтобы не отображать его как метод расширения. Вы получаете функциональность без затрат.

0 голосов
/ 16 марта 2009

Я думаю, это зависит от вашего использования-

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

Однако для тех, кого больше волнует, что абстрактные классы представляют концептуально , мало что изменилось. В организационном контексте кода все еще имеет смысл помещать "public int getCalories ()" в абстрактный класс "Food", так как было бы глупо для человека, который его ест (код, использующий подкласс пищи), определить, сколько калорий у него есть.

...