alt=Portrait of Barbara Liskov|miniatura|La substitució de Liskov va ser introduïda per Barbara Liskov El principi de substitució de Liskov (LSP) és una definició particular d'una relació de subtipificació, anomenada subtipificació comportamental forta, que va ser introduïda inicialment per Barbara Liskov en una conferència magistral de 1987 titulada Abstracció i jerarquia de dades. Es basa en el concepte de "substituïbilitat" un principi de la programació orientada a objectes que estableix que un objecte (com ara una classe) pot ser substituït per un subobjecte (com ara una classe que estén la primera classe) sense trencar el programa. És una relació semàntica més que no pas merament sintàctica, perquè pretén garantir la interoperabilitat semàntica dels tipus en una jerarquia, en particular els tipus d'objecte. Barbara Liskov i Jeannette Wing van descriure el principi succintament en un article de 1994 de la següent manera:

Requisit de subtipus: Sigui ⁠𝜙(𝑥)⁠ una propietat demostrable sobre objectes ⁠𝑥⁠ de tipus T. Aleshores 𝜙(𝑦)⁠ hauria de ser cert per a objectes ⁠𝑦⁠ de tipus S on S és un subtipus de T.

Simbòlicament:

És a dir, si els subtipus S són T, el que s'aplica als objectes T també s'aplica als objectes S. En el mateix article, Liskov i Wing van detallar la seva noció de subtipació conductual en una extensió de la lògica de Hoare, que té una certa semblança amb el disseny per contracte de Bertrand Meyer, ja que considera la interacció de la subtipació amb precondicions, postcondicions i invariants.

Principi La noció de Liskov d'un subtipus de comportament defineix una noció de substituibilitat per a objectes; és a dir, si S és un subtipus de T, aleshores els objectes de tipus T d'un programa es poden substituir per objectes de tipus S sense alterar cap de les propietats desitjables d'aquest programa (per exemple, la correcció).

La subtipificació conductual és una noció més forta que la subtipificació típica de funcions definides en teoria de tipus, que es basa només en la contravariància dels tipus de paràmetres i la covariància del tipus de retorn. La subtipificació conductual és indecidible en general: si q és la propietat "el mètode per a x sempre acaba", aleshores és impossible que un programa (per exemple, un compilador) verifiqui que es compleix per a algun subtipus S de T, fins i tot si q es compleix per a T. No obstant això, el principi és útil per raonar sobre el disseny de jerarquies de classes.

El principi de substitució de Liskov imposa alguns requisits estàndard a les signatures que s'han adoptat en els nous llenguatges de programació orientats a objectes (normalment a nivell de classes en lloc de tipus; vegeu subtipificació nominal vs. estructural per a la distinció):

Contravariància dels tipus de paràmetres de mètode en el subtipus. Covariància dels tipus de retorn de mètodes en el subtipus. Els mètodes del subtipus no poden llançar noves excepcions, excepte si són subtipus d'excepcions llançades pels mètodes del supertipus.

A més dels requisits de signatura, el subtipus ha de complir una sèrie de condicions de comportament. Aquestes es detallen en una terminologia semblant a la de la metodologia de disseny per contracte, cosa que comporta algunes restriccions sobre com els contractes poden interactuar amb l'herència:

Les condicions prèvies no es poden reforçar en el subtipus. Les postcondicions no es poden debilitar en el subtipus. Els invariants no es poden debilitar en el subtipus. Restricció d'historial (la "regla d'historial"). Els objectes es consideren modificables només a través dels seus mètodes (encapsulació). Com que els subtipus poden introduir mètodes que no són presents al supertipus, la introducció d'aquests mètodes pot permetre canvis d'estat en el subtipus que no són permesos en el supertipus. La restricció d'historial ho prohibeix. Va ser l'element nou introduït per Liskov i Wing. Una violació d'aquesta restricció és, per exemple, definir un punt mutable com a subtipus d'un punt immutable. Això és una violació de la restricció d'historial, perquè en l'historial del punt immutable, l'estat sempre és el mateix després de la creació, de manera que no pot incloure l'historial d'un punt mutable en general. Tanmateix, els camps afegits al subtipus es poden modificar amb seguretat perquè no són observables a través dels mètodes del supertipus. Per tant, es pot definir un cercle amb centre immutable i radi mutable com a subtipus d'un punt immutable sense violar la restricció d'historial.

Orígens Les regles sobre les precondicions i postcondicions són idèntiques a les introduïdes per Bertrand Meyer al seu llibre de 1988 Object-Oriented Software Construction. Tant Meyer com més tard Pierre America, que va ser el primer a utilitzar el terme subtipatge comportamental, van donar definicions teòriques de demostració d'algunes nocions de subtipatge comportamental, però les seves definicions no van tenir en compte l'aliàsing que pot produir-se en llenguatges de programació que admeten referències o punters. Tenir en compte l'aliàsing va ser la principal millora feta per Liskov i Wing (1994), i un ingredient clau és la restricció històrica. Segons les definicions de Meyer i America, un punt mutable seria un subtipus comportamental d'un punt immutable, mentre que el principi de substitució de Liskov ho prohibeix.

Violació El principi de substitució de Liskov explica una propietat: "Si per a cada objecte o1 de tipus S hi ha un objecte o2 de tipus T tal que per a tots els programes P definits en termes de T, el comportament de P no canvia quan o1 se substitueix per o2, aleshores S és un subtipus de T".

Aquí teniu potser un exemple de violació de LSP:class Rectangle { public: void SetWidth(double w) { itsWidth = w; } void SetHeight(double h) { itsHeight = h; } double GetHeight() const { return itsHeight; } double GetWidth() const { return itsWidth; } double GetArea() const { return GetHeight() * GetWidth(); } private: double itsWidth; double itsHeight; };Des d'un punt de vista de la programació, la classe Square es pot implementar heretant de la classe Rectangle.public class Square : Rectangle { public: virtual void SetWidth(double w); virtual void SetHeight(double h); }; void Square::SetWidth(double w) { Rectangle::SetWidth(w); Rectangle::SetHeight(w); } void Square::SetHeight(double h) { Rectangle::SetHeight(h); Rectangle::SetWidth(h); }Tanmateix, això viola LSP tot i que la relació és-a es compleix entre Rectangle i Square.

Considerem l'exemple següent, on la funció g no funciona si es passa un quadrat, i per tant es podria considerar que s'ha violat el principi obert-tancat.void g(Rectangle& r) { r.SetWidth(5); r.SetHeight(4); assert(r.GetArea() == 20); // assertion will fail }Per contra, si es considera que el tipus d'una forma només hauria de ser una restricció sobre la relació de les seves dimensions, aleshores la suposició a g() que SetHeight canviarà l'alçada i l'àrea, però no l'amplada, no és vàlida. Aquesta suposició no és vàlida no només per als quadrats, sinó fins i tot potencialment per a altres rectangles que podrien estar codificats per preservar l'àrea o la relació d'aspecte quan l'alçada canvia.

Referències

Categoria:Programari Categoria:Informàtica teòrica