SOLID a la Unity3D

There is no perfect way to do code, but creating reusable behaviors and components with SOLID coding principles might be the secret to fast iteration and code reuse.

Single Responsibility Principle These approaches should allows for changes along the line to prevent situations where small changes just break everything. Then I want to start making unit tests so that small parts of the game can be tested.

Make smaller classes. A class should do a single thing!

A class should only have a single reason to fail.
Each component should have a single responsibility. Each behavior should be separated out into its own component. These components should not require any drag-and-drop, they should instead communicate via Actions.

Open Closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.  Use interfaces! Seems like an IOC principle as well. I could refactor weapons based on this, so that each weapon is an IWeapon. It would be nice to take this further so that IWeapons automatically are assigned so there is no drag and drop needed.

Below are a few classes. ShapeBehavior is the base class for shapes, without the specific behaviors for shapes defined. I’ll have to do that on the child classes.
public abstract class ShapeBehavior : Monobehavior
{
     public float Height {get;set;}
     public float Width (get;set;}
     public abstract float Area();
}

public RectangleShape : ShapeBehavior
{
     public override float Area ()
     {
          return Height * Width;
     }
}

public CircleShape : ShapeBehavior ()
{
     return Radius * Radius * Mathf.PI;
}

Liskov’s Substitution Principle

Functions that use pointers to base classes must be able to use objects of derived classes without knowing it. The code should work without knowing the actual class of the Base object. Make sure any characteristics like fields or methods of a base class work for all kind of that base class. Otherwise, break that particular feature into a new class. Here is a good way to look at this. There are lots of birds, but not all birds fly. A Bird, being the base class, would also have a FlightedBird base class for something like a penguin. This is described here with examples : https://www.tomdalling.com/blog/software-design/solid-class-design-the-liskov-substitution-principle

Interface Segregation Principle

Interface reuse can be costly time-wise. Don’t make them too complicated. Make them small so they only have methods of interest to them. One member or member purpose per interface. Interfaces should pretty much do only one thing, since a class can use a lot of interfaces.

In Unity, interfaces are not supported.
Only internal methods use it.
public interface IStringIO{
    string LoadAsString (string id);
}

public interface IJsonIO
{
     JsonObject LoadAsJson(string id)
}

DO THESE instead of a larger interface that is reused amongst a lot of classes. 

Dependency Inversion Principle

Use interfaces / abstracts
Don’t rely on concrete references.
Interfaces are ideal
Abstract classes work too.

Unity Inspector does not support interfaces.
Unity will need abstract classes for fields, but only needs them to be designer editable.
Keep everything designer friendly.
public abstract class WeaponBase : MonoBehavior 
{
     public float Damage = 1f;
}

public class Sword : Weapon {}

public class Polearm : Weapon {}
I’m also quoting : https://www.youtube.com/watch?v=eIf3-aDTOOA
Share