Следует ли удалить наследование (неинтерфейсных типов) из языков программирования? - PullRequest
14 голосов
/ 12 декабря 2008

Это довольно спорная тема, и, прежде чем сказать «нет», действительно ли она действительно нужна?

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

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

Мое единственное предостережение: мы все равно разрешаем наследование интерфейсов.

(Update)

Давайте приведем пример того, почему это необходимо, вместо того, чтобы сказать: «иногда это просто необходимо». Это действительно бесполезно. Где ваше доказательство?

(пример обновления кода 2)

Вот классический пример формы, более мощный и более явный IMO, без наследования. В реальном мире почти никогда не бывает так, чтобы что-то действительно "являлось" чем-то другим. Почти всегда "Реализовано в терминах" более точно.

public interface IShape
{
    void Draw();
}

public class BasicShape : IShape
{
    public void Draw()
    {
        // All shapes in this system have a dot in the middle except squares.
        DrawDotInMiddle();
    }
}

public class Circle : IShape
{
    private BasicShape _basicShape;

    public void Draw()
    {
        // Draw the circle part
        DrawCircle();
        _basicShape.Draw();
    }
}

public class Square : IShape
{
    private BasicShape _basicShape;

    public void Draw()
    {
        // Draw the circle part
        DrawSquare();
    }
}

Ответы [ 20 ]

2 голосов
/ 12 декабря 2008

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

2 голосов
/ 12 декабря 2008

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

1 голос
/ 03 февраля 2010

Я считаю, что лучшим механизмом повторного использования кода, который иногда достигается с помощью наследования, являются черты . Посмотрите эту ссылку (pdf) , чтобы обсудить это, в том числе различие между чертами и миксинами, и почему предпочтения черт.

Есть некоторые исследования, которые вводят черты в C # (pdf) .

У Perl есть черты через Moose :: Roles . Черты Scala похожи на mixins , как в Ruby .

1 голос
/ 12 декабря 2008

Первоначально я думал, что ты сумасшедший. Но, подумав немного, я с вами согласен. Я не говорю полностью удалить Class Inheritance (например, абстрактные классы с частичной реализацией могут быть полезны), но я часто наследовал (предназначенный для каламбура) плохо написанный OO-код с многоуровневым наследованием классов, который ничего не добавил, кроме bloat, к код.

1 голос
/ 12 декабря 2008

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

Так что если вы серьезно относитесь к внедрению зависимости (а я нет, но мне интересно, должен ли я), вы все равно не сможете получить много пользы от наследования.

1 голос
/ 12 декабря 2008

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

Аналогично, как насчет Java, например, где каждый объект наследуется от Object и, следовательно, автоматически имеет реализации equals, hashcode и т. Д. Мне не нужно их повторно реализовывать, и это «просто работает», когда я хочу использовать объект в качестве ключа в хеш-таблице. Мне не нужно писать проход по умолчанию для метода Hashtable.hashcode(Object o), который, откровенно говоря, кажется, что он перемещается от ориентации объекта.

1 голос
/ 12 декабря 2008

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

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

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

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

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

1 голос
/ 12 декабря 2008

Действительно ли наследство необходимо? Зависит от того, что вы подразумеваете под «действительно». Вы можете вернуться к перфокартам или теоретически переключить тумблеры, но это ужасный способ разработки программного обеспечения.

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

Например, возьмем случай digiarnie в этой теме. Он / она использует интерфейсы почти для всего, что так же плохо, как (возможно, и хуже) с использованием большого количества наследования.

Некоторые из его очков:

это помогает с тестированием и улучшает читабельность

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

То же самое для глубокой иерархии наследования. В идеале вы хотите посмотреть только в одном месте.

Используя интерфейсы, я получил бы тот же эффект, но я бы имитировал наследование, используя 'Делегация'.

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

также значительно упрощает рефакторинг.

Ранняя привязка к интерфейсам обычно усложняет рефакторинг, а не облегчает его, потому что тогда остается больше мест для изменений. Злоупотребление наследованием на ранних этапах лучше (ну, не так плохо), как злоупотребление интерфейсами, поскольку извлечение классов делегатов легче, если изменяемые классы не реализуют никаких интерфейсов. И это довольно часто от этих делегатов, чем вы получаете полезные интерфейсы.

Так что злоупотребление наследованием - это плохо. Злоупотребление интерфейсами - это плохо. И в идеале, класс не будет ни наследовать (кроме, может быть, «объекта» или языкового эквивалента), ни реализовывать какие-либо интерфейсы. Но это не означает, что любая из этих функций должна быть удалена из языка.

1 голос
/ 12 декабря 2008
0 голосов
/ 08 января 2009

Вопрос в том, «Следует ли исключить наследование (неинтерфейсных типов) из языков программирования?»

Я говорю «нет», так как это сломает чертовски много существующего кода.

Кроме того, следует ли вам использовать наследование, кроме наследования интерфейсов? Я в основном программист на С ++, и я придерживаюсь строгой объектной модели множественного наследования интерфейсов, за которым следует цепочка одиночного наследования классов . Конкретные классы являются «секретом» компонента и его друзей, поэтому то, что происходит, не имеет никакого отношения к делу.

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

Сказав это, парадигма mixin в значительной степени уникальна для C ++. Без этого я ожидаю, что наследование будет очень привлекательным для прагматичного программиста.

...