No video

Принцип подстановки Барбары Лисков

  Рет қаралды 7,404

Timur Shemsedinov

Timur Shemsedinov

Күн бұрын

Примеры кода: github.com/HowProgrammingWork...
Оглавление курса: github.com/HowProgrammingWork...

Пікірлер: 27
@anryzhov
@anryzhov 4 жыл бұрын
Тимур, благодарю за лекцию
@timashoff
@timashoff Жыл бұрын
Спасибо!
@TimurSevimli
@TimurSevimli Жыл бұрын
Спасибо большое за лекцию! Это тема чего -то напоминает Information Expert из Grasp принципов))
@anryzhov
@anryzhov 4 жыл бұрын
Тимур есть еще твои лекции по принципам SOLID?
@dimitro.cardellini
@dimitro.cardellini 4 жыл бұрын
Не хватает еще одного кейса. Кстати, очень важного для ООП. type T = A|B|C; типы A, B, и C являются подтипами T (т.к. множество значений в рамках каждого из них, явяляется подмножеством значений в рамках T, что следует из объявления N), но не являются его наследниками. например, мы можем сделать функцию, которая будет принимать: массив, объект или строку в качестве параметра, и потом в зависимости от того, что пришло, будет производить действия. Этот кейс на самом деле также покрывается L-принципом. И думаю, имеет смысл показать, как это правильно разрулить в коде. Реальный пример: методы объекта application из Express.JS (там, по-моему, массив, строка и, кажется RegExp)
@test_bot5541
@test_bot5541 4 жыл бұрын
"...и потом в зависимости от того, что пришло, будет производить действия... " - ну, эта фраза противоречит Лиске, т. к. функции должно быть плевать на фактический тип того, что пришло, главное, чтоб аргумент соответствовал контракту супертипа. Т. е. ф-ция пограммируется на уровне этого базового контракта, без разводки на подтипы, которые его реализуют. В ООП это восходящее преобразование. Наверное вопрос в том, попадает ли под Лиски утиная типизация. Если есть, скажем, независимая ф-ция join, куда можно передовать массив или псевдомассив (объект, поля котрого это индексы: "0", "1" ... и есть св-во length), а на выходе получить строку с разделителем. Массив и псевдомассив имеют общего предка Object.prototype, который к делу не относится, но зато реализуют 1 и тот же контракт (по индексам и длине), так что внутренней реализации join не важно что это на самом деле - массив или объект, главное, что б оно выглядело как "утка". Фактически этот нигде не специфицированный контракт и есть супертип аргумента.
@dimitro.cardellini
@dimitro.cardellini 4 жыл бұрын
@@test_bot5541 ну речь идёт именно о тех случаях, когда аргумент может принимать значения типов, не имеющих общего предка, реализующего нужный интерфейс. Если взять пример с express, то по-хорошему, нам нужен предикат, который скажет, удовлетворяет ли путь некоторому условию. Но, в качестве аргументов передаются совсем разные типы. Лиски, как раз и говорит: не пишите такой код, когда надо разруливать тип аргумента внутри алгоритма. И в коде правильнее было бы разрулить созданием мапперов /кастеров/ разных типов /не из одной иерархии/ в один "основной тип" (в C++ часто используют перегрузку конструктора). В примере, надо было бы сделать мапперы строк и регулярное, а также их массивов в предикаты и вызывать уже методы монтирующие обработчики на конкретные пути с результатами этих мапперов. Но, такой кастинг "шумит" в коде ... Поэтому, имеет смысл сделать одну функцию, которая реализует алгоритм и принимает аргумент "базового типа", вторую, которая делает преобразование типов, и третью -- котооая будет использовать две первых. Т.е. уйти от использования подтипов в ключевом алгоритме, как таковом -- чтобы соответствовать принципу Лисков на уровне алгоритма, и следуя принципу единственной ответственности правильно распределить логику между функциями (или классами). Ну и да СОЛИД -- не только про ООП на классах ...
@ericraudy
@ericraudy 4 жыл бұрын
Надо же, я прямо сейчас нечто подобное изобретал по работе, не зная что это так называется)))
@TimurShemsedinov
@TimurShemsedinov 4 жыл бұрын
По поводу принципа или его конкретного применения?
@dimitro.cardellini
@dimitro.cardellini 4 жыл бұрын
18:16 Реализация Cancelable весьма условная. Во-первых, отмена не отработает на реджекте. Во-вторых, отмена отработает только после резолва основного исполнителя, что мягко говоря не ожидается от "отмены". Вот исправленная реализация с примером использования. gist.github.com/DScheglov/8ad1a59850a23f32751487f9e1078135 Но, проблема заключается в том, что так вообще нельзя делать потому, что такие промисы чейниться не будут. Пример ниже показывает, что так работать не будет: gist.github.com/DScheglov/8ad1a59850a23f32751487f9e1078135#gistcomment-3092598
@user-yu7gm4df3f
@user-yu7gm4df3f 3 жыл бұрын
там в сет таймауте нужно передать через лямбду ()=>{resolve('something')}
@ArMANIAK666
@ArMANIAK666 4 жыл бұрын
Тимур, а почему на примере юзерАккаунта Вы говорите, что принцип подстановки ломается? Ведь код его употребляет и работает. Другое дело, что нет видимого эффекта, но это уже другая история
@dimitro.cardellini
@dimitro.cardellini 4 жыл бұрын
там меняется контракт для метода withdraw. Определим свойство q(x) так: если для экземпляра x вызвать метод withdraw, передав в него отрицательное число, то баланс x увеличиться на модуль переданного отрицательного числа. Очевидно, что для q(new Account()) = true, а вот q(new UserAccount()) = false. Поэтому можно говорить, о нарушении L-принципа.
@user-bo4qo1vz1j
@user-bo4qo1vz1j 4 жыл бұрын
@@dimitro.cardellini выходит если я передаю в функцию наследника и получаю результат, отличный от результата вызова с родителем, то я автоматически нарушил принцип подстановки?
@dimitro.cardellini
@dimitro.cardellini 4 жыл бұрын
@@user-bo4qo1vz1j не совсем ) Какой бы тогда был смысл в переопределении (override).. Весь СОЛИД -- это весьма абстрактная штука, а Лисков -- особенно )) Смысл в том, что нарушение касается контракта базового типа. Если алгоритм, который использует некий базовый тип ожидает одно поведение, а получит другое, то результат алгоритма станет не предсказуемым ... Иными словами, если родительский тип пропускает операцию с отрицательным балансом, а наследник будет выбрасывать исключение, то ... Придется менять алгоритм, добавляя в него перехват исключения и его корректную обработку. И наоборот, все может пойти не так, если алгоритм ждём исключения, а метод наследника вернёт объект с признаком некорректности, к-й понятен только ему ... В общем, очень многое зависит от контракта
@johnins1646
@johnins1646 2 жыл бұрын
@@dimitro.cardellini Я что-то тоже понять не могу лсп это про соблюдение контрактов или про то, чтобы не ломалась логика?
@dimitro.cardellini
@dimitro.cardellini 2 жыл бұрын
@@johnins1646 ну, власне, дотримання контракту -- це необхідна умова для того, щоб логіка не ломалась. Необхідна, але не достатня. Але є різні рівні формалізації контракту. Тобто формальний контракт може покривати не всю логіку, а лише окремі частини. Тому дивіться на LSP з математичної точки зору: Якщо є будь-яка суттєва з точки зору логіки умова, що виконується для типу (базового класу, як окремий випадок), але не виконується для підтипу (клас-спадкоємець), то для підтипу LSP порушено, що має бути виправлено.
@romanilienko2357
@romanilienko2357 4 жыл бұрын
Правильно «Барбары Стрейзенд»
@dimitro.cardellini
@dimitro.cardellini 4 жыл бұрын
16:30 -- там оговорка в описании того, что происходит в методе. В UserAccount добавляется "защита от дурака", которая не позволяет увеличивать баланс с помощью функции withdrow, передавая отрицательные элементы. Здесь действительно ломается L-принцип, потому что нарушается контракт для этого метода. Решение: перенос этой проверки в базовый тип.
@whatthepeople
@whatthepeople 3 жыл бұрын
А если у UserAccount, например, действительно не должно быть такой возможности, т.е. уходить в минус нельзя (допустим, что мы расширим этот метод до проверки amount < value, чтобы обычный "пользователь" не смог зайти в минус), а для какого-нибудь AdminAccount мы такую логику оставляем, т.е. администратор может уйти в минус. Я правильно понимаю, что для этого мы должны создать ещё два класса, например, PremiumAccount и UsualAccount, которые насследуются от Account и накладывают или снимают ограничения, а уже от них будут AdminAccount и UserAccount?
@dimitro.cardellini
@dimitro.cardellini 3 жыл бұрын
​@@whatthepeople 1. в примере кода нет проверки на то, можно уходить в минус или нет (и что узвучил Тимур), там есть проверка можно ли списывать отрицательные суммы (т.е. вместо списания пополнять счет) или нет. 2. LSP гласит, что все (и каждое) свойство, присущее базовому классу, должно быть присуще и всем его потомкам. Т.е. у нас есть свойство: "Можно выполнять списание, если сумма операции отрицательная". Это свойство выполняется для базового класса, но не выполняется для потомка. Это означает, что LSP в потомке нарушен. Предложенное Вами решение, с промежуточными классами, разделяет иерархию счетов на две под-иерархии, в каждой из которых LSP соблюдаться будет, но на уровне всей иерархии -- нет. Это означает, что в коде также придется разделить иерархии счетов -- и счета из одной иерархии не могут использоваться в качестве счетов другой иерархии. Если это оправдано бизнес-требованиями -- то, такое решение вполне уместно. 3. Вместе с тем, предложенное Вами решение с классами "PremiumAccount и UsualAccount" скорее всего просто усложнит поддержку кода. Я бы посоветовал, до того, как анализировать LSP, посмотреть на требования SRP к решению Вашей задачи (и здесь не так важно, какую именно проверку надо добавить: неотрицательность баланса или неотрицательность суммы операции). Предоположим, что за баланс счетов у нас отвечает бухгалтерия, за логику "не может уходить в минус" -- менеджер банковского продукта. И получается, что проверку неотрицательности баланса после операции надо реализовать в каком-то другом классе, например: UserAccountContract, который и должен отвечать за продуктовые правила ведения счета. Если же предположить, что у нас за все отвечает только одна бухгалтерия, то в контракт базового класса необходимо заложить возможность выбрасывать исключение или возвращать ошибку (см. монада either), в случае если по каким-то причинам нельзя выполнить списание. И даже, если реализация в базовом классе никогда такие исключения бросать (возвращать ошибки) не будет, то потомки этого класса уже будут иметь такую возможность, следуя при этом LSP. Собственно, самым правильным решением будет -- правильно определить контракт базового класса, и правильно разделить ответственность за принятие решения о возможности выполнять операции.
@QuAzI_NODE
@QuAzI_NODE 2 жыл бұрын
@@dimitro.cardellini большое человеческое спасибо! Наконец-то хоть кто-то нормально объяснил.
@dimitro.cardellini
@dimitro.cardellini 2 жыл бұрын
@@QuAzI_NODE ) Не при Тимуре будет сказано, но по ООП лучше смотреть Дмитрия Афанасьева: kzfaq.info Он, к сожалению, пхп-шник, но рассказывает понятно и для js-ников. Вот реальное лучший видео-контент на русском языке по SOLID и Design Pattern-ам. Джавистов лучше не смотреть -- там очень мало контента, который можно рассматривать вне контекста явы ...
@maksymshyshkov2787
@maksymshyshkov2787 4 жыл бұрын
Можно вопрос? А зачем "Нормальный" программист при создании наследника не прочитав Интрефейс прямо такие "ломает" его поведение? Принцип Барбары Лисков это ПРИНЦИП ПРОВЕРКИ КРИВОРУКОСТИ? ))
@johnins1646
@johnins1646 2 жыл бұрын
Ничего не понимаю. ЛПС это про типы иди про логику? Во всех примерах кроме 4 приводится примеры в разрезе типов(контрактов), а в 4 приводится пример в разрезе бизнес логики. Где логика???
@TimurSevimli
@TimurSevimli Жыл бұрын
Так все понятно.. Вкратце от типов подразумевается все что удовлетворяет контракту и не меняет поведение. Структура должна быть таким что бы очевидным образом получали ожидаемый эффект без логических проверок, вот и вся логика))
@Antonio-fm1sq
@Antonio-fm1sq 2 жыл бұрын
Спасибо!
小天使太有爱心了#天使#小丑#家庭#搞笑
00:32
家庭搞笑日记
Рет қаралды 25 МЛН
Can A Seed Grow In Your Nose? 🤔
00:33
Zack D. Films
Рет қаралды 31 МЛН
SPILLED CHOCKY MILK PRANK ON BROTHER 😂 #shorts
00:12
Savage Vlogs
Рет қаралды 41 МЛН
Type classes в Scala. Основы за 20 минут.
26:26
Aleksey Voronets
Рет қаралды 195
Осторожно - факты, или Что не так со связью
52:42
Радио АВРОРА 9.0
Рет қаралды 271 М.
Пограничное расстройство личности за 10 минут
14:51
Левое полушарие Экстраверта
Рет қаралды 37 М.
SkyOS - An operating system that was developed by one person
12:33
Марк Аддерли
Рет қаралды 876
Антипаттерны общие для всех парадигм
1:30:31
小天使太有爱心了#天使#小丑#家庭#搞笑
00:32
家庭搞笑日记
Рет қаралды 25 МЛН