You could also use Service.AddInstanceChangedListener
instead of SceneManager.loadScene
. This way the system would be a bit more flexible, and e.g. wouldn’t break if you relocate the service to another scene, and would be more easily testable.
You can use a factory method (or a factory class) to encapsulate all the complexity of creating and setting up the PocoClass.
If you don’t want to have to worry about always null-checking ISecondSceneService inside PocoClass, and risk running into NullReferenceExceptions, then here’s a couple of alternative approaches you can consider:
Task<IMyService>
Task<IMyService>
provides a convenient mechanism for easily deferring execution of all methods in PocoClass until the required service is available:
public class PocoClass
{
private readonly Task<ISecondSceneService> secondSceneService;
private PocoClass(Task<ISecondSceneService> secondSceneService) => this.secondSceneService = secondSceneService;
// Factory method for easy creation
public static PocoClass Create()
{
if(Service.TryGet(out ISecondSceneService secondSceneService))
{
return new(Task.FromResult(secondSceneService));
}
var taskCompletionSource = new TaskCompletionSource<ISecondSceneService>();
Service.AddInstanceChangedListener<ISecondSceneService>(OnSecondSceneServiceChanged);
return new(taskCompletionSource.Task);
void OnSecondSceneServiceChanged(Clients clients, ISecondSceneService oldInstance, ISecondSceneService newInstance)
{
Service.RemoveInstanceChangedListener<ISecondSceneService>(OnSecondSceneServiceChanged);
taskCompletionSource.SetResult(newInstance);
}
}
// Async usage example
public async Task DoSomethingUsingSecondServiceAsync() => (await secondSceneService).DoSomething();
// Sync usage example
public bool TryDoSomethingUsingSecondService()
{
if(!secondSceneService.IsCompletedSuccessfully)
{
return false;
}
secondSceneService.Result.DoSomething();
return true;
}
}
Null Object Pattern
You could also use the Null Object pattern, to first initialize PocoClass with a placeholder service, that does basically nothing when its members are called. Then once the real service becomes available, it can be swapped in to take the place of the placeholder.
This way you would never need to worry about null-checking the service, and you wouldn’t need to add any sort of IsReady flag to PocoClass, and require all its clients to always to check the flag before using any of its members.
public class PocoClass
{
private ISecondSceneService secondSceneService;
public PocoClass(ISecondSceneService secondSceneService) => this.secondSceneService = secondSceneService;
public static PocoClass Create()
{
if(Service.TryGet(out ISecondSceneService secondSceneService))
{
return new(secondSceneService);
}
var result = new PocoClass(new SecondSceneServicePlaceholder());
Service.AddInstanceChangedListener<ISecondSceneService>(OnSecondSceneServiceChanged);
return result;
void OnSecondSceneServiceChanged(Clients clients, ISecondSceneService oldInstance, ISecondSceneService newInstance)
{
Service.RemoveInstanceChangedListener<ISecondSceneService>(OnSecondSceneServiceChanged);
result.secondSceneService = newInstance;
}
}
// Sync usage example
public void DoSomethingUsingSecondService() => secondSceneService.DoSomething();
private sealed class SecondSceneServicePlaceholder : ISecondSceneService
{
public void DoSomething() { }
}
}