{"id":545,"date":"2011-02-28T22:34:36","date_gmt":"2011-02-28T22:34:36","guid":{"rendered":"http:\/\/www.doolwind.com\/blog\/?p=545"},"modified":"2011-02-28T22:34:36","modified_gmt":"2011-02-28T22:34:36","slug":"solid-principles-for-game-developers","status":"publish","type":"post","link":"https:\/\/www.doolwind.com\/blog\/solid-principles-for-game-developers\/","title":{"rendered":"SOLID Principles For Game Developers"},"content":{"rendered":"<p><a href=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/solidprinciples.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright\" title=\"SOLID Principles\" src=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/solidprinciples.jpg\" alt=\"\" width=\"162\" height=\"130\" \/><\/a>The SOLID principles are a set of 5 software development principles coined by \u201cUncle Bob\u201d (Robert C. Martin).\u00a0 They are a set of guidelines for Object Oriented Design (OOD), specifically for class design.\u00a0 They are widely used by agile business programmers however they are generally unknown amongst game developers.\u00a0 This article describes the principles and frames them in common game development situations.<\/p>\n<p><strong>Single Responsibility Principle<\/strong><\/p>\n<p><em>\u201cThere should never be more than one reason for a class to change.\u201d<\/em><\/p>\n<p><a href=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/SingleResponsibilityPrinciple.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright\" title=\"Single Responsibility Principle\" src=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/SingleResponsibilityPrinciple.jpg\" alt=\"\" width=\"270\" height=\"216\" \/><\/a><\/p>\n<p>The first principle is the cornerstone of the set and gives the greatest return on investment when followed correctly.\u00a0 It states that each class should have only a single responsibility and therefore reason to change.\u00a0 Keeping each class small and tightly focussed allows developers to know exactly where to go to find or add particular functionality to the game.\u00a0<\/p>\n<p>Why is having more than one responsibility bad?\u00a0 Multiple responsibilities means there is coupling between separate pieces of code.\u00a0 Changes to one responsibility reduce the ability for the class to meet the requirements of the other responsibilities.\u00a0 This leads to fragile design that breaks often and in unexpected ways.\u00a0 \u201cWhy did changing from rendering API break jumping in the game?\u201d<\/p>\n<p>The way to fix code that breaks this principle is to separate each responsibility into its own class.\u00a0 The first step can be to extract an interface per responsibility.\u00a0 Other classes can then rely on these interfaces rather than the class itself.\u00a0 The class can then safely be split up into separate classes for each responsibility that each implements a single interface.<\/p>\n<p><span style=\"text-decoration: underline;\">When have you got it?<\/span> \u2013 The usual culprit breaking this principle is that one (or group) class that\u2019s hundreds or thousands of lines long.\u00a0 You know the one I\u2019m talking about. Often it\u2019s the <em>GameObject<\/em> or <em>Entity<\/em> class that everyone seems to throw code into.\u00a0 This class usually has about 500 reasons to change, and therefore 500 responsibilities.\u00a0 It\u2019s usually involved in the heinous bugs that crop up constantly.<\/p>\n<p><strong>Open Closed Principle<\/strong><\/p>\n<p><em>\u201cSoftware entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.\u201d<\/em><\/p>\n<p><a href=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/OpenClosedPrinciple.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright\" title=\"Open Closed Principle\" src=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/OpenClosedPrinciple.jpg\" alt=\"\" width=\"270\" height=\"216\" \/><\/a><\/p>\n<p>The goal of this principle is for each class to change as infrequently as possible while allowing it to be used in as many situations as possible.\u00a0 While these two requirements may seem at odds they actually complement each other to generate robust design.\u00a0 A class is open for extension means that the behaviour of a class can be changed in new and different ways as the requirements change.\u00a0 A class is closed for modification when no source code changes are required for these changes in requirements to be met.<\/p>\n<p>This principle can easily be resolved with data driven design.\u00a0 By passing the required configuration data to a class it can easily be extended reducing its need for modification.\u00a0 Any variables (in the mathematical sense) should be passed to the class so that the classes definition itself (the code) does not specify the functionality of the class alone.\u00a0 I find this the easiest to think about in terms of the basic OOD principle of data and operations.\u00a0 The class defines the operations it can perform (its functions) and the data that it operates on.\u00a0 As much of this data as possible should be passed to the class\/function.\u00a0 This moves the configuration of its data outside the class itself to the calling code increasing its ability to change.<\/p>\n<p><span style=\"text-decoration: underline;\">When have you got it?<\/span> \u2013 This is the file you dread checking-in because everyone seems to be working on it all the time.\u00a0 No matter what system you\u2019re working on, these files always seem to be involved.<\/p>\n<p><strong>Liskov Substitution Principle<\/strong><\/p>\n<p><em>\u201cFunctions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.\u201d<\/em><\/p>\n<p><a href=\"htp:\/\/www.doolwind.com\/images\/blog\/solid\/LiskovSubtitutionPrinciple.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright\" title=\"Liskov Substitution Principle\" src=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/LiskovSubtitutionPrinciple.jpg\" alt=\"\" width=\"270\" height=\"216\" \/><\/a><\/p>\n<p>Inheritance and polymorphism are powerful mechanisms for solving complex problems with simple solutions.\u00a0 They are also powerful mechanisms for creating bugs and problematic code.\u00a0 This principle involves making sure inheritance hierarchies are sound and not being abused by code that introduces bugs that are hard to find.\u00a0 While it seems simple on the surface, this principle can be quite complex to solve correctly.<\/p>\n<p>The first steps to solving this problem are to find instances of checking for an objects type &#8211; both its own and that of objects it\u2019s working on.\u00a0 Beyond this easy first step comes a principle known as \u201cDesign By Contract\u201d.\u00a0 Each function has a set of conditions that must be true before it is called (pre-conditions) and a set of conditions it guarantees are met after its completion (post-conditions).\u00a0 These are often implied conditions kept in the mind(s) of the programmer(s) working on them.\u00a0 The first step is formalising these conditions into code.\u00a0 Once this step is completed the following rule can be met \u2013 \u201cDerived classes can only weaken pre-conditions and strengthen post-conditions\u201d.\u00a0 Put another way, functions of a derived class should expect no more than their base class and promise no less.\u00a0 This principle is important because of the fact a model viewed in isolation cannot be meaningfully validated.\u00a0 You don\u2019t know whether your new \u201cTank\u201d class is valid until you run it in the context of its parent, siblings and other game system.<\/p>\n<p><span style=\"text-decoration: underline;\">When have you got it?<\/span> \u2013 Classes breaking this rule are really easy to find.\u00a0 Look for any base class that uses run-time type information (RTTI) to interrogate its own type (or the type of an object it\u2019s working on).\u00a0 As soon as the GameEntity class is checking if it\u2019s of type \u201cTank\u201d to do some special code, you\u2019ve broken this principle.\u00a0 The class should be able to polymorphically call functions without caring about the actual type of the object.<\/p>\n<p><strong>Interface Segregation Principle<\/strong><\/p>\n<p><em>\u201cClients should not be forced to depend upon interfaces that they do not use.\u201d<\/em><\/p>\n<p><a href=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/InterfaceSegregationPrinciple.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright\" title=\"Interface Segregation Principle\" src=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/InterfaceSegregationPrinciple.jpg\" alt=\"\" width=\"270\" height=\"216\" \/><\/a><\/p>\n<p>Interfaces should be used for communication between different objects to encourage clean, modular code. This principle takes that concept further by making sure that the interfaces we use are themselves clean and unified. The larger the interface, the more the client is relying on functionality of another object. By keeping small segregated interfaces each object will rely upon only the smallest set of functionality it actually requires. This reduces the complexity of links between objects and more importantly, lets someone reading your code know exactly what each class relies upon. Rather than one \u201cfat\u201d interface we break the interface up into multiple smaller groups of functionality that each serve a different client.<\/p>\n<p>This principle links back to the single responsibility principle nicely. In this case each interface should have a single responsibility. This lets you explicitly state the functionality requirements of each object based on the interfaces it requires.<\/p>\n<p><span style=\"text-decoration: underline;\">When have you got it?<\/span> \u2013 Look at all your interfaces (abstract classes) and make sure their listing of functions are homogenous.\u00a0 An easy way to tell you\u2019ve broken this principle is when there are small groupings of functions within the interface definition.\u00a0 Whitespace is the key here, the more whitespace between the groups of functions, the more disparate they are.<\/p>\n<p><strong>Dependency Inversion Principle<\/strong><\/p>\n<p><em>\u201cHigh level modules should not depend upon low level modules. Both should depend upon abstractions.\u201d<\/em><\/p>\n<p><em>\u201cAbstractions should not depend upon details. Details should depend upon abstractions.\u201d<\/em><\/p>\n<p><a href=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/DependencyInversionPrinciple.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright\" title=\"Dependency Inversion Principle\" src=\"http:\/\/www.doolwind.com\/images\/blog\/solid\/DependencyInversionPrinciple.jpg\" alt=\"\" width=\"270\" height=\"216\" \/><\/a><\/p>\n<p>This is a key point that I had not heard of at all in game development until recently. This principle is quite the opposite of how many developers are used to working. Usually, if a class depends on another class, the client will instantiate an object of that class and then act upon it. Dependency Inversion (also called Inversion of Control) turns this on its head. Instead of the client being responsible for creating the object, it is given the object it depends on. This takes the control away from the client and moves it to the owner of the client, often the game engine.<\/p>\n<p>A good example of this is in a rendering system. Rather than instantiating a rendering object, or directly calling the classes of the rendering API, the rendering system should receive an interface to the low level rendering functionality. By relying on an interface that is given to the rendering system, the low level rendering API can be changed without making breaking changes to the client rendering system. It becomes obvious if breaking changes occur as the low level rendering API\u2019s interface will require changing.<\/p>\n<p><span style=\"text-decoration: underline;\">When have you got it?<\/span> \u2013 When two systems are talking to each other, how do they do it?\u00a0 If they are using concrete classes then look for opportunities for them to rely on interfaces instead.\u00a0 The best way is for the class to take as parameter to its constructor an interface reference to the class it needs to work on.\u00a0 This also makes it obvious what sub-systems are particular class is reliant on.<\/p>\n<p><strong>Conclusion<\/strong><\/p>\n<p>What are your thoughts on the SOLID principles? Do you see benefit from adopting these in the games industry as web and application development has done?<\/p>\n<p>SOLID Motivational Posters, by <a href=\"http:\/\/www.lostechies.com\/blogs\/derickbailey\/archive\/2009\/02\/11\/solid-development-principles-in-motivational-pictures.aspx\">Derick Bailey<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The SOLID principles are a set of 5 software development principles coined by \u201cUncle Bob\u201d (Robert C. Martin).\u00a0 They are a set of guidelines for Object Oriented Design (OOD), specifically for class design.\u00a0 They are widely used by agile business programmers however they are generally unknown amongst game developers.\u00a0 This article describes the principles and <a class=\"more-link\" href=\"https:\/\/www.doolwind.com\/blog\/solid-principles-for-game-developers\/\">Read More<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[33],"tags":[112,35,37],"class_list":["post-545","post","type-post","status-publish","format-standard","hentry","category-game-development","tag-game-development","tag-game-engine","tag-game-programming"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pgEc5-8N","_links":{"self":[{"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/posts\/545","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/comments?post=545"}],"version-history":[{"count":0,"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/posts\/545\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/media?parent=545"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/categories?post=545"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doolwind.com\/blog\/wp-json\/wp\/v2\/tags?post=545"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}