Наследование классов: должны ли быть совместимы конструкторы? случай множественного наследования?

Одним из рекомендуемых принципов объектно-ориентированного программирования является принцип замещения Лисков: подкласс должен вести себя так же, как его базовый класс(ы) (предупреждение: это на самом деле не правильное описание принципа Лискова: см. PS).

Рекомендуется ли применять его также к конструкторам? В основном я имею в виду Python и его __init__() методы, но этот вопрос относится к любому объектно-ориентированному языку с наследованием.

Я задаю этот вопрос, потому что иногда полезно иметь подкласс, наследуемый от одного или нескольких классов, которые обеспечивают хорошее поведение по умолчанию (например, наследование от словаря в Python, так что obj['key'] работает для объектов нового класса). Однако не всегда естественно и просто разрешать использовать подкласс точно так же, как словарь: иногда было бы лучше, чтобы параметры конструктора относились только к конкретному пользовательскому подклассу (например, к классу, представляющему набор последовательных портов). может захотеть вести себя как словарь, где ports['usb1'] является USB-портом № 1 и т. д.). Каков рекомендуемый подход в такой ситуации? иметь конструкторы подклассов, которые полностью совместимы с конструкторами их базовых классов, и генерировать экземпляры с помощью функции фабрики объектов, которая принимает простые, удобные для пользователя параметры? или просто написать конструктор класса, набор параметров которого нельзя напрямую передать конструктору его базовых классов, но который более логичен с точки зрения пользователя?

PS: я неправильно истолковал принцип Лискова выше: комментарий Свена ниже указывает на тот факт, что объекты подкласса должны вести себя как объекты суперкласса (сам подкласс не имеет вести себя как суперкласс; в частности, их конструкторы не обязательно должны иметь одинаковые параметры [сигнатура]).


person Eric O Lebigot    schedule 23.01.2012    source источник
comment
@SvenMarnach: Ваш комментарий должен быть принятым ответом.   -  person Ferdinand Beyer    schedule 23.01.2012
comment
@SvenMarnach: +1: действительно хорошие моменты. Я был бы рад проголосовать за ваш ответ, если вы его напишете (пожалуйста!). Я также могу отметить это как принятое, так как вы ответили на один из моих основных вопросов.   -  person Eric O Lebigot    schedule 24.01.2012


Ответы (1)


В соответствии с просьбой, я публикую в качестве ответа то, что ранее было комментарием.

Принцип, определенный в связанной статье Википедии, гласит: «Если S является подтипом T, то объекты типа T могут быть заменены объектами типа S». Он не читает «подкласс должен вести себя так же, как его базовый класс (ы)». Разница важна, когда речь идет о конструкторах: версия Википедии говорит только об объектах подтипа, а не о самом типе. Для объекта конструктор уже был вызван, поэтому к конструкторам этот принцип неприменим. Это также то, как я его применяю, и то, как оно применяется в стандартной библиотеке (например, defaultdict и dict).

Конструкторы в множественном наследовании, вероятно, нельзя обсуждать независимо от языка. В Python есть два подхода. Если ваша диаграмма наследования включает ромбовидные шаблоны и вам нужно убедиться, что все конструкторы вызываются ровно один раз, вам следует использовать super() и следовать шаблону, описанному в разделе «Практические советы» статьи Рэймонда Хеттингера Python super() считается супер. Если у вас нет ромбов (за исключением тех, которые включают object), вы также можете использовать явные вызовы базового класса для всех конструкторов базового класса.

person Sven Marnach    schedule 24.01.2012