Skip to content
Published August 7, 2019

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

What is software architecture? The answer is multitiered. At the highest level, there are the architecture patterns that define the overall shape and structure of software applications. Down a level is the architecture that is specifically related to the purpose of the software application. Yet another level down resides the architecture of the modules and their interconnections. This is the domain of design patterns, packages, components, and classes.

Robert C Martin
Design Principles and Design Patterns
https://web.archive.org/web/20191116231621/https://fi.ort.edu.uy/innovaportal/file/2032/1/design_principles.pdf
SOLID is an acronym standing for :
  • Single Responsibility Principle
  • Open – Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle
Programs written using these golden rules of programming tend to be easier to scale, cost less, and are easier to make changes to than other organized approaches. They are Agile and Adaptive. “Uncle Bob”, or Robert C. Martin came up with this pattern, and is not to be confused with the Gang of Four’s design patterns. I hope to cover those as well, because they provide nice recipes for software. I’ll probably do a write up comparing these patterns. Studying these principles has allowed me to write code where making small changes no longer sends me into a deep dive of rewrites. These principles prevent situations where small changes just break everything. I should mention that test driven development has helped with this mindset a great deal. My examples here will be in C# for Unity3D architecture. Below are videos and summaries of the principles. Single Responsibility Principle
This one seems obvious to me. Make smaller classes. A class should do a single thing!
“A class should only have a single responsibility, that is, only changes to one part of the software’s specification should be able to affect the specification of the class.”
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

Comments are closed.

Share