Future パターンとは
wikipedia によると
何らかの処理を別のスレッドで処理させる際、その処理結果の取得を必要になるところまで後回しにする手法。
とある
例えばあるオブジェクトをロードする際にロード処理を丸投げしてしまい
使うときに問題なければ使うといった実装が Future パターンになる
UnityEngine の Future パターン
Unity 内では UnityWebRequest
が Future パターンが実装されているものを挙げられる
UnityWebRequest
のリクエストの流れを簡単に記述すると以下になる
// リクエスト用のインスタンスを作成 var request = UnityWebRequest.Get("http://www.my-server.com"); // リクエストを投げる request.SendWebRequest() // エラーや処理が終わってなければ使わない if (!request.isDone || request.isNetworkError) { return; } // 成功していればロードする if (request.isDone) { Debug.Log(request.downloadHandler.text); }
これは リクエストさえ投げてしまえば、リクエストの結果を使うタイミングはこちらで決めることができる
さらに、もし使うときにも、エラーや処理が終わってなければ待つことができるため、これは Future パターンと言えるはず
Future パターンを実装する
UnityWebRequest
を元に最低限必要な機能を出してみる
- Load : ロード処理を行う
- IsError : エラーが発生したかどうか
- Error : エラー時のメッセージ
- IsDone : ロードが終了したかどうか
- Value : ロード成功時に取得するおぶじぇくと
これを interface にするとこのようになる
public interface IFuture { void Load(); } public interface IFuture<T> : IFuture { T Value { get; } }
Value だけは型の指定が必要なので、型がいらない部分と、必要な部分で interface を分離
これで型が分からなくてもロード処理をできるようになる
これをクラス化するとこうなる
public abstract class Future<T> : IFuture<T> { public abstract void Load(); public bool IsError { get; protected set; } public bool IsDone => Value != null; public string Error { get; protected set; } public T Value => _Value; protected T _Value; }
このクラスを元に、Resource をロードするクラスを作ってみると
public class ResourceRequest<T> : Future<T> where T : UnityEngine.Object { private readonly string _path; public ResourceRequest(string path) { _path = path; } public override void Load() { var obj = Resources.Load(_path); _Value = obj as T; if (obj == null || Value == null) { IsError = true; Error = obj == null ? "Path is Illegal. " + _path : "Object Type is Illegal. " + nameof(T); } } }
また、上記クラスを使ってロードするクラスがこちら
public static class ResourceLoad { public static ResourceRequest<T> Load<T>(string path) where T : UnityEngine.Object { return new ResourceRequest<T>(path); } }
実際にロード処理を書いてみるとこうなる
var request = ResourceLoad.Load<Sprite>("Hoge/Fuga"); request.Load(); if (request.IsDone) { Debug.Log(request.Value.name); }
個人的にはこれで、Future パターンが実装できたように思う