为什么80%的码农都做不了架构师?>>>   

李氏代换(LSP)简介

李氏代换是软件设计的一个原则,又名依赖倒转原则或依赖倒置原则,其衍生原则有接口分离原则等。该原则由Barbara Liskov于1988年提出。

该原则指出,程序中高级别的元素应当独立于继承于它的低级别的元素独立存在,而程序中低级别的元素的设计和运行应当依赖于(继承)高级别元素的存在。换句话说,即高级的类、模块或接口应当不知道低级的类、模块或接口的存在,而低级的类、模块或接口的设计、实现以及运行应当继承高级的类、模块或接口,从而实现了“强内聚、低耦合”的软件设计要求。由此原则,可以衍生出以下一些规则(推论):

  1. 程序中低级别的元素应当能够完全用以替代高级别的元素。
  2. 设计接口时应当不考虑实体的具体行为,使得实体的具体行为实现可以建立在实现接口的基础上进行。
  3. 实体类的具体行为不应当依赖于与这些行为无关的接口。

规则2和3即为接口分离原则。

举个栗子

前不久金庸大侠和斯坦李大英雄都不幸逝世了,这两位大师的笔下叙述了很多的英雄形象(Hero)的故事,这个例子就与如何对小说影视等作品中出现的英雄人物编程有关。这些英雄人物各有不同的故事(IStory)、不同的超能力(IAbility),有的还有会飞(IFly)这种行为。可以设定为有超能力的英雄一定有故事,即用IAbility继承IStory。会飞的英雄一定有故事、并且有超能力,所以可以继承上述两个接口。

这些英雄形象一般在中国叫作武侠(Wuxia),而在美国叫做超人(SuperHero),因此可以将Wuxia和SuperHero根据李氏代换原则设置为Hero的子类,这些类应当实现IAbility接口。有的武侠会飞(FlyingWuxia),因此会飞的武侠可以继承武侠并扩展“会飞”的特性,而FlyingWuxia的实体在运行中也可以用于完全替代武侠类。类似地可以创建FlyingSuperHero类。这些会飞的武侠或者超人应当去实现IFly这一接口。

接口的实现

在程序设计中,接口名一般以大写字母“I”开头,其Java实现如下:

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, abstractmethodclass IStory:__metaclass__ = ABCMeta@abstractmethoddef showStory(person, whatHappened):print(person, " experienced ", whatHappened)class IAbility(IStory):__metaclass__ = ABCMeta@abstractmethoddef showAbility(person, ability):print(person, "has ability", ability)class IFly(IAbility, IStory):__metaclass__ = ABCMeta@abstractmethoddef take_off(thing):print(thing, "takes off!")@abstractmethoddef fly(thing):print(thing, "flying!")@abstractmethoddef landing(thing):print(thing, "landing!")

抽象类的实现

关于Hero这个类的实现,由于每个英雄人物都有特定的姓名、性别等特性,但每个英雄都有不同的故事,因此我们可以考虑设置英雄为一个抽象类并包含抽象方法“它的故事”。

“英雄”的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;}@Overridepublic void showAbility(String whatAbility) {System.out.println(this + " has ability " + whatAbility);}@Overridepublic String toString(){return name + " " + gender + " " + age + " " + nationality;}@Overridepublic abstract void showStory(String whatHappened);}

“英雄”的Python实现:

