val rect : Rectangle = new Square(5) // thinks it's a Rectangle, but is a Squareval rect2 : Rectangle = rect.setWidth(10) // height is 10, LSP violation
class Rectangle( val width : Int, val height : Int ){def setWidth( w : Int ) = new Rectangle(w, height)def setHeight( h : Int ) = new Rectangle(width, h)}
class Square( val side : Int ) extends Rectangle(side, side){override def setWidth( s : Int ) = new Square(s)override def setHeight( s : Int ) = new Square(s)}
这是因为如果调用者认为它有一个类型T,认为它正在调用T的方法,那么它提供类型Ti的参数并将输出分配给类型To。当它实际调用S的相应方法时,然后每个Ti输入参数被分配给Si输入参数,So输出被分配给类型To。因此,如果Si不是逆变的w. r. t.到Ti,那么子类型T1-不会是Si的子类型-可以分配给Ti。
public class Bird{}public class FlyingBirds extends Bird{public void fly(){}}public class Duck extends FlyingBirds{}public class Ostrich extends Bird{}
class ItemsRepository{/*** @return int Returns number of deleted rows*/public function delete(){// perform a delete query$numberOfDeletedRows = 10;
return $numberOfDeletedRows;}}
还有一个扩展它的子类:
class BadlyExtendedItemsRepository extends ItemsRepository{/*** @return void Was suppose to return an INT like parent, but did not, breaks LSP*/public function delete(){// perform a delete query$numberOfDeletedRows = 10;
// we broke the behaviour of the parent classreturn;}}
然后,您可以让客户使用Base ItemsRepositoryAPI并依赖它。
/*** Class ItemsService is a client for public ItemsRepository "API" (the public delete method).** Technically, I am able to pass into a constructor a sub-class of the ItemsRepository* but if the sub-class won't abide the base class API, the client will get broken.*/class ItemsService{/*** @var ItemsRepository*/private $itemsRepository;
/*** @param ItemsRepository $itemsRepository*/public function __construct(ItemsRepository $itemsRepository){$this->itemsRepository = $itemsRepository;}
/*** !!! Notice how this is suppose to return an int. My clients expect it based on the* ItemsRepository API in the constructor !!!** @return int*/public function delete(){return $this->itemsRepository->delete();}}
当取代父母类带有子类破坏了API的契约时,LSP被破坏。
class ItemsController{/*** Valid delete action when using the base class.*/public function validDeleteAction(){$itemsService = new ItemsService(new ItemsRepository());$numberOfDeletedItems = $itemsService->delete();
// $numberOfDeletedItems is an INT :)}
/*** Invalid delete action when using a subclass.*/public function brokenDeleteAction(){$itemsService = new ItemsService(new BadlyExtendedItemsRepository());$numberOfDeletedItems = $itemsService->delete();
// $numberOfDeletedItems is a NULL :(}}
<?php
interface Database{public function selectQuery(string $sql): array;}
class SQLiteDatabase implements Database{public function selectQuery(string $sql): array{// sqlite specific code
return $result;}}
class MySQLDatabase implements Database{public function selectQuery(string $sql): array{// mysql specific code
return $result;}}
这种设计符合LSP,因为无论我们选择使用哪种实现,行为都保持不变。
是的,您可以在此配置中违反LSP,进行一个简单的更改,如下所示:
<?php
interface Database{public function selectQuery(string $sql): array;}
class SQLiteDatabase implements Database{public function selectQuery(string $sql): array{// sqlite specific code
return $result;}}
class MySQLDatabase implements Database{public function selectQuery(string $sql): array{// mysql specific code
return ['result' => $result]; // This violates LSP !}}
// Violation of Likov's Substitution Principleclass Rectangle {protected int m_width;protected int m_height;
public void setWidth(int width) {m_width = width;}
public void setHeight(int height) {m_height = height;}
public int getWidth() {return m_width;}
public int getHeight() {return m_height;}
public int getArea() {return m_width * m_height;}}
class Square extends Rectangle {public void setWidth(int width) {m_width = width;m_height = width;}
public void setHeight(int height) {m_width = height;m_height = height;}
}
class LspTest {private static Rectangle getNewRectangle() {// it can be an object returned by some factory ...return new Square();}
public static void main(String args[]) {Rectangle r = LspTest.getNewRectangle();
r.setWidth(5);r.setHeight(10);// user knows that r it's a rectangle.// It assumes that he's able to set the width and height as for the base// class
System.out.println(r.getArea());// now he's surprised to see that the area is 100 instead of 50.}}
interface Account{/*** Withdraw $money amount from this account if its balance is enough.* Otherwise do nothing.** @param Money $money* @return mixed*/public function withdraw(Money $money);}
public interface CustomerLayout{
public void render();}
public FreeCustomer implements CustomerLayout {...@Overridepublic void render(){//code}}
public PremiumCustomer implements CustomerLayout{...@Overridepublic void render(){if(!hasSeenAd)return; //it isn`t rendered in this case//code}}
public void renderView(CustomerLayout layout){layout.render();}
通过LSP修复:
public interface CustomerLayout{public void render();}
public FreeCustomer implements CustomerLayout {...@Overridepublic void render(){//code}}
public PremiumCustomer implements CustomerLayout{...@Overridepublic void render(){if(!hasSeenAd)showAd();//it has a specific behavior based on its requirement//code}}
public void renderView(CustomerLayout layout){layout.render();}
public class Rectangle{private double width;
private double height;
public double Width{get{return width;}set{width = value;}}
public double Height{get{return height;}set{height = value;}}}
class A {func foo(a: C2) -> C2 {return C2()}}
class B: A {override func foo(a: C1) -> C3 {return C3()}}
Java
class A {public C2 foo(C2 a) {return new C2();}}
class B extends A {@Overridepublic C3 foo(C2 a) { //You are available pass only C2 as parameterreturn new C3();}}
public class Plane{}public class RealPlane{
public void startEngine(){}
}public class FighterJet extends RealPlane{}public class PaperPlane extends Plane{}
class Circle {private int radius;
public Circle(int radius) {if (radius < 0) {throw new RuntimeException("Radius should be >= 0");}this.radius = radius;}
public int getRadius() {return this.radius;}}
半径不允许为负。下面是一个suclass:
class ColoredCircle extends Circle {private Color color; // defined elsewhere
public ColoredCircle(int radius, Color color) {super(radius);this.color = color;}
public Color getColor() {return this.color;}}
class Square extends Circle {private int sideSize;
public Square(int sideSize) {super(0);this.sideSize = sideSize;}
@Overridepublic int getRadius() {return -1; // I'm a square, I don't care}
public int getSideSize() {return this.sideSize;}}
违反LSP
现在,看看这个程序:
public class Liskov {public static void program(Circle c) {System.out.println("The radius is "+c.getRadius());}
我们用Circle对象和Square对象测试程序。
public static void main(String [] args){Liskov.program(new Circle(2)); // prints "The radius is 2"Liskov.program(new Square(2)); // prints "The radius is -1"}}