0% found this document useful (0 votes)
84 views14 pages

OUG - Design Pattern - Singleton and Inversion of Control PDF

The document discusses the singleton pattern and inversion of control design patterns. It defines singletons, describes different types of singletons and when they should and should not be used. It also defines inversion of control, provides an example, and discusses what it does and when it should be used.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
84 views14 pages

OUG - Design Pattern - Singleton and Inversion of Control PDF

The document discusses the singleton pattern and inversion of control design patterns. It defines singletons, describes different types of singletons and when they should and should not be used. It also defines inversion of control, provides an example, and discusses what it does and when it should be used.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Singleton &

Inversion of Control
“Whats”, “Whens” and “Whys”
Definition

Singleton What It Looks Like

What types of Singletons exist


The mostly used anti-pattern
When Should I Use It
Singleton - Definition
● Enforcing that a “single instance” of a class exists. Reasons to do this are:
○ To define, within the class itself, the rules for its instantiations (Original Argument for It)
● Unfortunately, singletons are very easy to create and use:
○ They are essentially a mutable global variable; that is very hard to make thread-safe.
○ They allow you, the developer, to solve your own problem as opposed to the actual problem.
Not being careful with it leads to thoughts like:
■ I need access to a certain stateful system… How can I call that system from my system?
● Singleton or refactor it into my system’s dependencies? Singleton
■ There is only one of this so it can be a Singleton, [which are easy to implement and use].
● This, in particular, is very dangerous. While that statement is true, it also means you
are looking to fit the actual problem with a predetermined solution you prefer (silver
bullet).
● So, let’s look at its details and understand why that is the case.
Singleton - What it looks like

● See how easy it is to create one?


● Since they are quite easy to create and call, here are common places where
you’ll find them, even though you shouldn’t.
○ UI Code;
○ Asynchronous Communication with Server;
○ Local Data Layers
○ And others… even the killer of maintainability: Gameplay Code.
Singleton - What types of Singletons exist
● Stateful Singletons: These contain persistent state.
○ The state it contains is relevant from frame to frame and changes how it works. These tend to
turn into a glorified global variable; accessed through-out the code base with no clear rules on
who or what uses it. We highly advise against using these since they are a maintenance
nightmare. A very good case would have to be made for that to change;
● Stateless Singletons: These may contain cached state.
○ The state is usually calculated at an interval and made available via the Singleton instance.
Generally speaking, these are somewhat replaceable by either a static class (a singleton with
no state at all is just an utility library -- which is fine) or a normal class and some sort of way to
find the dependencies (Each Unity ECS World is a good example of this);
● Lazy Initialized Singletons:
○ These are very bad in real-time applications (AKA Games). In your game, you should know
when each system is initialized and how. There far too many considerations to be made for you
not to.
Singleton - Where Should I Use It
● In its Stateful form, I’d say the answer is actually never.
○ Time & budget concerns not factored in.
● So what alternatives do we have?
○ If you don’t need any state, a static utility class with no side-effects (only utilizing its parameters)
is much better and maintainable.
○ If you do need persistent state (or even a cached state), a regular class that explicitly declares
its dependencies will always be better.
● Why is it so bad? There are two main reasons:
○ Singletons create massive side-effects on methods that, over the course of a project’s
medium-to-long life, will create a terrible and completely impractical debugging environment.
○ Because it doesn’t seem that bad and its issues are not clear to inexperienced engineers.
■ Alternatives are both harder and require knowledge of more complex engineering techniques.
■ Basically, it is an easy-to-use and apparently harmless solution that is very bad long-term.
■ The reasons it is bad are complex which makes it easy even for non-junior engineers to fall into this trap.
○ So… Let’s check an example.
Singleton - An Example
● Let’s say you have a “Brawl-style” game:
○ That must keep a score that each player is accumulating throughout each match.
○ Now instead of writing a Point Accumulation system that can be used per-player, you go:
■ There is only one player (the game is currently single player) so I’ll just write this as a singleton and call it
from everywhere that adds player points.
■ Inside this system you decide to call the UI to update the points that are being displayed on the screen.
● Let’s look at the consequences of that:
1. Unaware engineers now create unintended side-effects every time they call
Points.Instance.GainPoints().
■ You now have UI code being called from everywhere in your codebase.
2. It is harder to refactor this system to accumulate and display scores for multiple players.
■ Brawl-games will naturally evolve into multiplayer of some sort.
3. This will lead to further side-effects being created:
■ Once multiplayer is finally added after a painstaking refactoring, you will say “points must be validated by
the server… just add that there.” and have Server Communication code being called from everywhere in
your codebase.
Don’t use Singletons
because much
better alternatives
often exist.
Inversion of Definition

