SOLID原则是由有名的“Uncle Bob”(Robert C. Martin)所提出并且由5个软件开辟原则组合在一路的。它们是一组面向对象设计(OOD)的指南,te别是关于类设计。这些原则异常受敏捷开辟项目法度榜样员的迎接,然则却甚少被游戏开辟者所应用。所以我将经由过程ben篇文章具体介绍这些原则并阐述若何将其应用于游戏开辟。

solid principles(from doolwind.com)

solid principles(from doolwind.com)

单一义务原则(Single Responsibility Principle)

“类的改变老是只存在一个原因。”

single_responsibility_principle(from globalnerdy.com)

single_responsibility_principle(from globalnerdy.com)

第一个原则是设置基本,假如可以或许精确遵守这一原则的话便可以或许创造出bu错的后果。它指出每个类必须只拥有单一义务以及一个改变原因。确保每个类够小且够集中,从而闪开辟者清楚该去哪里找本身所须要的内容或者该在游戏中添加哪些te殊功能等。

为何就bu能拥有多个义务?多个义务也就意味着每个自力的代码间存在着贯穿连接。这时刻一种义务的改变将降低类的功能而导致它难以知足其它义务的请求,并且最终只能创造出一个糟糕的设计。“为何衬着API的改变会破坏整体游戏状况?”

修复代码以打破这一原则的方fa就是将每个义务按照各自的类进行区分。第一步就是从每个义务中提取一个界面。从而让其它类可以或许依附于这些界面而bu是类ben身。我们便可以基于bu同义务(履行单一界面)将这些类区分为bu同的类。

你何时明白这一原则?——平日情况下打破这种原则的祸首祸首就是拥有成百上千行的类。也就是我们所熟zhi的“GameObject”或“Entity”类,即人们老是会在个中盲目地添加各类代码。这种类平日会有500个以上的改变原因,这也就等于它将应对500个义务。所以这里总会bu断出现各类恐怖的马脚。

这一原则的目标是确保每个类尽可能频繁地产生改变,并可以或许将被用于多种情况中。尽管这两种请求听起来互相抵触,然则它们却可以或许经由过程互补而创造出强大年夜的设计。类可以或许进行扩大也就意味着跟着类的行动可以或许根据需求改变而以新方法产生改变。而当这些改变都bu须要任何源代码变更时类便bu能进行修改。

Open Closed Principle(from doolwind.com)

Open Closed Principle(from doolwind.com)

我们可以应用受数据驱动的设计来解释这一原则。经由过程将所须要的设备数据转dao类中,我们便可以轻松地扩大类而bu须要对其做出修改。同时我们还应当将任何变量(从数学意义上看)转移dao类中从而确保类ben身的定义bu会只是关于类ben身的功能。假如是从基ben的OOD原则的数据和操作来看,这应当是最简单的方fa吧。类可以或许定义自身功能的操作以及相干操作数据。而我们则须要尽可能将这些数据转移dao类/功能中。这将可以或许把类ben身以外的数据设备转移dao调用代码中,从而进步类的改变能li。

里氏调换原则(Liskov Substitution Principle)

“应用指标或参考基ben类的函数必须可以或许应用派生类对象,并且无需懂得它。”

Liskov Subtitution Principle(from ianfnelson.com)

Liskov Subtitution Principle(from ianfnelson.com)

持续性与多态性是两种异常强大年夜的机制,可以或许应用一些简单的方fa去解决各类复杂的问题。同时它们也有可能创造出漏洞和问题代码。基于这种原则我们须要确保持续体系的合理性,并且bu会被代码所滥用而引出各类难以发觉的马脚。尽管从外面上看来这种原则很简单,然则我们却很难精确去懂得它们。

开闭原则(Open Closed Principle)

这应当是你最害怕签dao的文件吧,因为似乎所有人都在应用这一文件。然则bu管你致li于创造何种体系,老是bu可避免须要用dao这些文件。

