android cleanarchitecture

57
Android Clean Architecture ことはじめ Mercari, Inc. Tomoaki Imai 08/08/2015 Android All Stars @Shibuya dots.

Upload: tomoaki-imai

Post on 21-Apr-2017

15.884 views

Category:

Engineering


7 download

TRANSCRIPT

Page 1: Android cleanarchitecture

Android Clean Architecture ことはじめMercari, Inc. Tomoaki Imai

08/08/2015 Android All Stars @Shibuya dots.

Page 2: Android cleanarchitecture

Tomoaki ImaiMercari, Inc.

twitter: @tomoaki_imai

github: tomoima525

Page 3: Android cleanarchitecture

今日はみなさんに質問が あります

Page 4: Android cleanarchitecture

胸に手を当てて振り返って みてください

Page 5: Android cleanarchitecture

キレイな設計、できてますか?

Page 6: Android cleanarchitecture

Androidにおける設計の難しさ• Life Cycle(画面再生成 etc)

• 数多く存在するApi

• OSごとのバージョン管理

Page 7: Android cleanarchitecture

みんな似たようなことで 悩んでる

ライフサイクル 考えるのめんどくさい

画面回転が入ると面倒

Activity, Fragmentが どんどん肥大化する

ディレクトリ構成が プロジェクトでまちまち

Unit/UI Testが難しい

Page 8: Android cleanarchitecture

“Android Clean Architecture” という考え方

The Clean Architecture by Uncle Bob

http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Page 9: Android cleanarchitecture

“Android Clean Architecture” という考え方

The Clean Architecture (Enterprise Application)

(Web Service)+ =

Android Clean Architecture

Page 10: Android cleanarchitecture

“Android Clean Architecture” という考え方

http://www.slideshare.net/shinnosukekugimiya/ss-50705888

https://github.com/android10/Android-CleanArchitecture

Page 11: Android cleanarchitecture

今日みなさんに持って返って もらいたいもの

• Android Clean Architectureの思想をさっくり理解する

• Android Clean Architectureの実際的な実装方法を理解する

• 既存の設計と比べてどういうメリットがあるか理解する

Page 12: Android cleanarchitecture

標準的なWeb設計の指針

“MVC Architecture”

Page 13: Android cleanarchitecture

MVC Architecture

Modelアプリケーションデータ ビジネスロジック

View Modelをレンダリングする

Controller ユーザーイベントをハンドルする

View

Model

Controller

User

usessees

Page 14: Android cleanarchitecture

Life Cycle

Data source

Frameworks

Page 15: Android cleanarchitecture

Android Frameworks

Activity Life Cycle

MVC Architecture on Android

Model

アプリケーションデータ (http, DB, memory色々) ビジネスロジック

ViewModelをレンダリングする (端末の状態を意識しながら)

Controllerユーザーイベントをハンドルする (端末状態をハンドルする) (色んなViewもハンドルする)

View

Model

Controller

User

usessees

Page 16: Android cleanarchitecture

MVCでアプリ

Page 17: Android cleanarchitecture

Sample App• GithubからFollowerを取ってくるアプリ

• Followerのrepository見れる

• 選択したFollowerはメモリにキャッシュし、2回目以降はトーストで通知する

https://github.com/tomoima525/CleanArchitectureSample

/app配下 -> MVC で実装

/cleanarchitecture配下 -> Clean Architecture で実装

Page 18: Android cleanarchitecture

private GithubApi mApi;private UserMemoryCache mUserMemoryCache;…

