在遥远的 Python 王国, 有一位少年, 非常热爱编程, 他的父母想给他报一个班, 问了万能的朋友圈以后, 发现大家都推荐同一个老师, 人称吉先生.
于是他的父母毫不犹豫就交了一笔不菲的学费, 每周六日下午让孩子去学习.
少年学习非常刻苦, 很快就学会了 Python 语法, 工具和框架.
老师像是见到了可以雕刻的美玉, 倾囊相授, 告诉他不仅要把代码写对, 还要让代码漂亮, 优雅, 可读, 可维护.
少年又学会了单元测试, TDD, 重构, 努力让自己的代码达到老师所要求的标准.
他还把 "Python 之禅" 贴在了自己的墙上, 经常对照自己的代码, 从来都不敢违反.
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
......
三年以后, 少年以为自己成为了 Python 的大师, 直到有一天, 老师给他布置了一个大作业, 其实是个大项目, 业务非常复杂.
少年通宵达旦地编程, 可他悲惨地发现, 无论他怎么努力, 他的代码都是乱糟糟的, 没有美感, 他所写出的类, 模块混成了一团.
于是他只好去请教老师: "老师, 我的 Python 和 Flask 框架已经用得滚瓜烂熟了, 为什么完成不了这个项目呢?"
老师说:"孩子, 原来你只需要把框架的类给 import 进来, 稍微写点儿代码就行了, 现在你需要自己去设计类, 自己去做出抽象了!"
- "怎么设计呢?"
- "为师送你一本古书,《设计模式》 , 你回去好好看看吧."
少年如获至宝, 废寝忘食地去研究这本 20 多年前出的, 泛黄的古书, 还是用 C++ 描述的.
他看得云里雾里, 似乎明白, 又似乎不明白, 只好再去请教老师.
这一次, 老师给了他另外一本书, 《Head First 设计模式》
少年翻开一看, 这本书是用 Java 写的, 于是又一头扎到了 Java 语言当中.
这本书比较通俗易懂, 少年看得大呼过瘾.
终于, 他信心十足地用 Python 开始那个大项目了.
他用 Python 语言实现设计模式, 解决一些设计问题, 可是总觉得不对劲, 和 Java , C++ 相比, 感觉怪怪的.
另外他感觉到了动态语言的不爽之处, 每次想重构的时候, 总是不敢下手, 他把困惑给老师说了.
老师笑道:"我在 Java 王国的时候, 人们总是说'动态一时爽, 重构火葬场', 现在你体会到了吧!"
- "Java 就能避免这个问题吗?"
- "Java 是一门静态语言, 变量类型一旦确定就不能改变, 对重构的支持非常好, 你有没有兴趣去看看? 那里有很多的框架, 像 Spring,Spring Boot,MyBatis, Dubbo, Netty, 非常繁荣发达."
少年心生向往, 于是老师就给他写了个条子, 告诉他说到了 Java 王国, 找到 IO 大臣, 一切事情都会畅通无阻.
少年辞别老师, 奔向了 Java 帝国, 老师整了整衣冠, 望着东方 Java 帝国的方向, 庄严地拜了三拜:"五年了, IO 大人, 我没有辜负您的重托, 又忽悠了一个人去做 Java 了!"
原来这位老师就是吉森! IO 大臣派来传播 Java 文化和价值观的传教士, 入境后不幸被识破, 软禁在了 Python 王国.
吉森的故事请移步《Java 帝国对 Python 的渗透能成功吗?》
Python 没有接口?
Python 国王收到边关的奏报, 说是最近有不少年轻人奔向了 Java 王国, 不知道是不是国内政策有变, 导致人心浮动.
Python 国王震怒, 下令严查. 查来查去, 所有的线索都指向了一个人: 吉森.
这一天, Python 特使带着士兵来到了吉森的住所, 果然发现他又在忽悠年轻人了.
特使又气又笑:"你学了半吊子的 Python, 居然敢来蛊惑人心, 实在是可笑."
吉森看到自己的计谋已被识破, 依然很镇静:"大人误会了, 我教的就是正宗的面向对象的设计和设计模式啊, 这设计模式用 Python 实现起来很别扭, 我就推荐他们去学 Java 啊."
"胡说, Python 写设计模式怎么会很别扭? Java 由于语法所限, 表达能力比较弱, 对于一些问题, 只好用笨拙的设计模式来解决, 我们 Python 有可能在语法层面就解决问题了!"
"那你说说, 设计模式的原则是什么?" 吉森问道.
"1. 面向接口编程, 而不是面向实现编程. 2. 优先使用组合而不是继承." 这是难不住特使的.
"Python 连接口都没有, 怎么面向接口编程?" 吉森问道.
特使哈哈大笑:"说你是半吊子吧, 你还不服, 你以为这里的接口就是你们 Java 的 interface 啊! 你忘了 Python 的 Duck Typing 了?"
- class Duck: def fly(self): print("Duck flying")class Airplane: def fly(self): print("Airplane flying")def lift_off(entity): entity.fly()duck = Duck()plane = Airplane()lift_off(duck)lift_off(plane)
- "看到没有, Duck 和 Airplane 都没有实现你所谓的接口, 但是都可以调用 fly() 方法, 这难道不是面向接口编程, 如果你非要类比的话, 这个 fly 就是一个自动化的接口 啊."
吉森确实没想到这一层, 至于第二个原则, 优先使用组合而不是继承, 可以是每个面向对象的语言都可以实现的, 他叹了口气, 也就不问了.
Adapter 模式
特使接着说:"Duck Typing 非常强大, 你不是提到了设计模式吗, 在 Duck Typing 面前, 很多设计模式纯属多此一举. 我来给你举个例子, Adapter 模式. 假设客户端有这么一段代码, 可以把一段日志写入文件当中."
- def log(file,msg): file.write('[{}] - {}'.format(datetime.now(), msg))
- "现在来了新的需求, 要把日志写入数据库, 而数据库并没有 write 方法, 怎么办? 那就写个 Adapter 吧."
- class DBAdapter: def __init__(self, db): self.db = db def write(self, msg): self.db.insert(msg)
- "注意这个 DBAdapter 并不需要实现什么接口 (我大 Python 也没有接口), 就是一个单独的类, 只需要有个 write 方法就可以了."
- db_adapter = DBAdapter(db)log(db_adapter, "sev1 error occurred")
确实是很简单, 只要有 write 方法, 不管你是任何对象, 都可以进行调用, 典型的 Duck Typing .
既然 Adapter 可以这么写, 那 Proxy 模式也是类似了, 只要你的 Proxy 类和被代理的类的方法一样, 那就可以被客户使用.
但是这种方法的弊端就是, 不知道 log 方法的参数类型, 想要重构可就难了.
单例模式
吉森又想到了一个问题, 继续挑战特使:"Python 连个 private 关键字都没有, 怎么隐藏一个类的构造函数, 怎么去实现单例?"
特使不屑地说:"忘掉你那套 Java 思维吧, 在 Python 中想写个 singleton 有很多办法, 我给你展示一个比较 Python 的方式, 用 module 的方式来实现."
#singleton.pyclass Singleton: def __init__(self): self.name = "i'm singleton"instance = Singleton()del Singleton # 把构造函数删除
使用 Singleton:
import singletonprint(singleton.instance.name) # i'm singletoninstance = Singleton() # NameError: name'Singleton' is not defined
吉森确实没有想到这种写法, 利用 Python 的 module 来实现信息的隐藏.
Visitor 模式
不是每个设计模式都能这么干吧? 吉森心中暗想, 他脑海中浮现了一个难于理解的模式: Visitor, 自己当初为了学习它可是下了苦工.
吉森说:"那你说说, 对于 Visitor, 怎么利用 Python 的特色?"
- "我知道你心里想的是什么, 无非就是想让我写一个类, 然后在写个 Visitor 对它进行访问, 是不是?"
- class TreeNode: def __init__(self, data): self.data = data self.left = None self.right = None def accept(self, visitor): if self.left is not None: self.left.accept(visitor) visitor.visit(self) if self.right is not None: self.right.accept(visitor)class PrintVisitor: def visit(self,node): print(node.data)root = TreeNode('1')root.left = TreeNode('2')root.right = TreeNode('3')visitor = PrintVisitor()root.accept(visitor) #输出 2, 1, 3
吉森说:"是啊, 难道 Visitor 模式不是这么写的吗?"
- "我就说你的 Python 只是学了点皮毛吧, Visitor 的本质是在分离结构和操作, 在 Python 中使用 generator 可以更加优雅地实现."
- class TreeNode: def __iter__(self): return self.__generator() def __generator(self): if self.left is not None: yield from iter(self.left) yield from self.data if self.right is not None: yield from iter(self.right) root = TreeNode('1')root.left = TreeNode('2')root.right = TreeNode('3')for ele in root: print(ele)
不得不承认, 这种方式使用起来更加简洁, 同时达到了结构和操作进行分离的目的.
特使说道: "看到了吧, Python 在语言层面对一些模式提供了支持, 所以很多设计模式在我大 Python 看起来非常笨拙, 我们这里并不提倡, 当然我们还是要掌握面向对象设计的原则 SOLID 和设计模式的思想, 发现变化并且封装变化, 这样才能写出优雅的程序出来."
吉森叹了一口气, 感慨自己学艺不精, 不再反抗, 束手就擒.
尾声
Python 王国审判了吉森, 本来要判他死刑, 但是 Java 帝国重兵压境, 要求释放, 否则就开战.
吉森被送回 Java 王国, 成为了人们心目中的英雄, 回家他仔细对比了 Java 和 Python, 在 Java 虚拟机上把 Python 语言给实现了! 国王为了表彰他的英勇事迹, 把这个语言叫做 Jython.
ps. 本文故事受到了这个视频的启发: https://www.youtube.com/watch?v=G5OeYHCJuv0
能认真看到这里, 说明是码农翻身的铁杆支持者, 送出 5 本《Head First 设计模式》, 签名版 + 码农翻身专属印章 (再次感谢读者 @Aries 的辛苦雕刻).
书只有五本, 只有靠手气了, 在公众号回复消息 "设计模式抽奖", 即可进行抽奖.
来源: https://juejin.im/entry/5c887a416fb9a049f1550d81