|
|
51CTO旗下网站
|
|
移动端
创建专栏

为什么Python不用设计模式?

在遥远的Python王国,有一位少年,非常热爱编程,他的父母想给他报一个班,问了万能的朋友圈以后,发现大家都推荐同一个老师,人称吉先生。

作者:刘欣|2019-03-11 08:36

在遥远的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了?”

  1. class Duck: 
  2.     def fly(self): 
  3.         print("Duck flying"
  4.  
  5. class Airplane: 
  6.     def fly(self): 
  7.         print("Airplane flying"
  8.  
  9.  
  10. def lift_off(entity): 
  11.     entity.fly() 
  12.  
  13.  
  14. duck = Duck() 
  15. plane = Airplane() 
  16.  
  17. lift_off(duck) 
  18. lift_off(plane) 

“看到没有, Duck和Airplane都没有实现你所谓的接口,但是都可以调用fly()方法,这难道不是面向接口编程, 如果你非要类比的话,这个fly就是一个自动化的接口啊。”

吉森确实没想到这一层,至于第二个原则,优先使用组合而不是继承,可以是每个面向对象的语言都可以实现的,他叹了口气,也就不问了。

Adapter模式

特使接着说:“Duck Typing非常强大,你不是提到了设计模式吗,在Duck Typing面前,很多设计模式纯属多此一举。我来给你举个例子,Adapter模式。假设客户端有这么一段代码,可以把一段日志写入文件当中。”

  1. def log(file,msg): 
  2.     file.write('[{}] - {}'.format(datetime.now(), msg)) 

“现在来了新的需求,要把日志写入数据库, 而数据库并没有write 方法,怎么办? 那就写个Adapter吧。”

  1. class DBAdapter: 
  2.     def __init__(self, db): 
  3.         self.db = db 
  4.  
  5.     def write(self, msg): 
  6.         self.db.insert(msg) 

“注意这个DBAdapter并不需要实现什么接口(我大Python也没有接口),就是一个单独的类,只需要有个write方法就可以了。”

  1. db_adapter = DBAdapter(db) 
  2. log(db_adapter, "sev1 error occurred"

确实是很简单,只要有write 方法, 不管你是任何对象,都可以进行调用, 典型的Duck Typing 。

既然Adapter可以这么写,那Proxy模式也是类似了,只要你的Proxy类和被代理的类的方法一样,那就可以被客户使用。

但是这种方法的弊端就是,不知道log方法的参数类型,想要重构可就难了。

单例模式

吉森又想到了一个问题,继续挑战特使:“Python连个private 关键字都没有,怎么隐藏一个类的构造函数,怎么去实现单例?”

特使不屑地说:“忘掉你那套Java思维吧,在Python中想写个singleton有很多办法,我给你展示一个比较Python的方式,用module的方式来实现。”

  1. #singleton.py 
  2.  
  3. class Singleton: 
  4.     def __init__(self): 
  5.         self.name = "i'm singleton" 
  6.  
  7. instance = Singleton() 
  8.  
  9. del Singleton  # 把构造函数删除 

使用Singleton:

  1. import singleton 
  2.  
  3. print(singleton.instance.name)  # i'm singleton 
  4.  
  5. instance = Singleton() # NameError: name 'Singleton' is not defined 

吉森确实没有想到这种写法,利用Python的module来实现信息的隐藏。

Visitor模式

不是每个设计模式都能这么干吧? 吉森心中暗想,他脑海中浮现了一个难于理解的模式:Visitor,自己当初为了学习它可是下了苦工。

吉森说:“那你说说,对于Visitor,怎么利用Python的特色?”

“我知道你心里想的是什么,无非就是想让我写一个类,然后在写个Visitor对它进行访问,是不是?”

  1. class TreeNode: 
  2.     def __init__(self, data): 
  3.         self.data = data 
  4.         self.left = None 
  5.         self.right = None 
  6.     def accept(self, visitor): 
  7.         if self.left is not None: 
  8.             self.left.accept(visitor) 
  9.  
  10.         visitor.visit(self) 
  11.  
  12.         if self.right is not None: 
  13.             self.right.accept(visitor) 
  14.  
  15. class PrintVisitor: 
  16.     def visit(self,node): 
  17.         print(node.data) 
  18.  
  19. root = TreeNode('1'
  20. root.left = TreeNode('2'
  21. root.right = TreeNode('3'
  22.  
  23. visitor = PrintVisitor() 
  24.  
  25. root.accept(visitor)   #输出2, 1, 3 

吉森说:“是啊, 难道Visitor模式不是这么写的吗? ”

"我就说你的Python只是学了点皮毛吧,Visitor的本质是在分离结构和操作, 在Python中使用generator可以更加优雅地实现。”

  1. class TreeNode: 
  2.  
  3.     def __iter__(self): 
  4.         return self.__generator() 
  5.  
  6.     def __generator(self): 
  7.         if self.left is not None: 
  8.             yield from iter(self.left)  
  9.         yield from self.data 
  10.  
  11.         if self.right is not None: 
  12.             yield from iter(self.right)  
  13.  
  14. root = TreeNode('1'
  15. root.left = TreeNode('2'
  16. root.right = TreeNode('3'
  17.  
  18. for ele in root: 
  19.     print(ele) 

不得不承认,这种方式使用起来更加简洁,同时达到了结构和操作进行分离的目的。

特使说道: “看到了吧,Python在语言层面对一些模式提供了支持,所以很多设计模式在我大Python看起来非常笨拙,我们这里并不提倡,当然我们还是要掌握面向对象设计的原则SOLID和设计模式的思想,发现变化并且封装变化,这样才能写出优雅的程序出来。”

吉森叹了一口气,感慨自己学艺不精,不再反抗,束手就擒。

尾声

Python王国审判了吉森,本来要判他死刑,但是Java帝国重兵压境,要求释放,否则就开战。

吉森被送回Java王国,成为了人们心目中的英雄,回家他仔细对比了Java和Python,在Java虚拟机上把Python语言给实现了!国王为了表彰他的英勇事迹,把这个语言叫做Jython。

【本文为51CTO专栏作者“刘欣”的原创稿件,转载请通过作者微信公众号coderising获取授权】

戳这里,看该作者更多好文

【编辑推荐】

  1. 从华为的遭遇看代码重构
  2. 当你在纠结学Python还是Java时,大二学生已经开始造编程语言了!
  3. 为什么Julia比Python快?因为天生理念就更先进啊
  4. 李鬼见李逵,用Python“死磕”翟天临的博士论文
  5. Java帝国对Python的渗透能成功吗?
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

澳门博彩官网