Просмотров: 6697

Твердые объектно-ориентированные принципы. Принцип Лисков


Продолжаем начатую в прошлом посту серию «Твердые объектно-ориентированные принципы» — теперь мы отдельно и подробно остановимся на Liskov Substitution Principle (LSP).

Глубоко вдохните, выдохните, а теперь поехали головой орехи колоть.

~

Вот резюме определения этого принципа (более подробно изложено в прошлой статье):

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

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

Основные тезисы:

  1. предопределенные условия метода/класса не могут быть усилены
  2. постусловия не могут быть ослаблены
  3. все возбуждаемые исключения должны быть в равной степени заменяемыми
  4. сигнатуры методов должны быть полностью совместимы

PHP oop ооп  объектно-ориентированное программирование SOLID objects объекты принципы

Когда мы пытаемся применить это на практике, бывает сложно это сделать. Так, с чего же начать... Давайте начнем с одного из первых примеров, который многие из нас видели, когда начинали обучаться ООП. Геометрические фигуры!

Вот простой класс Rectangle (прямоугольник):

class Rectangle {
    protected $_height = '';
    protected $_width = '';
 
    public function __construct($height, $width) {
        $this->_height = $height;
        $this->_width = $width;
    }
 
    public function getArea() {
        return $this->_height*$this->_width;
    }
}

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

Теперь давайте расширим этот класс и получим класс Square (квадрат).

Т.к. мы знаем, что все квадраты это прямоугольники, но не все прямоугольники это квадраты, то логично приходим к такой иерархии классов:

class Square extends Rectangle {
    public function __construct($side) {
        parent::__construct($side, $side);
    }
}

К сожалению, у нас разные сигнатуры методов, т.е. мы нарушили требование №4.

Опаньки, так мы же сейчас это исправим:

class Square extends Rectangle {
    public function __construct($height, $width) {
        parent::__construct($height, $width);
    }
}

Но сейчас мы пришли к тому, что наш Квадрат может быть неквадратным, т.о. надо добавить валидацию сторон:

class Square extends Rectangle {
    public function __construct($height, $width) {
        if ($height != $width) {
            throw new Exception("That isn't a square!");
        }
        parent::__construct($height, $width);
    }
}

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

Приехали — это опять нарушение требований из списка выше: №1 и №3.

Т.о. принцип LSP трудно правильно реализовать даже с очень простой структурой классов.

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

Если мы не можем применить на практике один из основных принципов «хорошего» объектно-ориентированного дизайна здесь, менее чем в 20 строках кода, то, как мы можем это сделать в реальном мире? Можно ли вообще ожидать этого?

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

~

Для тех же, кто слишком умен для этого блога и уже занес руку чтобы аргументированно двинуть в ухо автора по поводу вышеприведенной "проблемы" LSP, попытаюсь остановить это импульсивное движение магическим уточнением — диаграмма Венна:

PHP oop ооп  объектно-ориентированное программирование SOLID objects объекты принципы

Поясняю: этот принцип (LSP) в терминах теории множеств будет звучать так:

Свойство q, выполняющееся для объектов множества (типа) S, должно выполняться для объектов подмножества (подтипа) T.

Это и есть тот самый ключ ко всем сложностям Принципа подстановки Барбары Лисков. Впрочем, более подробно об этом (и многом другом) мы поговорим уже в следующий раз.

~

В догонку: вот прекрасный пример для закрепления сути всего сказанного.

twitter.com facebook.com vkontakte.ru odnoklassniki.ru mail.ru ya.ru pikabu.ru blogger.com liveinternet.ru livejournal.ru google.com bobrdobr.ru yandex.ru del.icio.us

Подписка на обновления блога → через RSS, на e-mail, через Twitter
Теги: , , , , ,
Эта запись опубликована: Среда, 25 января 2012 в рубрике Программирование.

2 комментария

Следите за комментариями по RSS
  1. Василий

    > Если мы не можем применить на практике один из основных принципов

    > «хорошего» объектно-ориентированного дизайна здесь, менее чем в 20

    > строках кода, то, как мы можем это сделать в реальном мире? Можно ли

    > вообще ожидать этого?

    Отлично ответил автор статьи на хабре:

    > мы бы могли догадаться, что придуманная нами модель абстракции

    > в новых реалиях — полное фуфло

    И свои три копейки: любая модель, любая теория верна лишь в границах области применимости.

    Вышли за границы - получите фейл, и вперед - к новой модели.

    А принцип как раз и должен натолкнуть на мысль ДО того, как новый и дорогой контроллер температуры задымится

  2. Константин

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

Оставьте комментарий!

Не регистрировать/аноним

Используйте нормальные имена. Ваш комментарий будет опубликован после проверки.

Зарегистрировать/комментатор

Для регистрации укажите свой действующий email и пароль. Связка email-пароль позволяет вам комментировать и редактировать данные в вашем персональном аккаунте, такие как адрес сайта, ник и т.п. (Письмо с активацией придет в ящик, указанный при регистрации)

(обязательно)


⇑ Наверх
⇓ Вниз