李氏代換是軟件設計的一個原則,又名依賴倒轉原則或依賴倒置原則,其衍生原則有接口分離原則等。該原則由Barbara Liskov於1988年提出。java
該原則指出,程序中高級別的元素應當獨立於繼承於它的低級別的元素獨立存在,而程序中低級別的元素的設計和運行應當依賴於(繼承)高級別元素的存在。換句話說,即高級的類、模塊或接口應當不知道低級的類、模塊或接口的存在,而低級的類、模塊或接口的設計、實現以及運行應當繼承高級的類、模塊或接口,從而實現了「強內聚、低耦合」的軟件設計要求。由此原則,能夠衍生出如下一些規則(推論):python
規則2和3即爲接口分離原則。編程
前不久金庸大俠和斯坦李大英雄都不幸逝世了,這兩位大師的筆下敘述了不少的英雄形象(Hero)的故事,這個例子就與如何對小說影視等做品中出現的英雄人物編程有關。這些英雄人物各有不一樣的故事(IStory)、不一樣的超能力(IAbility),有的還有會飛(IFly)這種行爲。能夠設定爲有超能力的英雄必定有故事,即用IAbility繼承IStory。會飛的英雄必定有故事、而且有超能力,因此能夠繼承上述兩個接口。設計模式
這些英雄形象通常在中國叫做武俠(Wuxia),而在美國叫作超人(SuperHero),所以能夠將Wuxia和SuperHero根據李氏代換原則設置爲Hero的子類,這些類應當實現IAbility接口。有的武俠會飛(FlyingWuxia),所以會飛的武俠能夠繼承武俠並擴展「會飛」的特性,而FlyingWuxia的實體在運行中也能夠用於徹底替代武俠類。相似地能夠建立FlyingSuperHero類。這些會飛的武俠或者超人應當去實現IFly這一接口。安全
在程序設計中,接口名通常以大寫字母「I」開頭,其Java實現以下:app
public interface IStory{ public void showStory(String whatHappened); } public interface IAbility extends IStory { public void showAbility(String whatAbility); } public interface IFly extends IAbility, IStory{ public void take_off(String tool); public void fly(); public void landing(); }
Java中接口能夠多繼承,可是類不能夠。Python是個更強調具體實現的語言,因爲(原則上)全部的類都是可實例化、可多繼承的對象,所以沒有了接口這一說。可是咱們仍然可使用abc模塊的ABCMeta、abstractmethod這兩個子模塊進行接口與抽象類(設計上)的實現。標註abstractmethod的目的僅僅是爲了提醒後續的類進行實現(固然與Java不一樣,python中對於這些接口或抽象類的「抽象方法」不做實現也不會影響運行)編程語言
from abc import ABCMeta, abstractmethod class IStory: __metaclass__ = ABCMeta @abstractmethod def showStory(person, whatHappened): print(person, " experienced ", whatHappened) class IAbility(IStory): __metaclass__ = ABCMeta @abstractmethod def showAbility(person, ability): print(person, "has ability", ability) class IFly(IAbility, IStory): __metaclass__ = ABCMeta @abstractmethod def take_off(thing): print(thing, "takes off!") @abstractmethod def fly(thing): print(thing, "flying!") @abstractmethod def landing(thing): print(thing, "landing!")
關於Hero這個類的實現,因爲每一個英雄人物都有特定的姓名、性別等特性,但每一個英雄都有不一樣的故事,所以咱們能夠考慮設置英雄爲一個抽象類幷包含抽象方法「它的故事」。ide
「英雄」的Java實現:函數
public abstract class Hero implements IAbility { private String name; private char gender; private String nationality; private int age; public Hero(String name, char gender, String nationality) { this(name, gender, nationality, 20); } public Hero(String name, char gender, String nationality, int age) { this.name = name; this.gender = gender; this.nationality = nationality; this.age = age; } @Override public void showAbility(String whatAbility) { System.out.println(this + " has ability " + whatAbility); } @Override public String toString(){ return name + " " + gender + " " + age + " " + nationality; } @Override public abstract void showStory(String whatHappened); }
「英雄」的Python實現:工具
class Hero(IAbility): def __init__(self, name, gender, nationality, age=20): self.__name = name self.__gender = gender self.__nationality = nationality self.__age = age def __str__(self): return (self.__name + " " + self.__gender + " " + str(self.__age) + " comes from " + self.__nationality) @abstractmethod def showStory(self, whatHappened): print(self, " experienced ", whatHappened)
其餘的「武俠」、「超人」等實現,繼承「英雄」類並實現「超能力」接口便可,會飛的英雄人物須要實現「飛行」接口。這都比較簡單,此處就直接上代碼出招,不作贅述。
「武俠」類的Java實現:
public class Wuxia extends Hero{ public Wuxia(String name, char gender) { super(name, gender, "中國", 20); } public Wuxia(String name, char gender, int age) { super(name, gender, "中國", age); } @Override public String toString(){ return super.toString(); } @Override public void showStory(String whatHappened) { System.out.println(this + " experienced " + whatHappened); } }
「武俠」類的Python實現:
class Wuxia(Hero): def __init__(self, name, gender, age=20): Hero.__init__(self, name, gender, "中國", age)
「飛行武俠」類的Java和Python實現:
public class FlyingWuxia extends Wuxia implements IFly{ public FlyingWuxia(String name, char gender) { super(name, gender); } public FlyingWuxia(String name, char gender, int age) { super(name, gender, age); } @Override public String toString(){ return super.toString(); } @Override public void take_off(String tool) { System.out.println(this + " uses " + tool + " to take off!"); } @Override public void fly() { System.out.println(this + " is flying!"); } @Override public void landing() { System.out.println(this + " is landing!"); } @Override public void showStory(String whatHappened) { System.out.println(this + " experienced " + whatHappened); } }
class FlyingWuxia(Wuxia, IFly): def __init__(self, name, gender, age=20): Wuxia.__init__(self, name, gender, age) def take_off(self, tool): print(self, "uses ability", tool, "to take off!")
「飛行超人」的Java和Python實現:
public class FlyingSuperHero extends Hero implements IFly{ public FlyingSuperHero(String name, char gender) { super(name, gender, "U.S.A"); } public FlyingSuperHero(String name, char gender,int age) { super(name, gender, "U.S.A", age); } @Override public String toString(){ return super.toString(); } @Override public void take_off(String tool) { System.out.println(this + " uses " + tool + " to take off!"); } @Override public void fly() { System.out.println(this + " is flying!"); } @Override public void landing() { System.out.println(this + " is landing!"); } @Override public void showStory(String whatHappened) { System.out.println(this + " experienced " + whatHappened); } }
class FlyingSuperHero(Hero, IFly): def __init__(self, name, gender, age=20): Hero.__init__(self, name, gender, "U.S.A", age) def take_off(self, tool): print(self, "uses tool", tool, "to take off!")
Java和Python都是面向對象的編程語言,關於兩者在面向對象方面的優劣,江湖上各派也各有各的觀點。好比,Python雖然實現簡單,可是它的封裝特性有很大的問題,常常成爲各路黑客們行走江湖、「行俠仗義」或「替天行道」的工具;而Java雖然可以作到類型安全,也體現了不少的設計原則,但畢竟實現過程頗費周折。在「李氏代換」這個原則下,對於這兩個編程語言能夠分別有以下思考:
//Java main函數中調用英雄示例: public static void main(String[] args) { Wuxia duanyu = new Wuxia("段譽", 'M', 19); duanyu.showAbility("六脈神劍"); demoStory(duanyu, "趕走慕容復"); System.out.println(""); //Python中這下一行是不能夠的 Wuxia changqing = new FlyingWuxia("徐長卿", 'M', 26); changqing.showAbility("蜀山劍法"); demoTakeOff((FlyingWuxia)changqing, "御劍"); demoStory(changqing, "當了蜀山長老"); System.out.println(""); Hero captain = new FlyingSuperHero("Steven", 'M', 98); captain.showAbility("Flying"); //在Python中這也是不能夠的 demoTakeOff((FlyingSuperHero)captain, "aegis"); System.out.println(""); } public static void demoTakeOff(IFly fly, String tool){ fly.take_off(tool); } public static void demoStory(IStory story, String whatHappened){ story.showStory(whatHappened); } # Python調用英雄人物示例: # If you did this: # changqing = Wuxia("徐長卿", 'M', 26) # you cannot do: # FlyingWuxia(changqing).take_off("御劍") in Python changqing = FlyingWuxia("徐長卿", 'M', 26) IAbility.showAbility(changqing, "蜀山劍法") IStory.showStory(changqing, "當了蜀山長老") changqing.take_off("御劍") print() captain = FlyingSuperHero("Steven", 'M', 98) captain.take_off("aegis") print() boeing757 = Airplane("Boeing 757") boeing757.take_off() boeing757.landing()
如下分別是java netbeans和python spyder下的運行效果:
金庸大俠和斯坦李大英雄帶給咱們的青春記憶,以及程傑老師開創的故事體敘述軟件設計模式的先河。