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

Component Pattern for Android

85d7f0ae00c0cf1092dffcec966e3ae9?s=47 mattak
March 23, 2017

Component Pattern for Android

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

85d7f0ae00c0cf1092dffcec966e3ae9?s=128

mattak

March 23, 2017
Tweet

Transcript

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

  2. Ϛονϣ"DUJWJUZ໰୊

  3. class MainActivity : FragmentActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)
 this.setContentView(R.layout.activity_maps)
 }
 } プロジェクト作成時
  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追加
  5. class MainActivity : FragmentActivity(),
 GoogleApiClient.ConnectionCallbacks,
 GoogleApiClient.OnConnectionFailedListener,
 LocationListener, ResultCallback<LocationSettingsResult> { 


    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追加
  6. class MainActivity : FragmentActivity(),
 GoogleApiClient.ConnectionCallbacks,
 GoogleApiClient.OnConnectionFailedListener,
 LocationListener, ResultCallback<LocationSettingsResult>,
 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追加
  7. Payment追加 PushSDK追加 TrackingSDK追加 … 無限に膨らんでいくコード 増えていくinterface 厄介な継承 リファクタリングつらい …

  8. Α͋͘Δରࡦ

  9. ܧঝʹΑΔղܾ

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

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

    FooSDKActivity : FragmentActivity()
  12. 1. 継承分離による解決 - 継承がかぶると詰む (外部SDKとか - 継承元に密に依存する - 無限に継承が増える -

    テスタビリティが低い - 各要素の独立性を確保できない
  13. 'SBHNFOU ʹΑΔղܾ

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

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

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

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

  19. ͦ͜Ͱɺ $PNQPOFOU1BUUFSO

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

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

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

    } void Start() { } void Update() { } void FixedUpdate() { } void OnApplicationFocus(bool focusStatus) { } }
  23. UnityのComponentがすごいところ✨ View:Component = 1:N - 好きなだけComponentの付け足しができる Lifecycleの分離 - Component毎自由にライフサイクルに処理がかける -

    ファイルが膨れることがない - 機能分離が容易
  24. UnityのComponentがすごいところ✨ Component同士の依存が取得しやすい var collider = this.GetComponent<BoxCollider>(); Portabilityの高さ - Componentを使いまわしできる -

    利用する側に依存しない
  25. "OESPJEͰ ࣮૷ͯ͠ΈΑ͏

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

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

  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);
 }
  29. 全ライフサイクル 実装するんのめんどくない?

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

    void onCreate(Activity activity, Bundle savedInstanceState) {}
 
 @Override
 public void onStart(Activity activity) {}
 // …
 }
  31. 復数のComponent管理は?

  32. Componentsの定義 Components Component has

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

  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);
 }
 } // ...
 }
  35. Componentsの使い方 class AppComponents : Components(
 SampleComponent1(),
 SampleComponent2(),
 SampleComponent3()
 ) 必要Componentを列挙するだけ

  36. Activityからどう呼ぶ?

  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)
 }
 // ....
 }
  38. Componentの依存取得どうする?

  39. getComponentで取得 class SampleComponent : BaseComponent() {
 override fun onCreate(activity: Activity?,

    savedInstanceState: Bundle?) {
 val another = this.getComponent(AnotherComponent::class.java)
 // DO SOMETHING
 }
 }
  40. どこがすごいの?

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

  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のみなら これ以上増えない
  43. ͭ͘Γ·ͨ͠ʂ⭐͍ͩ͘͞ʂ HJUIVCDPNNBUUBLDPNQPOFOU (あとで公開しときます)