mApi = createApi();mUserMemoryCache = UserMemoryCache.getInstance();…public void HogeTask(…){ //ビジネスロジック… mApi.listFollowersAsync(…); mUserMemoryCache.put(…); }

Models

• Modelのデータ元は多様

• ModelとControllerのインタラクション(ビジネスロジック)がテストしにくい

Page 19: Android cleanarchitecture

public class MainActivity { @Override protected void onCreate(Bundle savedInstanceState) { // create views... }

@Override protected void onResume(){ // resume/update views when system state is resumed }

@Override protected void onPause(){ // pause views when system state is paused }}

Views

• Viewの状態管理が必要

Page 20: Android cleanarchitecture

public class MainActivity { // User events public void onClick(){} public void afterTextChanged(){}

// System events public void onSaveInstanceState(Bundle outState){} public void onRestoreInstanceState(Bundle inState){}

// View events public void gotoUserDetailActivity(Context context){}}

Controllers

• ViewとControllerがActivity上に混在している

• 画面再生成などのシステムの状態もハンドルする必要がある

Page 21: Android cleanarchitecture

MVC Architecture on Android における課題

View(の状態管理)とControllerがActivity上に混在している

ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい

Modelのデータ・ソースが多様

各層の依存関係が強い

Page 22: Android cleanarchitecture

Clean Way…?

Page 23: Android cleanarchitecture

MVC Architecture on Android における課題

View(の状態管理)とControllerがActivity上に混在している

ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい

Modelのデータ・ソースが多様

各層の依存関係が強い

Page 24: Android cleanarchitecture

MVP Architecture

Page 25: Android cleanarchitecture

MVP Architecture

• Presenterを介してModelを制御する設計

View

Presenterからの情報を レンダリングする -> 端末の状態は配慮不要になる

Presenter

- Activityから分割 - ユーザーイベントを ハンドルする - Modelの結果をViewに返すAndroid Frameworks

Activity

View

Model

Presenter

Userusessees

Page 26: Android cleanarchitecture

public interface ShowUserListView { // UI として表示したい処理を定義する void showLoading(); void hideLoading(); void showNoResultCase(); void hideNoResultCase(); void showResult(Collection<User> usersCollection);}

Views

Page 27: Android cleanarchitecture

public abstract class Presenter { // Activity LifeCycle public abstract void initialize(); public abstract void resume(); public abstract void pause(); public abstract void destroy();}

Presenters

public class ShowUserListPresenter extends Presenter { public void setShowUserListView(ShowUserListView view){ mShowUserListView = view; //Viewを監視できるようにする }

public void getFollowerList(String user){ mShowUserListView.showLoading(); // Activity側にCallbackする }}

Page 28: Android cleanarchitecture

Activitypublic class MainActivity implements ShowUserListView{

@Override public void onCreate(){ mShowUserListPresenter = new ShowUserListPresenter(…); mShowUserListPresenter.setShowUserListView(this); } @Override public void showLoading() { //実際のUI処理はActivityで実行できる! mListView.setVisibility(View.GONE); mProgress.setVisibility(View.VISIBLE); } @Override public void showResult(Collection<User> usersCollection) { mListView.setVisibility(View.VISIBLE); mUserAdapter.refresh(usersCollection); }}

Page 29: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい

Modelのデータ・ソースが多様

各層の依存関係が強い

Page 30: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい

Modelのデータ・ソースが多様

各層の依存関係が強い

Page 31: Android cleanarchitecture

Domain Layer

Page 32: Android cleanarchitecture

• Domain Layerにビジネスロジック(interactorまたはuse casesと呼ぶ)を集約する

• Modelとの処理はDomain経由で行う

• 処理は非同期で実行する

• Pure Java

Domain Layer

Domain

Android Frameworks

Activity

View

Model

Presenter

Userusessees

Page 33: Android cleanarchitecture

Domain Layer

UseCase <<abstract>> ベースとなるスレッド処理を実装

ConcreteUseCaseUseCaseを継承 ロジック処理の実行、Presenterへのコールバック

PostExecutionThread (UIThread) スレッド実行後の処理

• Domain層は大まかにUseCase, Threadにより成立

• 設計方法は多くの手法がある

Page 34: Android cleanarchitecture

public abstract class UseCase<T> { // スレッドをキュー処理できるようにする private ExecutorService mExecutorService

= Executors.newSingleThreadExecutor(); public void start(final T params) { mExecutorService.submit(new Runnable() { @Override public void run() { call(params); } }); } //スレッド内でcallされるメソッド abstract protected void call(T params);}

UseCase

https://github.com/kgmyshin/Android-archを参考

Page 35: Android cleanarchitecture

public class FollowerListUseCase extends UseCase<String> { @Override public void execute(String user, FollowerListUseCaseCallback callback){ mCallback = callback; this.start(user); // (1) ExecutorServiceのスレッド経由でcall()が呼ばれる }

@Override protected void call(String user) { // (2) Modelからデータ取得、バリデーション等のロジック処理。 // …省略 // (3) 処理後はUIThread( PostExecutionThread )にCallbackを返す mPostExecutionThread.post(() -> {mCallback.onUserListLoaded(usersCollection);}); } // UIThreadに返すCallbackのinterface interface FollowerListUseCaseCallback { onUserListLoaded(final Collection<User> usersCollection); onError(); }}

ConcreteUseCase

Page 36: Android cleanarchitecture

Presenterpublic class ShowUserListPresenter extends Presenter implements FollowerListUseCaseCallback { private FollowerListUseCase mFollowerListUseCase; public void getFollowerList(String user){ // (1) usecaseを実行 -> 別スレッドへ mFollowerListUseCase.execute(user, this); }

// (2) FollowerListUseCaseCallback経由で処理結果を受け取る @Override onUserListLoaded(final Collection<User> usersCollection){ // Viewへ結果を渡す } @Override onError(){ // error handling }}

Page 37: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

Domain Layer

Modelのデータ・ソースが多様

各層の依存関係が強い

Page 38: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

Domain Layer

Modelのデータ・ソースが多様

各層の依存関係が強い

Page 39: Android cleanarchitecture

Data Layer

Page 40: Android cleanarchitecture

Android Frameworks

Data Layer

• データをEntity(メソッドやデータ構造体のかたまり)として扱う

• Domain Layerにデータ元を意識させないためにRepository パターンで実装する

Domain

Android Frameworks

Activity

View

Data

Presenter

Userusessees

Page 41: Android cleanarchitecture

Repository Pattern• ドメイン駆動開発で利用されるデザインパターン

• UseCaseで必要なデータソースを集約したRepositoryクラスを実装する

Data

UserRepository DocsRepository XXRepository

Page 42: Android cleanarchitecture

public class UserRepository { private GithubApi mApi; // Github API用のインスタンス private UserMemoryCache mUserMemoryCache; //メモリキャッシュ用インスタンス

public UserRepository(){ mUserMemoryCache = UserMemoryCache.getInstance(); mApi = createGithubApi(); }

public void getFollowers(String user,UserListCallback callback) { mApi.listFollowersAsync( ... // Networkから取得 }

public void putUser(User user){ mUserMemoryCache.put(… // メモリにキャッシュ }}

Repository

Page 43: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

Domain Layer

Data Layer(Repository Pattern)

各層の依存関係が強い

Page 44: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

Domain Layer

Data Layer(Repository Pattern)

各層の依存関係が強い

Page 45: Android cleanarchitecture

Dependency Inversion Principle

Page 46: Android cleanarchitecture

Dependency Inversion Principle

• 依存関係逆転の原則

• 上位モジュールは下位モジュールに依存しない

例) PresenterはDomainの実装がどのようになってても影響を受けない

• interfaceを各層の間に置く

Android Frameworks

Domain

Android Frameworks

Activity

View

Data

Presenter

Userusessees

interface

interface

Page 47: Android cleanarchitecture

public interface UserRepository { // Domain層(UseCase)側はこの interfaceの仕様だけ知っていればOK void getFollowers(String userId, UserRepositoryCallback callback); void getUser(String userId, UserRepositoryCallback callback); void putUser(User user);}

Dependency Inversion

//実際の実装は interfaceを継承する public class UserRepositoryImpl implements UserRepository { @Override public void getFollowers(String userId, UserRepositoryCallback callback){ //データ取得 … } @Override public void putUser(User user) { } //…}

Page 48: Android cleanarchitecture

MVC Architecture on Android における課題

MVP Architecture

Domain Layer

Data Layer(Repository Pattern)

Dependency Inversion Principle

Page 49: Android cleanarchitecture

どう変わった?

Page 50: Android cleanarchitecture

こう変わった!

Android Frameworks

Domain

Android Frameworks

Activity

View

Data

Presenter

Userusessees

interface

interface

UIがビジネスロジックから分離して 見通しが良くなった

ビジネスロジックがAndroidのライフサイクルから分離され、テストが容易に

Modelの仕様変更に柔軟に 対応可能になった

モジュールの置き換えが 容易になった

Page 51: Android cleanarchitecture

なんか難しい…?

Page 52: Android cleanarchitecture

少しずつ手をつけると よいかも

Page 53: Android cleanarchitecture

どこから手を付けるべきか

手法 実現難易度

MVP Architecture ☆

Domain Layer ☆☆☆

Data Layer(Repository Pattern) ☆☆

Dependency Inversion Principle ☆☆

Page 54: Android cleanarchitecture

Next step…• Dependency Injection

• 簡潔に書ける

• モジュールの載せ替えが容易

Page 55: Android cleanarchitecture

Next step…• Implementing Observer Pattern

• Domain Layerの煩雑なスレッド処理, Callback地獄からの開放

参考例: https://github.com/android10/Android-CleanArchitecture

Page 56: Android cleanarchitecture

– Clean Architecture, Uncle Bob

Architecture is about Intents, not Frameworks

Page 57: Android cleanarchitecture

– Clean Architecture, Uncle Bob

設計とは(良いコードが書きたいっていう)

意志である!

フレームワークなんて関係ねぇ!(意訳)