Control Model

What it Actually Does


Separate what-to-do code from
Where to Use it
when-to-do code
Inversion of Control - Definition
● A system should either do something or tell systems to do something but
never both at the same time.
○ DISCLAIMER: When looking into this pattern (online) it is easy lose track of the point of the term
which has become quite conflated with frameworks for commercial software and
server-side-solutions, however the principle can be applied to create decoupled systems. It is
also poorly named as Inversion only makes sense if you know what it is inverted from.
● Since this is quite generic, it presents varying solutions to multiple problems:
○ How can we have explicitly declared dependencies that are easy to track and test (mock)?
■ DOTS is an extension of this idea (but keeps two different “dependency trees” -- systems and data).
■ In Mono, this is harder to do. But there are still massive maintainability gains that can be achieved.
○ How can systems we write talk to one another? Or link systems to generate behaviour?
■ How can we write self-contained systems that can be reused in different contexts (very useful in UI)?
● Separating data that does things from data that configures things is generally a good practice.
● This allows a solution to a problem be applied in different contexts.
○ So let’s take a look at it.
Inversion Of Control - Simple Model Example

In this example, both the 2DTrailedMapSystem and SagaStateSystem do specialized things (“draw a map”
and “Process Saga State data”).

The Systems that implement the ISagaMapController are systems that know how to use both the other
systems in order to generate the wanted behaviour.
Inversion Of Control - What it actually does
● At its core, this pattern is about enforcing, in a very clear way, the Single
Responsibility Principle (do one thing and do it well).
● Key ways to think about this:
○ Don’t mistake what you see with what you need to implement (this is especially true of UI).
■ It is very easy, in OOP, to focus on the broader domain terms (character, health, etc...) and to let that into
certain systems. Always analyse the specific case from a data perspective to see if it makes sense.
○ Always look at the specific problem you have to solve and create a system that solves that
problem.
■ Don’t focus on coding the exact domain behaviour. Instead, break down that behaviour into small
systems that can be controlled by other code to generate the behaviour you want.
○ How to use different systems is a problem itself… so create a system that solves that =).
■ You have systems that do things; in OOP, HealthMonoBehaviour, WeaponMonoBehaviour, etc…; in DOTS
(DOP), DamageSystem, MovementSystem, etc...
■ And Systems that tell these what to do; in OOP, whichever script calls GainHealth() or SelectWeapon(); in
DOTS (DOP), CollisionResolutionSystems, InputHandlingSystems, etc…
● So… let’s look at when to use it.
Inversion Of Control - When to use it
● In order to know when to use it, you must be aware of its consequences.
○ In general, the only drawback is that you will have slightly more code.
○ The code will be reusable and optimal for the problems it solves.
○ The only real constraints are budgets and time. But even then, the more you practice this type
of design the faster become able to do it.
● So… Based off of that, when can we use it?
○ Since there is a non-trivial up-front cost to use this pattern in Unity’s MonoBehaviour
environment, use where you have complex requirements that will allow you to enjoy all of the
benefits of the pattern.
○ In DOTS, this is enforced by DOP principles (systems that transform data to create behaviour).
Therefore, it’s kind of hard not to use it.
Don’t code
behaviour; code
systems that can
generate behaviour.

You might also like