A loosely coupled code is preferred.
The introduction of an anti-corruption layer and the Dependency Inversion Principle (DIP) protect your code from external library changes.
In Unity, you can separate assembly space by Assembly definition. It allows for more powerful layer separation
Main
has IDependencee
object to do something. Unity life cycle will call Main.Start()
and DoSomething()
.
public class Main : MonoBehaviour
{
[Inject] IDependencee dependencee;
void Start()
{
dependencee.DoSomething();
Debug.Log(dependencee.GetCommonObject().value);
}
}
Main
does not know implemention of IDependencee
. It means changing Dependency
implemention do not effect Main
.
In addition, Dependencer.asmdef does not refer DependenceeImplement.asmdef so you can not even construct Dependencee
object in Main
.
Dependencer.asmdef refers to DependenceeAbstruct.asmdef. This is anti-corruption layer assembly space and contains only interfaces that define the behavior.
namespace AsmdefDependencyPattern.DependenceeAbstruct
{
public interface IDependencee
{
void DoSomething();
Common GetCommonObject();
}
}
Of course, DependenceeImplement.asmdef refers to DependenceeAbstruct.asmdef to implement interfaces.
namespace AsmdefDependencyPattern.DependenceeImplement
{
public class Dependencee : DependenceeAbstruct.IDependencee
{
public void DoSomething()
{
UnityEngine.Debug.Log("Dependencee.DoSomething()");
}
public Common GetCommonObject()
{
return new Common {value = 999};
}
}
}
Wait, how and who provide Dependencee
object to Main
?
The answer is Dependency Injection(DI).
Zenject is DI framework wroks on Unity. If IDependencee
is requested, set it to return an instance of Dependencee
.
public class DependenceIntermediary : Zenject.MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<IDependencee>().To<Dependencee>().AsTransient();
}
}
DependenceIntermediary.asmdef refers to both DependenceeAbstruct.asmdef and DependenceeImplement.asmdef to resolove dependency.
And Zenject.SceneContext
will resolove dependency between IDependencee
and Dependencee
in scene, before Main.Awake()
called.
Do you need a class to traverse these layers?
In order to refer from these assembly spaces, you need to create a new assembly space and define class there.
Common.asmdef does not refer to an assembly, but it is referred to by 3 assemblies.
The Assembly definition help you strictly separate the layers within your Unity project.
see also: https://github.com/naninunenoy/UnityCleanArchitectureExample