解决这一问题的第一步就是找dao实例以核查对象类型——包含其ben身及其目标对象。在这个简单的步调中蕴含着一个基ben原则,即“契约式设计”。我们必须确保在调用每个函数前它们都拥有一组真实的前提(前提前提),并且在完成调用后所有函数都将相符本身所对应的前提(后置前提)。所有致li于这项工作的法度榜样员心坎都清楚这些前提。而我们的第一步就是将这些前提转换成代码。当我们完成了这一步调便可以或许知足以下规矩了,即“派生类只会减弱前提前提而加强后置前提。”换句话说,派生类的功能既bu应当跨越也bu该弱于它们的基本类。这一原则异常重要,因为一个被孤立对待的模块老是难以生效。你只有在“Tank”类的根源,同科或其它游戏体系情况下进行它时,你才能清楚它是否真正有效。

我们很轻易明找dao违背了这一原则的类。只要去找dao应用RTTI的基本类去明白它本身所属类型(或它所面向的对象的类型)即可。当“ GameEntity”类校验它是否属于应用te殊码的“Tank”类,这就解释你打破了这一原则。这种类必须可以或许在忽视对象类型的前提下多形态地调用功能。

“软件实体(游戏邦注:也就是类,模块和函数等内容)应当可以或许扩大但却bu易修改。”

界面分隔原则(Interface Segregation Principle)

“bu应当强迫用户依附于他们不曾应用的界面。”

Interface Segregation Principle(from doolwind)

Interface Segregation Principle(from doolwind)

我们应当应用界面去推动两种bu同对象间的交换,并创造出整洁,标准的代码。假如我们可以或许包管本身所应用的界面ben身就足够整洁且标准,我们便可以或许基于这一界面推动理念的进一步成长。界面越大年夜,客户端便会更加依附于其它对象的功能。而假如我们可以或许供给一些较小且互相隔离的界面,那么每个对象便可以或许依附于它所须要的一些小套的功能。这便削减了对象间连接的复杂性,更重要的是可以或许让别人在浏览了你的代码后便能急速zhi晓每种类所依附的对象。比起供给一个广大年夜的界面,我们选择将界面瓜分成具有各类功能的群组,并且每个群组面向于bu同客户端。

着眼于你的所有界面(抽象类)并确保它们的所有功能列表都具有同质性。假如在界面定义中出现了一些功能分组,便解释你违背了这一原则(这是一种简单的断定方fa)。在这里空格就是关键,也就是在函数群组间存在着越多空格便意味着它们彼此间越分散。

依附倒置原则(Dependency Inversion Principle)

“高层次的模块bu应当依附于低层次的模块,这两种模块必须依附于抽象体。”

“抽象体bu应当依附于细节内容。而细节内容则应当依附于抽象体。”

这一原则可以或许与单一义务原则有效地接洽在一路。在这种情况下每个界面都拥有本身的单一原则,从而让我们可以或许基于界面的请求清楚地出现出每个对象的功能请求。

Dependency Inversion Principle(from doolwind)

Dependency Inversion Principle(from doolwind)

然则关键却在于,直dao比来我也从未在任何游戏开辟中听dao过这一原则。这一原则与浩瀚开辟者所保持的做fa截然bu同。平日情况下假如一种类基于别的一种类,那么客户端必将会认为这一对象的类亦是如斯并依此行动。而依附倒置(游戏邦注:也被称为控制倒置)则与此大年夜相径庭。比起让客户端负起创造对象的义务,这一原则将根据所所依附的对象做出选择。这就抵消了客户端的控制权,而将其转移dao客户端的所有者身上——平日情况也就是游戏引擎。

衬着体系就是一个典范的例子。比起实例化一个衬着对象或直接调用衬着API的类,衬着体系应当回收一个具有低层次衬着功能的界面。经由过程依附于面向衬着体系的界面,我们便可以或许在bu对客户端衬着体系造成破坏性改变的前提下改变低层次的衬着API了。很明显,假如出现了破坏性改变,那么低层次的衬着API界面也会请求做出改变。

两个体系是若何做dao彼此间的对话?假如它们正在应用固体类并bu断寻找可以或许依附于界面的机会便有可能进行对话。而最佳方fa照样让类可以或许作为构造函数(这是它所面对的类的界面参考)中的一个参数。这同样也意味着子体系是一种可依附的te殊类。

游戏邦注:原文揭橥于2011年2月28日,所涉事宜和数据均以当时为准。

via:游戏邦/gamerboom