Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

何回か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共に必ず行うわけではないので、不要かも)

ソースコード

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();
    }
}
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をオーバーライドすればよいか)
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away