class Hero(IAbility):def __init__(self, name, gender, nationality, age=20):self.__name = nameself.__gender = genderself.__nationality = nationalityself.__age = agedef __str__(self):return (self.__name + " " +  self.__gender + " " +  str(self.__age) + " comes from " + self.__nationality)@abstractmethoddef 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);}@Overridepublic String toString(){return super.toString();}@Overridepublic 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);}@Overridepublic String toString(){return super.toString();}@Overridepublic void take_off(String tool) {System.out.println(this + " uses " + tool + " to take off!");}@Overridepublic void fly() {System.out.println(this + " is flying!");}@Overridepublic void landing() {System.out.println(this + " is landing!");}@Overridepublic 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);}@Overridepublic String toString(){return super.toString();}@Overridepublic void take_off(String tool) {System.out.println(this + " uses " + tool + " to take off!");}@Overridepublic void fly() {System.out.println(this + " is flying!");}@Overridepublic void landing() {System.out.println(this + " is landing!");}@Overridepublic 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虽然能够做到类型安全,也体现了很多的设计原则,但毕竟实现过程颇费周折。在“李氏代换”这个原则下,对于这两个编程语言可以分别有如下思考:

  1. 这两种OOP语言都能体现“低级别的元素应当能够完全用以替代高级别的元素”这一李氏代换衍生规则一。需要注意的区别是,Java的“向下转化(down-casting)”更是获得了低级别类的行为在需要时能够代替高级别类的行为的效果,但也因为开放了对高级别类行为的修改而违反了“开放-封闭”原则中“对修改封闭”的原则。Python则因为根本没有down-casting这一出而没有在此处违反“开放-封闭”原则。
//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下的运行效果:

  1. 两种编程语言都能够体现李氏代换推论2,但是Java由于强制要求类对于所实现的接口中所有方法必须进行实现,这难免违反了推论3的规则。因此很容易就导致代码冗长。从上述实现中,读者可以发现Python的代码远远要短于Java的代码。再具体一点,如果只需要关注作品中的部分情节,比如详细描写“起飞”而不需要过度关注怎么平飞和降落的情形时(比如,“徐长卿利于剑上,运用内功缓缓升起,顷刻便到了长安城。”的描写中就忽略了平飞和降落的细节),这时明显Python给出的实现方式更佳。
  2. 通过思考的结论2,很明显,如果在日常的编码实现中能够充分应用、活用“李氏代换”的原则,我们就能够大幅度减少代码重复性,从而实现码农早下班、不脱发的美好理想。

参考资料

  1. 程杰, 大话设计模式. 北京: 清华大学出版社, 2015.
  2. Y. D. Liang, Introduction to Java programming, Tenth edition. Boston: Pearson, 2015.
  3. M. Lutz, Learning Python, Fifth edition. Beijing: O’Reilly, 2013.
  4. C. Giridhar, “Learning Python Design Patterns - Second Edition,” p. 311.

特别鸣谢

金庸大侠和斯坦李大英雄带给我们的青春记忆,以及程杰老师开创的故事体叙述软件设计模式的先河。

转载于:https://my.oschina.net/Samyan/blog/2877509

