個人的なRealmクラス設計のベストプラクティス(Android)

# はじめに
何回かRealmを利用するアプリを作成している上で、段々とこれがベストなのではないかという設計・実装を思いついたので、ここに記そうと思う。(異論・改善は大歓迎)

# 前提条件
RealmHelperというクラスを作成する。
Realmにアクセスするために作成。
1RealmObjectに対して1RealmHelperを作り、そのRealmHelperがそのオブジェクトへのアクセスを担う。
これによって、一つのクラスの肥大化を防ぎ、各Helperの責任範囲が明確になる。

# 各視点での最適解を考える
設計に当たって、2視点から必要なことを洗い出してみた。

### 必要になる使い方
Realmの利用方法にどんなものがあるかを考えてみる。

1. 単発でアクセス行いたい場合
 - 画面で1回だけinsert処理を行いたい
 - 一度値を読み込んで、画面に表示したいetc...
 - **この場合、helperクラスはインスタンス化せず、staticメソッドで気軽に呼び出したい。Realmのopen、closeは内部でhelper側で自動的にやってほしい。**

2. RealmからRealmObjectを取得して同期したい場合
 - RealmAdapterを使いたい
 - リアルタイムにRealmの値を修正したいetc...
 - **この場合、realm自体は終了したいときまでopenにしておきたい。ユーザー(Fragmentなど)は、必要に応じてcloseメソッドを呼び出したい。**

### クラスの実装しやすさ
実装は早く終わった方が良いし、共通処理はなるべくまとめたい。

# 考えた構成
- 抽象クラスとしてAbstractRealmHelperというクラスを用意する。
 - このクラスがどのhelperクラスでもやりたい共通処理を実装している。
 - 各子helperクラスはこれを継承すれば実装が楽+処理も統一される
- AbstractRealmHelper
 - コンストラクタでは、Realmを生成し、フィールドに保持する。(小クラスはこのフィールドを使ってRealmにアクセスできる)
 - トランザクション処理を2種類用意する
     - staticなもの
         - 子helperがstaticメソッドを実装しやすい時用
         - transactionを引数で受取realmを生成し、closeする
     - 非staticなもの
         - 子helperがインスタンス化した状態で使いたいとき用
         - transactionを引数にとり、フィールドに持つmRealmに対してtranscationを実行する(closeは行わない)
 - フィールドのrealmをcloseする処理を定義する
 - 作るhelperクラスによっては不要なもの
     - RealmObjectを継承しているクラスをジェネリクスとして定義
     - upsertメソッドとfindAllメソッドを作成
     - (これらは私が作った時は必要だったため作成。upsert, findAll共に必ず行うわけではないので、不要かも)

# ソースコード
```java
public abstract class AbstractRealmHelper<T extends RealmObject> {

    protected final Realm mRealm;

    public AbstractRealmHelper() {
        mRealm = getRealm();
    }

    static Realm getRealm() {
        return Realm.getDefaultInstance();
    }

    protected static void executeTransactionOneShot(Realm.Transaction transaction) {
        Realm realm = getRealm();
        realm.executeTransaction(transaction);
        realm.close();
    }

    public abstract void upsert(T t);

    public abstract RealmResults<T> findAll();

    protected void executeTransaction(Realm.Transaction transaction) {
        mRealm.executeTransaction(transaction);
    }

    public void destroy() {
        mRealm.close();
    }
}
```

```java
public class ItemRealmHelper extends AbstractRealmHelper<ItemRealmObject> {

    public static void insertOneShot(ItemRealmObject itemRealmObject) {
        executeTransactionOneShot(insertTransaction(itemRealmObject));
    }

    private static Realm.Transaction insertTransaction(final ItemRealmObject itemRealmObject) {
        return new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.insertOrUpdate(itemRealmObject);
            }
        };
    }

    @Override
    public void upsert(final ItemRealmObject item) {
        executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.copyToRealmOrUpdate(item);
            }
        });
    }

    @Override
    public RealmResults<ItemRealmObject> findAll() {
        return mRealm.where(ItemRealmObject.class).findAll();
    }
}
```
# 課題など
- このままだと、一度closeすると再オープン出来ない(現状そういうものとして使っている)
- そのため、closeをするメソッドをcloseとせずdestroyにしている(気休め？)
- 各RealmObjectでconfigurationを分けたい場合に対応できない
 - ただ、ファイル名を分けることの利点がよく分かっていないためdefaultInstanceで良い気がしている
 - 複数ファイルを作っても一つのRealmObjectが修正されると結局どのRealmでもMigrationが必要になると思うので
 - 本当はconfigurationも分けようと思っていたが、staticクラスのオーバーライドが出来ないため、子クラスに実装を持たせることが出来ず手詰まってしまった。(getRealmをstaticメソッドでも、インスタンスメソッドでも両方で呼びたかった。staticメソッドを諦めれば子クラスでgetRealmをオーバーライドすればよいか)