【IT168 技术】本文面向的是那些刚刚接触编程,可能已经听说过“面向对象编程“,“OOP”,”类“,”继承/封装/多态”,以及其他计算机科学术语的,但是仍然没有真正明白如何使用OOP的朋友。在本文中,我将解释为什么使用OOP和如何轻松编码。这篇文章使用Python 3代码,但其概念适用于任何编程语言。
有两个关键的非面向对象编程概念需要马上理解:
重复的代码是一件坏事。
代码永远都在改变。
除了一些单任务和只运行一次的微小的”用完即弃”的程序,你几乎总是需要为了解决bug或增加新功能而更新你的代码。大部分编写良好的软件是那种可读性高,易于修改的软件。
如果你经常在你的程序中复制/黏贴代码,那么当你修改它的时候,就需要在很多地方做出同样的改动。这是棘手的。如果在某些地方遗漏了修改,你将到处修改bug或者实现的新功能有不一致性。重复的代码是一件坏事。程序中重复的代码将会把你置于bug和头痛之中。
函数让你摆脱重复的代码。你只需要将代码写到函数中一次,就可以在程序中的任何需要运行代码的地方调用它就可以。更新函数的代码就可以自动更新调用函数的地方。正如函数使得更新代码变得容易,使用面向对象编程技术也会组织你的代码使它更容易改变。记住代码总是在改变的。
一个角色扮演游戏栗子
大多数OOP教程都是令人作恶的。它们有”汽车”类和”鸣笛”方法,其他一些例子与新手写的或者之前接触过的实际程序都是不相关的。因此本博文将使用一个RPG类视频游戏(回忆下魔兽,宠物小精灵,或龙与地下城的世界)。我们已经习惯于将游戏中的事物想象成一些整数与字符的集合。看看Diablo(暗黑破坏神)角色屏幕或D&D角色表单上的数字:
从这些RPG视频游戏中去除图片,角色,装甲,其他对象只是变量形式的一个整数或者字符值的集合。不使用面向对象概念,你可以在Python中这样实现这些事物:
1 2 3 4 5 6 | name = 'Elsa' health = 50 magicPoints = 80 inventory = { 'gold' : 40 , 'healing potion' : 2 , 'key' : 1 } print ( 'The hero %s has %s health.' % (name, health)) |
以上变量名都是非常通用的。为了在游戏中增加怪兽,你需要重命名玩家角色变量,并且增加一个怪兽角色:
1 2 3 4 5 6 7 8 9 10 | heroName = 'Elsa' heroHealth = 50 heroMagicPoints = 80 heroInventory = { 'gold' : 40 , 'healing potion' : 2 , 'key' : 1 } monsterName = 'Goblin' monsterHealth = 20 monsterMagicPoints = 0 monsterInventory = { 'gold' : 12 , 'dagger' : 1 } print ( 'The hero %s has %s health.' % (heroName, heroHealth)) |
当然你希望有更多的怪物,接着你会有类似monster1Name, monster2Name等等的变量。这不是一种好的编码方法,因此你可能会使用怪物变量列表:
1 2 3 4 | monsterName = [ 'Goblin' , 'Dragon' , 'Goblin' ] monsterHealth = [ 20 , 300 , 18 ] monsterMagicPoints = [ 0 , 200 , 0 ]
|
然后列表索引0处是第一个哥布林的状态,龙的状态在索引1处,另一个哥布林在索引2处。这样你可以在这些变量中存放很多怪物。
但是,这种代码容易导致错误。如果你的列表不同步,程序将无法正常工作。例如玩家击败了索引0处的哥布林,程序调用了vanquishMonster()函数。但这个函数有一个bug,它会(意外的)删除列表中的所有值除了monsterInventory:
1 2 3 4 5 6 7 | def vanquishMonster(monsterIndex): del monsterName[monsterIndex] del monsterHealth[monsterIndex] del monsterMagicPoints[monsterIndex] # Note there is no del for monsterInventory vanquishMonster( 0 ) |
怪兽列表最后看起来像这样:
1 2 3 4 | monsterName = [ 'Dragon' , 'Goblin' ] monsterHealth = [ 300 , 18 ] monsterMagicPoints = [ 200 , 0 ] monsterInventory = [{ 'gold' : 12 , 'dagger' : 1 }, { 'gold' : 890 , 'magic amulet' : 1 }, { 'gold' : 15 , 'dagger' : 1 }] |
现在龙的道具看起来跟之前哥布林的道具一样。第二个哥布林的道具是之前龙的道具。游戏迅速失控了。问题是你把一个怪物的数据散布在多个变量中。解决这个问题的方法是将一个怪物的数据放入一个字典里,然后使用一个字典列表:
1 2 3 | monsters = [{ 'name' : 'Goblin' , 'health' : 20 , 'magic points' : 0 , 'inventory' : { 'gold' : 12 , 'dagger' : 1 }}, { 'name' : 'Dragon' , 'health' : 300 , 'magic points' : 200 , 'inventory' : { 'gold' : 890 , 'magic amulet' : 1 }}, { 'name' : 'Goblin' , 'health' : 18 , 'magic points' : 0 , 'inventory' : { 'gold' : 15 , 'dagger' : 1 }}] |
啊哈!这段代码变得更加复杂了。例如,一个怪兽的状态是一个字典列表中的字典项。假如咒语或者道具栏也有它们自己的属性,且需要放到字典该怎么办?假如一个道具栏中的物品是一个背包,它本身包含了其他道具该怎么办?这个怪物列表会变得紧张。
这点是面向对象程序设计通过创建一个新的数据类型就可以解决的。