浅谈“李氏代换”——从纪念金庸和斯坦李说起相关推荐

  1. 纪念金庸。创造了整整一个江湖。一个江湖的时代落幕了。。。

    纪念金庸.创造了整整一个江湖.一个江湖的时代落幕了... 金庸武侠小说中的绝美句子,至今都让人回味无穷-- 情不知所起,一往情深:恨不知所终,一笑而泯. --金庸<笑傲江湖> 红颜弹指老, ...

  2. 一篇流水账,纪念金庸大侠

    熟悉码农翻身公众号的同学知道,老刘是不太喜欢追热点写文章的,可今天看到金庸去世的消息,无数的旧事突然间涌上心头,真是不吐不快, 那就写一篇流水账,纪念下从小学开始,就一直陪伴我的金庸武侠. 我小学的时 ...

  3. 算法浅谈——递归算法与海盗分金问题

    本文始发于个人公众号:TechFlow 最近看到一道很有意思的问题,分享给大家. 还是老规矩,在我们聊算法问题之前,先来看一个故事. 传说中,有5个海盗组成了一支无敌的海盗舰队,他们在最后一次的寻宝当 ...

  4. 斯坦李去世、霍金去世、金庸去世、李咏去世,2018年有点悲壮!

    11-1310:01 就在漫威超级英雄电影<毒液>正在内地如火如荼的上映时,远在大洋彼岸传来了一个悲伤的消息.当地时间11月12日,斯坦李的个人官方推特账号发布消息,美国漫画界元老级人物, ...

  5. 学了计算机专业英语感悟,浅谈计算机专业英语的学习

    浅谈计算机专业英语的学习 江苏盐城技师学院 李书琴 [摘 要]职业教育是现代化教育的重要组成部分,其目标在于培养大批有理想.有道德.有文化.有纪律,具有一定知识与技 能的劳动者和各种实用人才.英语作为 ...

  6. 这可能是最非主流的斯坦·李纪念方式。论编程语言与超级英雄的联系

    若不是逝世时间挨得紧 你会把金庸和斯坦·李放想到一块么? 一个,写宇宙里的超级英雄 一个,写江湖中的武林高手 二人都是在给成年人写童话 安徒生伟大,便伟大在这里 金庸不只是打打杀杀 斯坦·李也不只是谁 ...

  7. 泡妞技术帖:一物降一物 金庸爷爷教你如何谈恋爱

    就金庸大侠的小说来讲,对于女性人物塑造是武侠小说里面最成功的(没有之一),甚至许多言情小说都比不上(比如琼瑶奶奶),不管是适用面还是真实性上来讲,基本可以涵盖你日常生活能接触到的人了.so,下面开讲. ...

  8. 作家金庸谈他皈依佛教的心路历程

    作家金庸谈他皈依佛教的心路历程 皈依佛教与生死问题 池田:适才我们谈过雅戈布列夫先生与佛教的话题.金庸先生也信奉佛教,且对佛学甚有造诣,先生皈依佛教,是缘起于什么事呢? 金庸:我之皈依佛教,并非由于接 ...

  9. 名人谈酒:李白、曹操、杜甫、金庸是这样评价酒的!

    酒,能忘忧. 酒能消忧解愁,忘却生死利禄及荣辱,让人游乎四海之外,能给我们带来自由的欢乐. 图 李煜说:醉乡路稳宜频到,此外不堪行. 曹操说:何以解忧,唯有杜康. 李白说:五花马,千金裘,呼儿将出换美 ...

最新文章

  1. Discuz!常用函数解析(续)
  2. 技术面试 vs 实际岗位 | 每日趣闻
  3. matplotlib 设置图形大小时 figsize 与 dpi 的关系
  4. 贝塞尔曲线 cubic-bezier()
  5. 去哪里学习python_Python从哪里开始学?怎么入门?
  6. Docker快速搭建docker-nfs-server服务器
  7. python判断某一天是周几
  8. 设计模式学习-每日一记(1.简单工厂模式)
  9. 如何在VMware软件上安装Red hat(红帽)Linux6.9操作系统
  10. Codeforces 402 and 403 (Div. 2 and 1)
  11. SpringCloud - 2. 服务注册 和 发现
  12. 改善C#公共程序类库质量的10种方法
  13. Elasticsearch 读时分词、写时分词
  14. 程序员必备简捷开发辅助工具总结
  15. 尘埃系列的服务器怎么样,棕色尘埃台服和谐了吗?棕色尘埃各服务器有何区别...
  16. 2014微软校园招聘笔试试题(英文)
  17. ue4 unreal4 json序列化工具 数据转成字符串等
  18. springboot+REST
  19. Keras : 训练minst数据集并加载模型对本地手写图片进行预测
  20. testin云测操作

热门文章

  1. SPSS数据分析流程经验总结
  2. 微信小程序 - 音乐列表点击播放 / 暂停音频,流畅切换音频(支持暂停音乐后,保留音乐 “进度“ 继续播放)最好用最详细的源码示例教程,适用于wechat列表点击后播放 mp3 wav 文件的需求
  3. 打车日记 - 谨慎的小哥哥
  4. 前端中的icon几种用法
  5. CSDN博客积分规则和获取积分方法
  6. 2020最新广告法_2020新广告法关于房地产
  7. 搜狗输入法4.2android,搜狗手机输入法增加适配系统 推Android专用版
  8. linux中文输入法 2017,ubuntu 16.04 下安装并切换搜狗中文输入法
  9. 台式机耳机或音响使用
  10. java双骰儿赌博_java 编写程序:投掷2个骰子,200次