Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Component Pattern for Android

mattak
March 23, 2017

Component Pattern for Android

Component pattern is introduced by GameProgrammingPattern#14. I applied this pattern to Android Activity/Fragment.

mattak

March 23, 2017
Tweet

More Decks by mattak

Other Decks in Programming

Transcript

  1. $PNQPOFOU1BUUFSO
    GPS"OESPJE
    Gotanda.mobile #2
    2017/03/23
    @mattak

    View full-size slide

  2. Ϛονϣ"DUJWJUZ໰୊

    View full-size slide

  3. class MainActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    this.setContentView(R.layout.activity_maps)

    }

    }
    プロジェクト作成時

    View full-size slide

  4. class MainActivity : FragmentActivity(),

    GoogleApiClient.ConnectionCallbacks,

    GoogleApiClient.OnConnectionFailedListener {


    var client: GoogleApiClient? = null


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    this.setContentView(R.layout.activity_maps)

    /* ... */

    }


    override fun onStart() {

    super.onStart()

    /* ... */

    }


    override fun onResume() {

    super.onResume()

    /* ... */

    }


    override fun onConnected(bundle: Bundle?) { /* ... */ }


    override fun onConnectionSuspended(value: Int) { /* ... */ }


    override fun onConnectionFailed(result: ConnectionResult) { /* ... */ }

    }
    GoogleApiClient追加

    View full-size slide

  5. class MainActivity : FragmentActivity(),

    GoogleApiClient.ConnectionCallbacks,

    GoogleApiClient.OnConnectionFailedListener,

    LocationListener,
    ResultCallback {

    var client: GoogleApiClient? = null


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    this.setContentView(R.layout.activity_maps)

    /* ... */

    }


    override fun onStart() {

    super.onStart()

    /* ... */

    }


    override fun onResume() {

    super.onResume()

    /* ... */

    }


    override fun onConnected(bundle: Bundle?) { /* ... */ }


    override fun onConnectionSuspended(value: Int) { /* ... */ }


    override fun onConnectionFailed(result: ConnectionResult) { /* ... */ }


    override fun onLocationChanged(location: Location?) { /* ... */ }


    override fun onResult(result: LocationSettingsResult) { /* ... */ }

    }
    LocationAPI追加

    View full-size slide

  6. class MainActivity : FragmentActivity(),

    GoogleApiClient.ConnectionCallbacks,

    GoogleApiClient.OnConnectionFailedListener,

    LocationListener,
    ResultCallback,

    OnMapReadyCallback {


    var client: GoogleApiClient? = null


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    this.setContentView(R.layout.activity_maps)

    /* ... */

    /* ... */

    }


    override fun onStart() {

    super.onStart()

    /* ... */

    }


    override fun onResume() {

    super.onResume()

    /* ... */

    }


    override fun onConnected(bundle: Bundle?) { /* ... */ }


    override fun onConnectionSuspended(value: Int) { /* ... */ }


    override fun onConnectionFailed(result: ConnectionResult) { /* ... */ }


    override fun onLocationChanged(location: Location?) { /* ... */ }


    override fun onResult(result: LocationSettingsResult) { /* ... */ }


    override fun onMapReady(client: GoogleMap?) { /* ... */ }

    }
    GoogleMap追加

    View full-size slide

  7. Payment追加
    PushSDK追加
    TrackingSDK追加

    無限に膨らんでいくコード
    増えていくinterface
    厄介な継承
    リファクタリングつらい

    View full-size slide

  8. Α͋͘Δରࡦ

    View full-size slide

  9. ܧঝʹΑΔղܾ

    View full-size slide

  10. 1. 継承分離による解決
    MainActivity
    FragmentActivity
    class MainActivity : FragmentActivity()

    View full-size slide

  11. 1. 継承分離による解決
    MainActivity
    FragmentActivity
    class MainActivity : FooSDKActivity()
    FooSDKActivity
    class FooSDKActivity : FragmentActivity()

    View full-size slide

  12. 1. 継承分離による解決
    - 継承がかぶると詰む (外部SDKとか
    - 継承元に密に依存する
    - 無限に継承が増える
    - テスタビリティが低い
    - 各要素の独立性を確保できない

    View full-size slide

  13. 'SBHNFOU
    ʹΑΔղܾ

    View full-size slide

  14. 2. Fragmentによる解決
    - テスタビリティ低い (mock)
    - 制約が大きい、別の問題が起きやすい
    - Viewが必要 (onCreateView)

    View full-size slide

  15. ֎෦Ϋϥε
    ʹΑΔղܾ

    View full-size slide

  16. 3. 外部クラスによる解決
    - モジュールとして外部クラスにする
    - Lifecycleで必要モジュールの関数を呼ぶ

    View full-size slide

  17. 3. 外部クラスによる解決
    Activity.onCreate()
    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    Class1.setup(this)
    Class2.setup(this)
    // …

    }
    Class1.setup()
    Class2.setup()
    Activity.onResume() Class1.connect()
    Class3.run()

    View full-size slide

  18. 3. 外部クラス化による解決
    - Classごとの呼び出し形式が統一されてない
    - Class間の依存に対応しにくい
    - なんだかんだActivityでベタ実装しがち
    - でも筋はいい気がする

    View full-size slide

  19. ͦ͜Ͱɺ
    $PNQPOFOU1BUUFSO

    View full-size slide

  20. 第14章: Component
    1つのゲームの要素の処理が
    複数のドメインにまたがる
    ものであっても、各ドメイン
    が結合しないようにする。

    View full-size slide

  21. 特にUnityでの実装が参考になる
    1 GameObject
    N Component

    View full-size slide

  22. Componentにライフサイクルが移譲されている
    public class MyComponent : MonoBehaviour
    {
    void Awake()
    {
    }
    void Start()
    {
    }
    void Update()
    {
    }
    void FixedUpdate()
    {
    }
    void OnApplicationFocus(bool focusStatus)
    {
    }
    }

    View full-size slide

  23. UnityのComponentがすごいところ✨
    View:Component = 1:N
    - 好きなだけComponentの付け足しができる
    Lifecycleの分離
    - Component毎自由にライフサイクルに処理がかける
    - ファイルが膨れることがない
    - 機能分離が容易

    View full-size slide

  24. UnityのComponentがすごいところ✨
    Component同士の依存が取得しやすい
    var collider = this.GetComponent();
    Portabilityの高さ
    - Componentを使いまわしできる
    - 利用する側に依存しない

    View full-size slide

  25. "OESPJEͰ
    ࣮૷ͯ͠ΈΑ͏

    View full-size slide

  26. 方針
    - Activity, Fragmentへの依存薄く
    - 機能分離できるようにする
    - 使いまわせるようにする
    - 必要に応じて依存取得しやすくする

    View full-size slide

  27. Componentの定義
    - Lifecycleのみにする
    - onCreate, onStart,…
    - Activityを参照可能にする

    View full-size slide

  28. Componentの定義
    public interface Component {

    void onComponentsAssigned(Components components);


    void onCreate(Activity activity, Bundle savedInstanceState);


    void onStart(Activity activity);


    void onRestart(Activity activity);


    void onResume(Activity activity);


    void onPause(Activity activity);


    void onStop(Activity activity);


    void onDestroy(Activity activity);

    }

    View full-size slide

  29. 全ライフサイクル
    実装するんのめんどくない?

    View full-size slide

  30. 空基底クラスをつくってやる
    public class BaseComponent implements Component {


    @Override

    public void onCreate(Activity activity, Bundle savedInstanceState) {}


    @Override

    public void onStart(Activity activity) {}

    // …

    }

    View full-size slide

  31. 復数のComponent管理は?

    View full-size slide

  32. Componentsの定義
    Components Component
    has

    View full-size slide

  33. Componentsの定義
    - Component達を保持する
    - LifecycleにComponent達を呼び出すだけ
    - ComponentsもComponent

    View full-size slide

  34. Componentsを実装する
    public class Components implements Component {

    private Component[] components;


    public Components(Component... components) {

    this.components = components;

    }


    @Override

    public void onCreate(Activity activity, Bundle savedInstanceState) {

    for (Component component : this.components) {

    component.onCreate(activity, savedInstanceState);

    }

    }


    @Override

    public void onStart(Activity activity) {

    for (Component component : this.components) {

    component.onStart(activity);

    }

    }
    // ...

    }

    View full-size slide

  35. Componentsの使い方
    class AppComponents : Components(

    SampleComponent1(),

    SampleComponent2(),

    SampleComponent3()

    )
    必要Componentを列挙するだけ

    View full-size slide

  36. Activityからどう呼ぶ?

    View full-size slide

  37. 必要なライフサイクルで呼び出し
    class ExampleActivity : FragmentActivity() {

    val component: Component = AppComponents()


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    component.onCreate(this, savedInstanceState)

    }


    override fun onStart() {

    super.onStart()

    component.onStart(this)

    }


    override fun onResume() {

    super.onResume()

    component.onStart(this)

    }

    // ....

    }

    View full-size slide

  38. Componentの依存取得どうする?

    View full-size slide

  39. getComponentで取得
    class SampleComponent : BaseComponent() {

    override fun onCreate(activity: Activity?, savedInstanceState: Bundle?)
    {

    val another = this.getComponent(AnotherComponent::class.java)

    // DO SOMETHING

    }

    }

    View full-size slide

  40. どこがすごいの?

    View full-size slide

  41. ここがすごい!✨
    Activityが全く太らない
    継承問題がない
    各ComponentがPortable
    Activity,Fragmentを問わずつかえる
    機能を疎結合にできる
    他のComponentとの依存も書ける
    Mockもつくりやすい

    View full-size slide

  42. Activityの実装
    class MainActivity : FragmentActivity() {

    private val mAppComponents = AppComponents()


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_maps)


    this.mAppComponents.onCreate(this, savedInstanceState)

    }


    override fun onStart() {

    super.onStart()

    this.mAppComponents.onStart(this)

    }


    override fun onResume() {

    super.onResume()

    this.mAppComponents.onResume(this)

    }


    override fun onRestart() {

    super.onRestart()

    this.mAppComponents.onRestart(this)

    }


    override fun onPause() {

    super.onPause()

    this.mAppComponents.onPause(this)

    }


    override fun onStop() {

    super.onStop()

    this.mAppComponents.onStop(this)

    }


    override fun onDestroy() {

    super.onDestroy()

    this.mAppComponents.onDestroy(this)

    }

    }
    基本Lifecycleのみなら
    これ以上増えない

    View full-size slide

  43. ͭ͘Γ·ͨ͠ʂ⭐͍ͩ͘͞ʂ
    HJUIVCDPNNBUUBLDPNQPOFOU
    (あとで公開しときます)

    View full-size slide