Able
Askowl Base Library Enabler
ComponentDecoupler.cs
1 // Copyright 2018 (C) paul@marrington.net http://www.askowl.net/unity-packages
2 
3 using Askowl;
4 
5 namespace Decoupled {
6  using System;
7  using System.Collections.Generic;
8  using UnityEngine;
9 
10  /// <a href="http://bit.ly/2AMWG4P">Creating Decoupled Components</a> <inheritdoc />
11  public abstract class ComponentDecoupler<T> : MonoBehaviour where T : ComponentDecoupler<T> {
12  /// <a href="http://bit.ly/2AO0T8m">List of component initialisers (used internally)</a>
13  protected static event Action<T> Initialisers = delegate { };
14 
15  // ReSharper disable once StaticMemberInGenericType
16  private static readonly Map interfaces = new Map();
17 
18  /// <a href="http://bit.ly/2AO0T8m">Parent class for component interfaces</a>
19  public class ComponentInterface {
20  internal Component Component;
21 
22  /// <a href="http://bit.ly/2AO0T8m">Called in concrete class constructor</a>
23  protected void Instantiate<Tc>(bool primary) where Tc : Component {
24  Type type = typeof(Tc);
25  if (interfaces[type].Found) return;
26 
27  interfaces.Add(type);
28 
29  void initialiser(T t) {
30  if (t.Instantiated) return; // someone else got in first
31 
32  if (primary || (t.defaultComponent == null)) t.defaultComponent = type;
33 
34  var component = t.GetComponent<Tc>();
35  if (component != null) {
36  Component = component;
37  t.componentInterface = (ComponentInterface) MemberwiseClone();
38  }
39  }
40 
41  Initialisers += initialiser;
42  }
43 
44  /// <a href="http://bit.ly/2PHS606">Returns game object name as the most useful</a>
45  public override string ToString() => Component == null ? "null" : Component.gameObject.name;
46  }
47 
48  private ComponentInterface componentInterface;
49 
50  private Type defaultComponent;
51 
52  /// <a href="http://bit.ly/2AO0T8m">Set when the component is first instantiated (used internally)</a>
53  protected bool Instantiated => componentInterface != null;
54 
55  /// <a href="http://bit.ly/2AO0T8m">Called in concrete component instances to get the correct backing component</a>
56  protected ComponentInterface Instance {
57  get {
58  if (componentInterface != null) return componentInterface;
59 
60  Initialisers(this as T);
61  if ((componentInterface != null) || (defaultComponent == null)) return componentInterface;
62 
63  gameObject.AddComponent(defaultComponent);
64  Initialisers(this as T);
65  return componentInterface;
66  }
67  }
68  }
69 }
Definition: Clock.cs:3
void Instantiate< Tc >(bool primary)
Called in concrete class constructor
bool Instantiated
Set when the component is first instantiated (used internally)
override string ToString()
Returns game object name as the most useful
A dictionary wrapper
Definition: Map.cs:10
Creating Decoupled Components
ComponentInterface Instance
Called in concrete component instances to get the correct backing component
static Action< T > Initialisers
List of component initialisers (used internally)
Parent class for component interfaces