作者yauhh (哟)
看板Translate-CS
标题[翻译] Prototype-based OOP over class-based
时间Sat Mar 30 21:28:06 2013
原文网址:
http://programmers.stackexchange.com/questions/110936/#110939
============= 译文:以下译文使用 Markdown 语法撰写 =============
###关於物件导向程式设计,基於原型比较基於类别的方式,好在哪里?###
***Vivek Viswanathan 发问, Pierre 修改。***
*标签:设计、物件导向、无关於语言*
过去我都在做基於类型的物件导向程式设计,後来当我开始写JavaScript时,对为何都
是用基於原型的方式感到有疑问。
1. 假如可以,请问基於原型的物件导向设计结构上有什麽好处?譬如可以期待它在某些
时候执行速度比较快,或者有较不密集占用记忆体吗?
2. 从程式员的方面看,有什麽优点?譬如写什麽哪种程式比较简单,或者比较方便藉由
原型重新利用别人的程式?
请不要把这个问题看成特别是JavaScript的问题,因为关於JavaScript,过往有人讲
出很多有错的论点,完全跟原型无关。相对的,请由理论方面端视它,告知基於原型对
基於类别有什麽不同的好处。
感谢。
---
***mikera 回答:***
我有很多处理到这两种方式的经验,是用Java写RPG。本来我用了基於类别的物件导向写
完一个游戏,但最後觉得那是错的方式,当类别树长多了,程式就不好维护。於是,我把
全部程式修改为基於原型。结果好多了,很好管理。
如果你感到有兴趣,到这里下载源码 ([Tyrant - Java
Roguelike](
http://sourceforge.net/projects/tyrant/ "Tyrant - JavaRoguelike"))
。
主要的效益如下:
- **容易弄出新的类别** :只要把另一个类别当做原型复制一份来,改一些属性,
瞧,一个新的类别。我用这种方法来建立药水瓶,如例子,每个改 3 到 6 行 Java
。比开一个新的 class 档案并载入样版更好做。
- **可能藉由相对很少的程式来建置和修改大量的类别**:例如 Tyrant 有
3000 件不同的原型,但总共只有 42,000 行程式。以 Java 来说很惊人。
- **多重继承很简单**:只要从一个原型复制出一部份的属性,贴到另一个原型
中。以 RPG 为例,可能你想要「钢铁魔像」怪物要有些「钢」物件的特性,还
要有些「魔像」怪物的特性,还要有些「智力不足的怪物」的特性。用原型法比
较简单。否则你尝试类别继承看看......
- **可以藉由属性参数做聪明的事** :藉着在通用的「读取属性」方法中放一些
聪明的逻辑,可以做出各种参数。譬如要定义一只魔法戒指可以给穿戴者增加 2 点力
量,就这麽做。逻辑是要加在戒指物件中而不是「读取力量」方法,这样就会避免在
程式中放很多条件判断,来检查像「人物是否戴一只力量之戒」的例子。
- **实例可以做为别的实例的模版** :譬如,很容易「克隆」物件,只要把现有物
件当做新物件的原型。不必为每个类别分别写复杂的克隆逻辑。
- **容易在运作中改变行为** :譬如,在执行时可以相当任意改变属性,让物件
「变形」。允许你做到酷炫的游戏特效,而且这可以和「脚本语言」搭配,在运作时可
以做到几乎任何事情。
- **较适於用「函数风格」的编程** :你会发现自己在写大量的函数分析物件运作
得当,而不是把这种逻辑写死在某个类别的方法里头。我个人偏好这种函数语言程式风
格。
主要的缺点如下:
- **失去静态型别的保障** :因为你在有效地创造动态物件系统。这往往表示你要
写更多测试来确保物件的行为很正确,而因此物件是正确的「类别」。
- **额外耗损一些效能** :有些物件属性都一定要在一些对应表里头寻找,所以在
性能方面会付出一点代价。在我的情况中这是没问题,但可能在别人的情况中可能有
点问题,例如 3D 画面中有很多物件时,刷新画片是每一张画片都查询每个物件。
- **不能用同一个方式重构** :基於原型的系统基本上是你自己用程式制造继承树
。整合开发工具和重构工具没真的帮你忙,因为这些工具不懂你的做法。我从来没撞
到问题,但如果你自己不小心,维护就会失控。可能要自己用测试的程式检查你的继
承树架构正确。
- **长得有点怪** :惯用原有的物件导向程式设计风格的人很容易把脑子搞乱。「
谁写的,只有一个类别,类别名字叫做『东西』?」「这个 final Thing class 要
怎麽继承?」「你这根本不是 OOP !」「有这麽多 static 方法用在全部的物件上
干嘛?」
最後,实作的注意事项:
- 我用到 Java HashMap 来放属性,而且有个「根」指标指回到原型上。这运作得很
顺,但有以下缺点: ***a)*** 读取属性有时要回访资料结构,走过一大堆父节点,
让效能受损, ***b)*** 如果修改了父原型,会影响到没有覆写属性的子原型。一不
小心就会造成无谓的程式错误。
- 若再给我一次机会,我会用不可变的持续性对应表来维护属性,就像 Clojure 的持
续性对应表一样。或者用我自己做的 Java 持续性杂凑对应表来做。这样,可由简单
的复制修改和不可变的行为得到优势,那就不必让原型拉一个指标指向父原型。
- 在物件属性中放函数或方法,会让你得到乐趣。我用这种破格的方式,像「脚本」类别
的匿名的子型别,写起 Java 不太简洁。让我再做一次,我可能会拿较容易嵌入里头
的语言用来写脚本,例如用 Clojure 或 Groovy 。
***(回应)***
1. +1 跟这个很像
steve-yegge.blogspot.com/2008/10/universal-design-pattern.html
- jk。 09月27日'11 8:45
2. +1 分析得很好。虽然这套方法比较是因应 Java 模型。像 Delphi、 C#、VB.Net
都有明确的属性。- umlcat 09月27日'11 15:22
3. @ umlcat - 我认为 Java 模型相当类似 Delphi 和 C# 模型,除非是简便的定
义属性语法 - 你仍然应该要静态定义想要的属性。原型模式的重点在於定义不是静
态的,你不必预先做任何宣告。...... 在9月28日'11 2:21 -- mikera
---
***umlcat 回答:***
基於原型的物件导向程式设计的优点是物件和「类别」可以在执行时扩充。
基於类别的物件导向程式设计有一些不错的功能,但可惜的是,是由程式语言决定。
Object Pascal(DELPHI)、 VB.NET 和 C# 有个非常直接的方式可使用属性(不要
和栏位搞混)和属性的存取方法,而 Java 和 C++ 属性是由方法存取。PHP 把两种方
式混合,称做「神奇方法」。
即使主流的类别式物件导向语言是静态型别,仍然有些类别可以是动态型别。我认为类别
的物件导向的静态型别很有用,因为它允许称做是物件检查的功能,可以用来以可见的方
式快速实现整合开发工具及开发网页。
--
/yau
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 118.167.54.194
※ 编辑: yauhh 来自: 118.167.54.194 (03/30 21:32)
1F:推 Minilogo:无关语言,GOOD! 03/30 23:07
2F:推 swpoker:深有同感~我在设计web service也是有这样的问题 04/01 11:35
3F:推 stopcrying:推,最近才看了这篇文章 04/08 12:37