在遥远的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.py
- class Singleton:
- def __init__(self):
- self.name = "i'm singleton"
- instance = Singleton()
- del Singleton # 把构造函数删除
使用Singleton:
- import singleton
- print(singleton.instance.name) # i'm singleton
- instance = 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。
【本文为51CTO专栏作者“刘欣”的原创稿件,转载请通过作者微信公众号coderising获取授权】