{"id":311,"date":"2022-11-04T19:14:28","date_gmt":"2022-11-04T19:14:28","guid":{"rendered":"https:\/\/codingwithleo.xyz\/?p=311"},"modified":"2023-03-17T19:22:28","modified_gmt":"2023-03-17T19:22:28","slug":"solid-the-single-responsibility-principle","status":"publish","type":"post","link":"https:\/\/codingwithleo.xyz\/index.php\/2022\/11\/04\/solid-the-single-responsibility-principle\/","title":{"rendered":"[S]OLID \u2013 The single responsibility principle"},"content":{"rendered":"\n<p>As the same suggests, The Single Responsibility Principle says to us that one entity should be responsible for just one functionality. Some people take this saying to the letter, but we need some common sense when writing code while thinking about the single responsibility of our classes. In other words, the scope of our classes will depend on our project scope.<\/p>\n\n\n\n<p>I&#8217;m very acquainted with game development and the SRP is present in a huge part of this area. For example, let&#8217;s create a character for our game. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t1-1.png\" alt=\"\" class=\"wp-image-330\" width=\"512\" height=\"256\" srcset=\"https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t1-1.png 1024w, https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t1-1-300x150.png 300w, https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t1-1-768x384.png 768w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n<p>It&#8217;s not a compilable code, because it&#8217;s just for demonstration of the diagram above.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp\">class MainCharacter\r\n{\r\nprivate:\r\n\t\r\n\tVector2D Position;\r\n\tWeapon* CurrentWeapon;\r\n\tArray&lt;Projectile*> ShotProjectiles;\r\n\t\r\npublic:\r\n\r\n\tMainCharacter()\r\n\t{\r\n\t\tPosition = Vector2D(0,0);\r\n\t\tCurrentWeapon = new Weapon0();\r\n\t\tInput.Delegates.WhenClicked(\"Up\", CharacterMovement::Jump);\r\n\t\tInput.Delegates.WhenClicked(\"Right\", CharacterMovement::MoveToFront);\r\n\t\tInput.Delegates.WhenClicked(\"Left\", CharacterMovement::MoveToBack);\r\n\t\tInput.Delegates.WhenClicked(\"Space\", CharacterMovement::Shoot);\r\n\t}\r\n\t\r\n\tvoid MoveToFront()\r\n\t{\r\n\t\tPosition += Vector2D(10, 0);\r\n\t}\r\n\tvoid MoveToBack()\r\n\t{\r\n\t\tPosition += Vector2D(-10, 0);\r\n\t}\r\n\t\r\n\tvoid Jump()\r\n\t{\r\n\t\tPosition += Vector2D(0, 10);\r\n\t}\r\n\t\r\n\tvoid Shoot()\r\n\t{\r\n\t\tShotProjectiles.Add(new Projectile());\r\n\t}\r\n\t\r\n\tvoid SelectNextWeapon()\r\n\t{\r\n\t\tdelete Weapon;\r\n\t\tif(CurrentWeapon->Name() == \"Weapon0\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon1();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon1\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon2();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon2\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon0();\r\n\t\t}\r\n\t}\r\n\t\r\n\tvoid SelectPreviousWeapon()\r\n\t{\r\n\t\tdelete Weapon;\r\n\t\tif(CurrentWeapon->Name() == \"Weapon0\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon2();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon1\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon0();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon2\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon1();\r\n\t\t}\r\n\t}\r\n\t\r\n\tvirtual ~MainCharacter()\r\n\t{\r\n\t\tdelete Weapon;\r\n\t\tfor(auto&amp; d : ShotProjectiles) { delete d; };\r\n\t}\r\n}<\/code><\/pre>\n\n\n\n<p>Following the code, you can see that we have the <code>MainCharacter<\/code> class doing a lot of things related to the weapon, input, and movement. For example:<\/p>\n\n\n\n<ul>\n<li><strong>MainCharacter(): <\/strong>setting the initial position of the character, in the sequence, creating a default weapon class, (Weapon0, Weapon1, and Weapon2 are inherited from Weapon). This same constructor method is setting up all the necessary delegates to make the keyboard works with our functions.<\/li>\n\n\n\n<li><strong>MoveFront():<\/strong> Increment the position<\/li>\n\n\n\n<li><strong>MoveBack(): <\/strong>Increment the position<\/li>\n\n\n\n<li><strong>Jump(): <\/strong>Increment the position<\/li>\n\n\n\n<li><strong>Shoot(): <\/strong>Create an object of type Projectile and add it to the array of projectiles.<\/li>\n\n\n\n<li><strong>SelectNextWeapon():<\/strong> Circulate the weapon classes in the forward direction<\/li>\n\n\n\n<li><strong>SelectPreviousWeapon():<\/strong> Circulate the weapon classes in the backward direction<\/li>\n<\/ul>\n\n\n\n<p>All these functionalities are violating our SRP. We need to rethink the mode we made the <code>MainCharacter<\/code> class. Look at the diagram below:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"512\" src=\"https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t2-2.png\" alt=\"\" class=\"wp-image-329\" srcset=\"https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t2-2.png 1024w, https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t2-2-300x150.png 300w, https:\/\/codingwithleo.xyz\/wp-content\/uploads\/2023\/03\/t2-2-768x384.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<p>Now we have a much more organized scheme with some further details. The <code>MainCharacter<\/code> no more is using resources from an external class like <code>EngineInput<\/code>, because now the entity responsible for setting up the input delegates is the <code>CharacterMovement<\/code> (maybe, in a very large scope\/project, it will be necessary to split the <code>CharacterMovement<\/code> in 2 parts, <code>CharacterMovement<\/code> and <code>CharacterInput<\/code>, this last one used just to handle the input delegates). As I said at the start of this post, all these decisions are strictly related to the size of your project or how much you need your project to be extensible\/scalable.<\/p>\n\n\n\n<p>Look at the <code>CharacterWeapon<\/code> class, it is responsible just to store the current weapon that the character is using, and select the next or the previous one as well. The storage of how many projectiles were spawned or which projectile was spawned is now inside the <code>SceneProjectileManager<\/code>.<\/p>\n\n\n\n<p>The CharacterMovement now stores the character position property and all the motion methods related as well. The <code>MainCharacter<\/code> class is working now as a Facade for the classes that are responsible for these other behaviors. To make our system responsible, we can wrap some important methods like <code>GetCharacterPosition()<\/code> and <code>GetCurrentWeapon()<\/code> in our <code>MainCharacter<\/code> in order to access it fast.<\/p>\n\n\n\n<p>Look at the code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp\">class CharacterMovement\r\n{\r\nprivate:\r\n\t\r\n\tVector2D Position;\r\n\t\r\npublic:\r\n\r\n\tCharacterMovement()\r\n\t{\r\n\t\tPosition = Vector2D(0,0);\r\n\t\tInput.Delegates.WhenClicked(\"Up\", CharacterMovement::Jump);\r\n\t\tInput.Delegates.WhenClicked(\"Right\", CharacterMovement::MoveToFront);\r\n\t\tInput.Delegates.WhenClicked(\"Left\", CharacterMovement::MoveToBack);\r\n\t\tInput.Delegates.WhenClicked(\"Space\", CharacterMovement::Shoot);\r\n\t}\r\n\r\n\tvoid MoveToFront()\r\n\t{\r\n\t\tPosition += Vector2D(10, 0);\r\n\t}\r\n\tvoid MoveToBack()\r\n\t{\r\n\t\tPosition += Vector2D(-10, 0);\r\n\t}\r\n\t\r\n\tvoid Jump()\r\n\t{\r\n\t\tPosition += Vector2D(0, 10);\r\n\t}\r\n\t\r\n\tVector2D GetPosition()\r\n\t{\r\n\t\treturn Position;\r\n\t}\r\n\t\r\n\tvirtual ~CharacterMovement();\r\n}\r\n\r\nclass ProjectileManager\r\n{\r\nprivate:\r\n\r\n\tArray&lt;Projectile*> ShotProjectiles;\r\n\r\npublic:\r\n\r\n\tProjectileManager() = default;\r\n\t\r\n\tAddProjectile(Vector2D Direction)\r\n\t{\r\n\t\tShotProjectiles.Add(new Projectile(Direction));\r\n\t}\r\n\r\n\tvirtual ~ProjectileManager()\r\n\t{\r\n\t\tfor(auto&amp; d : ShotProjectiles) { delete d; };\r\n\t}\r\n}\r\n\r\nclass CharacterWeapon\r\n{\r\nprivate:\r\n\r\n\tProjectileManager* _ProjectileManager;\r\n\tWeapon* CurrentWeapon;\r\n\r\npublic:\r\n\r\n\tCharacterWeapon()\r\n\t{\r\n\t\t_ProjectileManager = new _ProjectileManager();\r\n\t}\r\n\t\r\n\tvoid Shoot()\r\n\t{\r\n\t\t_ProjectileManager->AddProjectile(Vector2D::RandomDirection());\r\n\t}\r\n\t\r\n\tvoid SelectNextWeapon()\r\n\t{\r\n\t\tdelete Weapon;\r\n\t\tif(CurrentWeapon->Name() == \"Weapon0\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon1();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon1\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon2();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon2\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon0();\r\n\t\t}\r\n\t}\r\n\t\r\n\tvoid SelectPreviousWeapon()\r\n\t{\r\n\t\tdelete Weapon;\r\n\t\tif(CurrentWeapon->Name() == \"Weapon0\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon2();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon1\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon0();\r\n\t\t}\r\n\t\t\r\n\t\telse if(CurrentWeapon->Name() == \"Weapon2\")\r\n\t\t{\r\n\t\t\tCurrentWeapon = new Weapon1();\r\n\t\t}\r\n\t}\r\n\t\r\n\tWeapon* GetWeapon()\r\n\t{\r\n\t\treturn CurrentWeapon;\r\n\t}\r\n\r\n\tvirtual ~CharacterWeapon()\r\n\t{\r\n\t\tdelete _ProjectileManager;\r\n\t}\r\n}\r\n\r\nclass MainCharacter\r\n{\r\n\t\r\npublic:\r\n\t\r\n\tCharacterMovement* _CharacterMovement;\r\n\tCharacterWeapon* _CharacterWeapon;\r\n\t\r\npublic:\r\n\r\n\tMainCharacter()\r\n\t{\r\n\t\t_CharacterMovement = new CharacterMovement();\r\n\t\t_CharacterWeapon = new CharacterWeapon();\r\n\t}\r\n\t\r\n\tVector2D GetCharacterPosition()\r\n\t{\r\n\t\treturn _CharacterMovement->GetPosition();\r\n\t}\r\n\t\r\n\tWeapon* GetCharacterWeapon()\r\n\t{\r\n\t\treturn _CharacterWeapon->GetWeapon();\r\n\t}\r\n\t\r\n\tvirtual ~MainCharacter()\r\n\t{\r\n\t\tdelete _CharacterMovement;\r\n\t\tdelete _CharacterWeapon;\r\n\t}\r\n}<\/code><\/pre>\n\n\n\n<p>During our work, when we need some action from a weapon, for example, we need to access the CharacterWeapon object and invoke the desired method like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp\">\/\/ Select the previous weapon\nMainCharacterObject->_CharacterWeapon->SelectPreviousWeapon();\n\n\/\/ Using our wrapped shortcut to get current weapon\nWeapon* W = MainCharacterObject->GetCharacterWeapon();<\/code><\/pre>\n\n\n\n<p>Remember, all the code in the post wasn&#8217;t tested, it&#8217;s just to illustrate the manner you need to handle the classes to make them unique in terms of responsibility.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As the same suggests, The Single Responsibility Principle says to us that one entity should be responsible for just one functionality. Some people take this saying to the letter, but we need some common sense when writing code while thinking about the single responsibility of our classes. In other words, the scope of our classes [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,6,7],"tags":[],"_links":{"self":[{"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/posts\/311"}],"collection":[{"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/comments?post=311"}],"version-history":[{"count":14,"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/posts\/311\/revisions"}],"predecessor-version":[{"id":332,"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/posts\/311\/revisions\/332"}],"wp:attachment":[{"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/media?parent=311"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/categories?post=311"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codingwithleo.xyz\/index.php\/wp-json\/wp\/v2\/tags?post=311"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}