太ったActivity/Fragmentをダイエット！Controllerへ処理を委譲する


これは [スピカ Advent Calendar 2015](http://qiita.com/advent-calendar/2015/spika) の１５日目(12/15)の投稿です。

以前、[Roppongi.aar #1](http://roppongi-aar.connpass.com/event/19478/) にて下記ような発表をさせて頂きました。

![スクリーンショット 2015-12-14 22.14.36.png](https://qiita-image-store.s3.amazonaws.com/0/35733/75cf929d-4b0f-712b-7482-6d24e8e472f4.png "スクリーンショット 2015-12-14 22.14.36.png")

> スライド(SlideShare)：[Activity/FragmentからControllerへ処理を委譲する](http://www.slideshare.net/hiroyukikusu/roppongiaar-1activityfragmentcontroller)
> 参考実装(GitHub)：[hkusu/android-controller-delegate-sample](https://github.com/hkusu/android-controller-delegate-sample)

発表の内容は上記のリンクからスライドを見てもらえればよいのですが、かいつまむと、大きくなりがちな Acitivity/Fragment の処理を別クラス(このスライドでは便宜的に `Controller` と読んでいます)へ分離してスッキリさせましょう、という内容になっています。

この方法がいいなと思ってる点は、既に太った Activity/Fragment が存在する場合に、既存の構成を大きく変えずに Activity/Fragment のダイエットが出来る点です。

> 上記のスライドおよび参考実装では [Butter Knife](http://jakewharton.github.io/butterknife/) の利用を前提としていますが、別に Butter Knifie を利用せずとも普通に View(の参照)を Controller クラスに渡す、という感じでも構わない。

私が開発に携わっている [Android版ネイルブック](https://play.google.com/store/apps/details?id=jp.nailbook) では更に発展させ、`Controller` 用の基底クラスを用意して利用しています。今回はこの方法を紹介したいと思うのですが、プロダクトのソースコードは載せられないので、説明用の参考実装をこちらに用意しました。
⇒ [hkusu/android-butterknife-viewcontroller-sample](https://github.com/hkusu/android-butterknife-viewcontroller-sample)

![スクリーンショット 2015-12-16 1.06.34.png](https://qiita-image-store.s3.amazonaws.com/0/35733/00c2ebc0-7350-171e-9321-5b20bec55a20.png "スクリーンショット 2015-12-16 1.06.34.png")

この参考実装の中から、主要なコードを抜粋して説明したいと思います。

> ちなみにタイトルの「委譲」は Java での「委譲」と同じ意味合いです。英語で表記するなら delegate と書けそうですが、iOS の delegate と混同しそうなので避けています。

## 今回の前提

- この参考実装では `Controller` を `ViewController` と呼称しています。
	- iOS の `ViewController` と混同しそうですが、単に `Controller` だと他の `Controller` 的な役割のクラスと区別しづらい為です。
- Butter Knife を利用します。
	- Butter Knife のバージョンは執筆時点の最新バージョンである `7.0.1` です。
- 今回は Activity を対象にしています。
	- Fragment 用の `ViewController` も、同じような感じで作れると思います。
- 参考実装のコードはプロダクトで利用しているものそのままではなく、個人的に汎用的に利用できるよう再整理したものです。宜しければご自由にご利用ください。

***2015.12.16追記：***

- Activity から `ViewController` でなく Fragment を使えばいいという話もありますが、レイアウト or コードで画面に配置しないといけないのと、ライフサイクル等の管理が必要になり面倒なので私は避けてます。
- この `ViewController` と MVPアーキテクチャの `Presenter` の概念の違いはそれほどありません。まあ同じようなものだと思います。

## ButterKnifeViewController クラス

> [ソースはこちら(GitHub)](https://github.com/hkusu/android-butterknife-viewcontroller-sample/blob/master/app/src/main/java/io/github/hkusu/butterknifeviewcontrollersample/ButterKnifeViewController.java)

Activity の処理をいい単位(管理しやすい単位/意味的に人が分かりやすい単位)で `ViewController` へ切り出すのですが、この `ButterKnifeViewController` クラスは各 `ViewController` の基底クラスです。

```java:ButterKnifeViewController.java
// ...

public class ButterKnifeViewController<T> {

    private WeakReference<Activity> mActivityRef;
    private WeakReference<T> mListenerRef;

    @Nullable
    @CheckResult
    protected final Activity getActivity() {
        if (mActivityRef == null) {
            return null;
        }
        return mActivityRef.get();
    }

    @Nullable
    @CheckResult
    protected final T getListener() {
        if (mListenerRef == null) {
            return null;
        }
        return mListenerRef.get();
    }

    @CallSuper
    public void onCreate(@NonNull Activity activity) {
        ButterKnife.bind(this, activity);
        mActivityRef = new WeakReference<>(activity);
    }

    @CallSuper
    public void onStart(@Nullable T listener) {
        if (listener != null) {
            mListenerRef = new WeakReference<>(listener);
        }
    }

    protected void onResume() {}

    protected void onPause() {}

    protected void onStop() {}

    @CallSuper
    public void onDestroy() {
        ButterKnife.unbind(this);
        mActivityRef = null;
        mListenerRef = null; // onStopで処理したいがそのためにコールさせるのは冗長なので
    }
}
```

Butter Knife 周りの定形処理の他に、Activity を渡しています（Android のビュー周りのプログラミングでは何かと Activity が必要な為）。またジェネリクスを利用して、任意のコールバック用のリスナーを設定できるようにしてあります。

> Activity とリスナーの実体が一致する可能性がありますが、役割として別物なので分けてます。

## ◯◯ViewController クラス

> [ソースはこちら(GitHub)](https://github.com/hkusu/android-butterknife-viewcontroller-sample/blob/master/app/src/main/java/io/github/hkusu/butterknifeviewcontrollersample/UserEventViewController.java)

`ButterKnifeViewController` クラスを継承して各 `ViewController` クラスを作成します。私の場合は、クラス名は ◯◯ViewController とするルールにしています。

```java:
public class UserEventViewController 
        extends ButterKnifeViewController<Void> {
// ...
}
```


`ViewController` へ Activity のレイアウトが持つ View がバインドされるので、普通に Butter Knife の記法でインスタンスフィールドに View を定義します。

```java:
public class UserEventViewController 
        extends ButterKnifeViewController<Void> {

    @Bind(R.id.toolbar)
    Toolbar mToolbar;
    @Bind(R.id.todoEditText)
    EditText mTodoEditText;
    @Bind(R.id.createButton)
    Button mCreateButton;
    @Bind(R.id.countTextView)
    TextView mCountTextView;
    @Bind(R.id.todoListView)
    ListView mTodoListView;

// ...
```

後は普通にバインドした View に対してプログラミングしていきます(Acitivity でのプログラミングと一緒です)。Acitivityのライフサイクルメソッドに応じたタイミングで実行したい処理したいであれば  `ButterKnifeViewController` の `onCreate()` `onStart()` etc.. をオーバーライドしてその中に処理を記述します。

```java:
@Override
public void onStart(@Nullable Void listener) {
    super.onStart(listener);

    // ここに Activity の onStart で実行したい何かしらの処理
    mCreateButton.setEnabled(false);

    // ...
}
```

View にイベントを設定したい場合は普通に View にイベントリスナーを設定してもいいですし、Butter Knife でリスナーを設定してもよいです。

```java:
@OnClick(R.id.createButton)
public void onCreateButtonClick() {
    // ...
}
```

Activity が必要な場合は `getActivity()` で取得します(必ずnullチェックする)。例えばソフトウェアキーボードを隠す場合は、次のようになります。

```java:
Activity activity = getActivity();
if (activity == null) {
    return;
}

((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE))
        .hideSoftInputFromWindow(mTodoEditText.getWindowToken(), 0);
```

もし `ViewController` から Activity へコールバックで何かしらの通知をする場合は、`ViewController` 内でインタフェースを定義し、`ButterKnifeViewController` 継承時にジェネリクスで作成したインタフェースを指定します。

```java:
public class UserEventViewController
        extends ButterKnifeViewController<UserEventViewController.Listener> {
    // ...

    public interface Listener {
        void onCreateButtonClick();
        void onDeleteButtonClick();
    }

    // ...
}
```

あとはコールバックしたいタイミングでリスナーのメソッドをコールします。

```java:
Listener listener = getListener();
if (listener != null) {
    listener.onCreateButtonClick();
}
```

## Activity

> [ソースはこちら(GitHub)](https://github.com/hkusu/android-butterknife-viewcontroller-sample/blob/master/app/src/main/java/io/github/hkusu/butterknifeviewcontrollersample/MainActivity.java)

Activity から `ViewController` を利用します。

まず Activity のインスタンスフィールドで `ViewController` のインスタンス化します。

```java:
private UserEventViewController mUserEventViewController = new UserEventViewController();
```

Activity の `onCreate()` で ViewController の `onCreate()` をコールします。

```java:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    mUserEventViewController.onCreate(this);

    // ...
    }
```

> 以降もそうなのですが、Activity のライフライクルメソッド名と `ViewController` のライフサイクルメソッド名を一致させて分かり易いようにしています。

もし `ViewController` から Activity へコールバックで何かしらの通知を受ける場合は `onStart()` でリスナーを渡します。

```java:
@Override
protected void onStart() {
    super.onStart();
    // ...

    mUserEventViewController.onStart(new UserEventViewController.Listener() {
        @Override
        public void onCreateButtonClick() {
            // ...
        }
        @Override
        public void onDeleteButtonClick() {
            // ...
        }
    });

    // ...
}
```

> この例では匿名クラスでリスナーを作成していますが、Activity 自体にリスナーのインタフェースを実装(implements)して `mUserEventViewController.onStart(this)` としてもよいです。好みで。

Activity の `onDestroy()` で `ViewController` の `onDestroy()` をコールします。これを忘れると `ViewController` 内のインスタンス変数に色々な参照が残ってしまうので注意です。

```java:
@Override
protected void onDestroy() {
    super.onDestroy();
    // ...

    mUserEventViewController.onDestroy();

    // ...
}
```

以上となります。Activity を見てみると `ViewController` に処理が分離している分、コード量が減りスッキリしているのが分かるかと思います。今回は Activity から利用する `ViewController` は１つでしたが、`ViewController` を複数作成して利用しても構いません（管理しやすい単位/意味的に人が分かりやすい単位で作成するのが大事）。

また今回のように `ViewController` の基底クラスを用意することで、各 `ViewController` のコードの可読性が良くなります。

## おわりに

Activity の処理を別クラスへ分離する方法を説明しました。再掲となりますが、出来るだけ分かりやすいようコメントもいれていますので、興味があれば参考実装もご覧ください。
⇒ [hkusu/android-butterknife-viewcontroller-sample](https://github.com/hkusu/android-butterknife-viewcontroller-sample)

もしゼロから開発するなら MVP やクリーンアーキテクチャ等の構成の方が良いかもしれません。ただそれらのアーキテクチャは慣れるまで時間がかかりますし、今回の方法は既存のAndroid のコードに導入すること、または、既存の Android の作り方を大きく変えないことに重きを置いています。

また処理をカスタムビューへ分離する方法もあります。
⇒ [Activity, Fragment, CustomView の使い分け - マッチョなActivityにさよならする方法](http://www.slideshare.net/yanzm/droid-kaigi2015-yanzm)
画面をいい単位でパーツ化できるなら、カスタムビューでも良いと思います。

> 個人的にはカスタムビューはちゃんと設計しないと可読性が悪くなってたりして使いづらく、またパーツ同士の連動を考えると複雑になってしまったりするので、あまり使ってません（テクニックが無いだけという説もありつつ..）。

私は [スピカ Advent Calendar 2015](http://qiita.com/advent-calendar/2015/spika) で残り２日ほど記事を担当します。他のメンバーの投稿と合わせてどうぞ宜しくお願いします^^