7.10.06 12:28

Python, Проектирование

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

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

Для оформления более строго наследования в языках, где есть скрытие данных (Delphi, C++, Java), широко применяется паттерн "Template Method" (из книжки "Design Patterns", которую должен прочитать каждый объектно-ориентированный программист). По этому паттерну поведение объекта оформляется невиртуальными публичными методами, которые сами по себе не перекрываются. А уже внутри такого метода выполняются как обязательные куски, так и вызываются виртуальные методы, которые можно наследовать.

TDocument=class
Private
  Procedure Backup;
Protected
  Procedure DoSaveData; Virtual;
Public
  Procedure Save;
end;

Procedure TDocument.Save;
Begin
  Backup;
  DoSaveData; { Перекрывая этот метод, наследник не сможет забыть
                вызвать Backup }
End;

Основной минус строго подхода в том, что при создании базового класса программисту нужно как-то предвидеть все возможные способы использования класса в будущем. И если она их не смогла все предвидеть, то тот, кому этот непредвиденный способ понадобится, будет, что называется, "outta business". Хотя, конечно, если они в одной команде или это один человек, то можно и базовый класс переписать...

Питон же исповедует свободный подход практически во всем, и там, собственно, и скрытия данных как такового нет (использование "__" для приватности -- это хак, некрасивый и легко обходится). Поэтому все методы по сути публичные виртуальные. С одной стороны, это хорошо, потому что устраняет основной минус строгого подхода, но с другой стороны на практике у класса обычно все равно есть методы, которые автор расчитывал на будущие изменения, а есть и более фундаментальные, которые лучше не трогать. Беда только в том, что выглядят они все одинаково...

В принципе, есть соглашение, что методы названные с одного подчерка -- это внутреннее дело класса, и если вы их используете, то надо быть готовым к тому, что оно может и сломаться. Но это аналог скорее private, чем protected. Потому что protected -- это скорее "вот это удобнее использовать при наследовании". И вот этого -- не хватает.

Комментарии: 6 (feed)

  1. Andrey Fedoseev

    Мне кажется, это задача разработчика, следить за тем, какие методы можно перекрывать и как это нужно делать. Конечно, для этого нужно хорошо ориентироваться в коде наследуемого класса, но Питон это такой язык, который позволяет держать в голове большой объем кода (а точнее знаний, о том, как этот код работает) из разных классов. Хаки типа _ это дополнительная подсказка, хороший тон при разработке. И этого, мне кажется, вполне достаточно.

    Я, когда перекрываю метод, как правило делаю так:

    class Spam(Ham):
        def eggs(self):
            Ham.eggs(self)
            [..my code goes here..]
    

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

    Вообще, я без стеснения перекрываю методы типа __init__ или __del__, если это необходимо. В конце-концов это МОЙ класс, пусть даже он унаследован от "чужого" :)) Главное, это понимать, что ты делаешь.

  2. Smash

    Иван, а почему "она [...] не смогла"?
    Откуда там женский род?

  3. Иван Сагалаев

    Почему бы и не женский? :-) Вполне возможно, что этот гипотетический программист -- девушка.

  4. Иван Сагалаев

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

    Которого разработчика? Который базовый класс пишет или который наследует? :-)

    Конечно, для этого нужно хорошо ориентироваться в коде наследуемого класса, но Питон это такой язык, который позволяет держать в голове большой объем кода

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

  5. Петро

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

  6. Иван Сагалаев

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

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

Добавить комментарий

Вы можете подписать комментарий своим OpenID-логином или именем с EMail'ом.

OpenID

Имя и EMail

Текст через пустую строку превращается в отдельные абзацы, цитата отделяется символами > слева, списка состоит из пунктов с дефисом слева, курсив выделяется * с каждой стороны, жирный - двойными **, блоки кода отступают слева на 4 пробела