/ / Ограничението на LSP за укрепване напредпоставки в конфликт с предположенията, че необходимостта от понижаване показва лош дизайн - наследяване, дизайн на класа, понижаване, класово-йерархия, lsp

Дали ограничението на LSP за укрепване на предпоставките в конфликт с предложенията, че нуждата от нестабилност показва лош дизайн - наследяване, дизайн на класа, downcasting, йерархия на класа, lsp

Наскоро започнах да чета за принципа на замяна на Лисков (LSP) и аз се боря да разбера напълнопоследиците от ограничението, че "Предварителните условия не могат да бъдат засилени в подтип". Струва ми се, че това ограничение е в противоречие с принципа на дизайна, който предполага, че трябва да се сведе до минимум или изцяло да се избегне необходимостта от понижаване от основата на производен клас.

Това означава, че започвам с Animal и да извличат животните Dog, Bird, и Human, Ограничението на LSP за предварителните условия е ясносе вписва в природата, доколкото нито едно куче, птица или човек не трябва да бъдат по-ограничени от общия клас животни. Придържайки се към LSP, получените класове ще добавят специални функции, като например Bird.fly() или Human.makeTool() които не са общи за Animal.

Той се чувства малко абсурдно за базовия клас Animal да има виртуални методи за всяка възможна характеристика на всеки възможен подтип животни, но ако не стане тогава, ще трябва да премахна Animal позоваване на основния подтип за достъптези уникални характеристики. Тази необходимост от понижаване обаче обикновено се счита за червен флаг за лош дизайн. Уикипедия дори стига дотам, че предполага, че е поради LSP, че понижаването на стойността се счита за лоша практика.

И така, какво ми липсва?

Въпрос за бонус: Помислете отново за йерархията на класовете на Animals описано по-горе. Очевидно би било нарушение на LSP, ако Animal.setWeight(weight) изисква само неотрицателно число, но Human.setWeight(weight) засили тази предпоставка и изискваше неотрицателно число по-малко от 1000. Но какво да кажем за конструктора за Human, което може да изглежда Human(weight, height, gender)? Ще бъде ли LSP нарушение, ако конструкторът наложи лимита на теглото? Ако е така, как тази йерархия трябва да бъде преработена, за да се спазят ясните граници на физическите свойства на получените животни?

Отговори:

1 за отговор № 1

LSP е всичко за поведенчески подтип. Грубо казано, B е подтип на A ако винаги може да се използва където A се очаква. Освен това такова използване не трябва да променя очакваното поведение.

Така че, като се има предвид прилагането на LSP, основната точка е какво "очаквано поведение на." AВ твоя пример е Animal, Не е толкова просто да се проектира полезно Animal интерфейс, който е общ за всички животни.

Придържайки се към LSP, получените класове ще добавят специални функции, като Bird.fly() или Human.makeTool() които не са общи за животните.

Не точно. LSP приема, че се занимавате само с Animalс. Сякаш нямаше да може да се срине Human, Bird и други животни могат да имат някакви методи, конструктори или каквото и да е друго. Той изобщо не е свързан с LSP. Те просто трябва да се държат както се очаква, когато се използват като Animalс.

Проблемът е, че такива интерфейси са много ограничени. На практика ние често трябва да използваме превключване на типа, за да позволят на птиците да летят и хората да правят полезни инструменти.

Два общи подхода в основните езици на ООП са:

  1. Даункастинг
  2. Модел на посетителите

В това няма нищо лошоконтекст, тъй като това е начинът, по който обикновено правите превключване на типа в езици, които не поддържат типови варианти. Можете да прекарвате много време, въвеждайки йерархии на интерфейси, за да избегнете изрично downcasting, но обикновено това просто прави кода по-малко четлив и по-труден за поддържане.


1 за отговор № 2

Много аспекти на програмирането включват компромис иСред тях са принципите SOLID. Ако има някои видове действия, които могат да бъдат извършени по един и същи начин с почти всички производни на клас или реализации на интерфейс, и не са наистина част от основната цел на интерфейса, но няколко конкретни деривати или реализации могат да имат по-добър начин да ги направят, "Принципът на сегрегацията на интерфейса" би предполагал, че такива действия не се включват в общия интерфейс (*). тип, за да провери дали действителният обект има определени "специални" характеристики и да ги използва, ако е така IEnumerable<Animal> и иска да знае колко елемента съдържа може да провери дали изпълнява ICollection<Animal> или не-родово ICollection [отбележи, че List<Cat> изпълнява последното, но не и първото] и - ако е така - намалява и използва Count Метод]. В такива случаи няма нищо лошо, тъй като методът не изисква преминаване в такива случаи - просто работи по-добре.

(*) IMHO, IEnumerable трябва да е включил метод за описание на свойствата на последователността, като например дали е известно броя, дали тя ще съдържа повече от един и същ артикул и т.н.

Друго използване на свиване се наблюдава в случаите, когаточовек ще има колекция от групи обекти и ще знае, че конкретните обекти в рамките на всяка група са "съвместими" помежду си, въпреки че обектите в една група може да не са съвместими с обекти в друга. Например, MaleCat.MateWith() Методът може да приеме само инстанция на FemaleCat, и FemaleKangaroo.MateWith() с може да приеме само инстанция на MaleKangaroo()но най-практичният начин за Ной да има колекция от чифтосващи се двойки животни е всеки вид животно да има MateWith() метод, който приема Animal и низходящи към правилния тип (и вероятно също имат a CanMateWith() Имот). Ако MatingPair е конструиран съдържащ a FemaleHamster и MaleWolf, опит да се позове на Breed() метод на тази двойка ще се провали по време на изпълнение, но акоКодексът избягва изграждането на несъвместими чифтосващи се двойки. Обърнете внимание, че генеричните лекарства могат значително да намалят необходимостта от този вид премахване, но не и напълно да го премахнат.

При определяне дали downcasting нарушава LSP, въпросът за $ 50,000 е дали даден метод ще запази договора си за всичко, което може да бъде прехвърлено. MateWith() договорът на метода уточнява, че той е гарантиран само, че ще се държи полезно по конкретни случаи Animal за което CanMateWith() е върнал вярно, фактът, че ще се провали, когато се дадат някои подтипове на Animal не би било нарушение на LSP. По принцип, полезно е да има методи за отхвърляне на обекти за време на компилация, чийто тип не е гарантирано да бъде използваем, но в някои случаи кодът може да има познания за отношенията между видовете определени обекти, които не могат да бъдат изразени синтактично [напр. факта, че a MatingPair ще проведе две Animal копия, които могат успешно да бъдат отгледани]. Докато downcasting често е мирис на код, няма нищо лошо в него, когато се използва по начин, съвместим с договора на обекта.