学习面向对象思维
我目前正在用C ++编写一个小型的2D游戏引擎,但是现在我正在面对一个守护进程 – 我在devise一个实际上起作用的“类的系统”。 我脑海中有一个封锁,使我无法看到我应该在哪里上课,在哪里我不应该这样做。 我正在阅读一篇关于引擎devise的文章,并且打算使用“State”类来pipe理不同游戏条目的状态(我正在使用int)。 它还build议游戏的所有对象(不是io / video / sound等)都来自Renderable或NonRenderable类。 这很聪明。 我已经知道这是一个明智的做法 – 我的意思是,Java中的每个对象都是基类Object的权利? 聪明,我知道! 我怎么没有这样做呢? 我需要阅读什么才能真正进入这个思维模式?
另一个例子。 我正在参加这个夏季的Ruby课程(非常简单),我们应该devise一个露营地。 简单! 所以,露营是一个“地块”的集合,每个都有一个电量表来衡量客人消耗了多less电量。 我的devise是三个class级,一个是露营 – 反过来使用Guest和Plot类的数组。 我的老师build议我多用些class。 跆拳道(!)是我的第一个想法,在哪里,什么类? 在我看来,一切都是一stream的,直到我意识到,也许这个仪表应该是一个阶级呢? 现在这个规格在Plot类中是一个Integer。
我想学习如何为我的问题提出一个面向对象的解决scheme – 而不仅仅是如何使最明显的东西进入类!
提示/书籍/文章/博客?
我在CS的两年拼贴学位,已经作为一个爱好多年编程! 我只是被卡住了 – 而且阻止了我创build任何更大的软件!
我个人的经历是Bertrand Meyer 第二版“面向对象软件构build面向对象的软件构build” 。
这本书对我来说是非常宝贵的,现在仍然是我一般从OO编程和软件build设中学到的最多的书。
这里有一些优点:
- 在A部分:这些问题 ,对软件质量有很好的定义。
- 在B部分:面向对象的道路上 ,逻辑的,逐步的search面向对象的技术,让读者认为调查是在现场进行的,就好像还没有任何已知的结果。 你可能会从这个部分获得你正在寻找的心态。
- 在C部分:面向对象的技术 ,这本书的技术核心,你将使你的知识扎实,并学习非常有用的技术,devise合同,inheritance,通用性等。
- D部分:面向对象的方法论:很好的应用这个方法是一个更实用的devise方法,我也觉得非常有用。 请参阅示例如何查找可以在线 查找的类(22) 。
在这些部分之后,会出现更多高级主题,如并发(30)或数据库(31) 。
由于本书使用了埃菲尔语言(由作者devise),这将使你正确的思维,并教你思考。 将这些想法应用于其他或多或lessOO编程语言将会很容易。
面向对象
面向对象程序devise就是要求对象做一些事情:一个看似难以理解的正确应用的概念。
Goban
考虑一个2D游戏板,就像玩Go (叫做goban )一样。
首先考虑完成任务所需的行为。 这意味着列出对象的行为,而不是决定行为操纵的数据。 例如,一个基板可能有以下行为:
- 放置一块石头。
- 去掉一块石头。
- 清除所有的石头。
对于Go的电脑版来说,关注特定区域是很方便的:
- 标记交叉点(例如三angular形,数字,字母,圆形,方形)。
- 从标记的交叉点上去除标记。
- 删除所有的标记。
请注意, goban并不需要提供一种方法来为客户在特定路口提供参考。 相反,它可以回答有关其状态的问题。 例如,一个goban可能回答以下问题:
- 在给定的十字路口有黑石头吗?
- 在给定的十字路口有白石吗?
- 在给定的十字路口有没有标记?
知道游戏状态并不是Goban的责任:属于游戏 (有规则 )的实例。 在现实生活中,goban只是一个石头的舞台。
在这一点上,我们可以编写一个goban的接口,而不知道底层的实现是如何工作的。
public interface Goban { public void place( Stone stone, Point point ); public void removeStone( Point point ); public void removeStones(); public void place( Mark mark, Point point ); public void removeMark( Point point ); public void removeMarks(); public boolean hasWhiteStone( Point point ); public boolean hasBlackStone( Point point ); public boolean hasMark( Point point ); }
注意董事会是如何与规则和游戏完全分离的。 这使得goban可以重用于其他游戏(涉及石块和十字路口)。 goban可以从一个通用的接口inheritance(例如一个Board接口),但是这应该足以解释一种用对象来思考的方法。
封装
Goban接口的实现不公开其内部数据。 在这一点上,我可以要求你实现这个接口,编写unit testing,并在完成后向我发送编译后的类。
我不需要知道你使用了什么数据结构。 我可以用你的实现来玩(并描绘)一个Goban。 这是很多项目出错的关键。 许多项目编码如下:
public class Person { private HairColour hairColour = new HairColour( Colour.BROWN ); public Person() { } public HairColour getHairColour() { return hairColour; } public void setHairColour( HairColour hairColour ) { this.hairColour = hairColour; } }
这是无效的封装。 考虑一下鲍勃不喜欢把头发涂成粉红色的情况。 我们可以做到以下几点:
public class HairTrickster { public static void main( String args[] ) { Person bob = new Person(); HairColour hc = bob.getHairColour(); hc.dye( Colour.PINK ); } }
鲍勃现在的头发已经变成了粉红色,没有什么能阻止它。 有办法避免这种情况,但人们不这样做。 相反,封装被打破,导致僵硬,僵硬,错误缠身和不可维护的系统。
一种可能的方式来执行封装是通过返回HairColour
的克隆。 修改后的Person类现在很难将发色改成Pink。
public class Person { private HairColour hairColour = new HairColour( Colour.BROWN ); public Person() { } public HairColour getHairColour() { return hairColour.clone(); } public void setHairColour( HairColour hairColour ) { if( !hairColour.equals( Colour.PINK ) { this.hairColour = hairColour; } } }
鲍勃可以安然入睡,知道他不会为了粉色染料工作而醒来。
值得记住的是: OO本身并不是目的。 面向对象的一点是在产品的整个生命周期中,使代码的开发,特别是维护更容易。 注意“为OO OO”的思想。
首先面向对象的分析和devise
我喜欢Head First Books,因为它们很有趣。 他们有练习和谜题来抓你的头。 我读过这本书,发现它非常好。
本书涵盖:
- 使用面向对象的原则(封装和委托)
- 开放原则(OCP)
- 单一责任原则(SRP)
- devise模式,UML,用例等
我脑海中有一个封锁,使我无法看到我应该在哪里上课,在哪里我不应该这样做。
当涉及到它时,类就是将复杂系统分离成彼此交互的简单部分的一种方式。 尝试创build类,否则你会重复自己。
现在这个规格在Plot类中是一个Integer。
仪表是否需要成为class级? 把它变成一个class级的好处是什么? 这是你总是需要问自己的事情。
- 游戏引擎很难devise。 这种模糊定义的需求的分离是一个复杂的过程,请阅读: 关于游戏引擎的文章
- devise是迭代的,你会重构几次,不要感到惊讶。
Jeff Bay的“ ThoughtWorks Anthology ”一书中有一篇文章:“Object Calisthenics”,他提供了一套deviseOOP软件的规则:
- 每个方法只使用一个缩进级别
- 不要使用else关键字
- 包装所有的基元和string
- 每行只使用一个点
- 不要缩写
- 保持所有实体的小
- 不要使用超过两个实例variables的任何类
- 使用一stream的collections
- 不要使用任何getters / setters /属性
在第一眼看来,它可能看起来太严格,无法遵循所有这些规则。 请记住,即使试图编写一些代码,它们会使你更好的OOPdevise师。
请记住,从来没有一个解决scheme的问题。 把所有东西都变成一个类也不是解决scheme。 特别小的东西(如量表)很可能就像你的情节类中的一个int或float成员。
我的build议是练习是一个好老师。 只要继续努力,并继续阅读。 及时你会越来越stream利。
我可能从Craig Larman的“ 应用UML和模式:面向对象分析和devise以及迭代开发入门”中学到了大部分面向对象的软件开发。
在他的方法中,类是从用例中系统地推导出来的:
- 用例中的名词映射到类,
- 动词的方法和
- 形容词到成员variables。
当然,这对于问题域中的概念比GUI部件更好。 尽pipe如此,从描述/使用案例的写作开始,帮助我find了更好的抽象,而不是当我省略那一步。
对我来说OO没有“点击”,直到我读了一本关于devise模式的书。 如果你已经习惯了抽象类,接口等概念,那只是战斗的一半。
下一步是弄清楚为什么你应该比inheritance更喜欢合成,如何编码到一个接口,以及如何编写你的类,使它们解耦和封装好。 devise模式向您显示常见面向对象问题的解决scheme,并帮助您按照上述指导原则构build您的代码。
我不能推荐任何有关C ++的书,但是GOF书是devise模式(Java)的标准。 我更喜欢以某种特定语言来讨论devise模式的书籍,以便获得具体的代码示例。 Ruby中的devise模式非常好,就像PHP:对象,模式和实践一样 。
我觉得你的老师不会特别知道他在说什么。 “更多class级”本身就是无用的build议。
也许你会发现布鲁斯·埃克尔的思维模式很有用。 你可以从他的网站免费下载这本书(我只能发布一个链接作为新成员,所以只需点击链接,你就可以find它)。 虽然这本书是从2003年开始的,但也许本书中提出的思想将帮助你成长为程序员。
编写一个真正巨大的软件,在整个过程中,它越大,你需要的可扩展性越好,你需要更好的类devise,因此下次你会提前思考,并使你的课堂devise好在一开始的时候…
其中一个有助于进入面向对象思维的东西,以及前面提到的做法已经被列出了,就是用面向对象的原则来重写/改进现有的代码。
例如 :
一个。 在有很多if / else结构的情况下,你可能会想到有一个类层次来相应地分配分支代码,并使用多态性。
湾 任何使用像(Java中的instanceof)的操作符都会将编程指示为具体的types,您可以考虑如何删除instanceof检查。
C。 使用“德米特法则”作为指导,看看class级之间的耦合是否高
在某种程度上,“testing驱动开发”的实践也帮助了我,因为它迫使你从界面/行为的angular度去思考,而不是集中在如何编码问题的最佳解决scheme上。
一个简单的方法来提出一个合理的东西,可能应该是对象(因此,类):写下你的任务的问题描述,如:
在露营地有客人,每个客人都可以访问几个网点。 软件应该能够pipe理每个客人所消耗的电力,所以应该知道客人使用的电源sockets和每个sockets消耗的电力。
现在,创build一个所有名词的列表,以便在你的问题中涉及到什么类(=对象types):
- 露营地
- 客人
- 出口
- 功率
这不一定是一个确定的清单,但这是一个好的开始。
哈哈。 我记得那一点。 整个“这个事情怎么办?” 只要坚持下去,在某个点上它只是点击。 这真的就像一个灯泡正在进行。 有一刻它不是真的有意义,然后在一秒钟之后,你会在课堂上编码。
尝试下载一些您可能最终使用的开源工具,并阅读代码。 它会给你一些引用你的代码风格的东西。
Peter Coad和Ed Yourdon几年前就写了一本关于它的书。 虽然没有充满新的过度规范的方法论, 但本书为对象风格的思考提供了良好的基础。
在我看来,我读过的用于学习面向对象概念的最好的书之一是:
面向对象的思想过程
对于我来说,这本书确实让你以面向对象的方式思考(嗯,这是标题中的线索!)它是相当不相关的语言,包含VB.NET,C#和Java中的一些小代码示例,并经常引用许多面向对象分析和devise领域的“伟大”,如Grady Booch , Martin Fowler等。
既然本书帮助你以面向对象的方式进行思考 ,常常会举一个具体的例子来说明OO的接近问题的方式与程序化的方式的区别。 如果你来自更多或程序背景,这可以是一个很大的帮助。 它还涉及到UML等内容 ,帮助解释和理解完整的类库(如框架)背后的devise和类之间的交互,以及使用聚合和合成等概念devise丰富的类层次结构。