Share
Embed
Clean Architecture in Android
Droid Knights 2017에서 발표한 [Clean Architecture in Android] 발표 슬라이드입니다. (https://droidknights.github.io/2017/)
Sunghyun Hwang
March 25, 2017
Transcript
79 slides
-
• 2015֙ 12ਘ द • ف ߣ ӏݽ সؘ •
Րহח ࢎਊ ੋఠ࠭ܳ ాೠ ѐࢶ -
• 2015֙ 12ਘ द • ف ߣ ӏݽ সؘ •
Րহח ࢎਊ ੋఠ࠭ܳ ాೠ ѐࢶ • ҅ࣘ ѐࢶغҊ ߸ചೞח ઁಿ -
• 2015֙ 12ਘ द • ف ߣ ӏݽ সؘ •
Րহח ࢎਊ ੋఠ࠭ܳ ాೠ ѐࢶ • ҅ࣘ ѐࢶغҊ ߸ചೞח ઁಿ • ٘о ҅ࣘ ߄Ո -
߸ചী ੜ ೡ ࣻ . = ߸ചী ٮܲ ٘ ߸҃
. = ٘о ੜ ܻ࠙غয. -
߸ചী ੜ ೡ ࣻ . = ߸ചী ٮܲ ٘ ߸҃
. = ٘о ੜ ܻ࠙غয. = ٘о ࠄী ݏѱ ࢸ҅غয. -
ࠄ = { بݫੋ ࠄ, ѐߊ ҳઑ ࢚ ࠄ, উ٘۽٘
ۖಬ ࠄ, ... } -
ࠄ = { بݫੋ ࠄ, ѐߊ ҳઑ ࢚ ࠄ, উ٘۽٘
ۖಬ ࠄ, ... } -
ࠄ = { بݫੋ ࠄ, ѐߊ ҳઑ ࢚ ࠄ, উ٘۽٘
ۖಬ ࠄ, ... } -
ࠄ = { بݫੋ ࠄ, ѐߊ ҳઑ ࢚ ࠄ, উ٘۽٘
ҳઑ ࢚ ࠄ, ... } -
ࠄ = { بݫੋ ࠄ, ѐߊ ҳઑ ࢚ ࠄ, উ٘۽٘
ҳઑ ࢚ ࠄ, ... } -
1. Independent of Frameworks 2. Testable 3. Independent of UI
4. Independent of Database -
1. Independent of Frameworks 2. Testable 3. Independent of UI
4. Independent of Database 5. Independent of External Configuration -
Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬ ઓࢿ হ •
بݫੋ(࠺ૉפझ ۽)ীࢲ ࢤغח ѐ֛ਸ അ -
Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬ ઓࢿ হ •
بݫੋ(࠺ૉפझ ۽)ীࢲ ࢤغח ѐ֛ਸ അ • (э ࢲ࠺झ) Android - iOS - ࢲߡ ݽف زੌೠ ഋక -
class TransactionCategory( val id: String, val name: String, val transactionType:
TransactionType, val priority: Int ) : Entity -
Domain • ࣽࣻೠ Java(Kotlin) ݽٕ • Use Case • Repository
Interface -
class GetBanks(val repository: BanksRepository) : UseCase<List<Bank>>() { override fun buildUseCaseObservable():
Observable<List<Bank>> { return repository.getAll() } } -
class GetBanks(val repository: BanksRepository) : UseCase<List<Bank>>() { override fun buildUseCaseObservable():
Observable<List<Bank>> { return repository.getAll() } } -
class GetBanks(val repository: BanksRepository) : UseCase<List<Bank>>() { override fun buildUseCaseObservable():
Observable<List<Bank>> { return repository.getAll() } } -
Data • Repository पઁ ҳഅ • Data Source ઓࢿ •
Android ઓࢿ -
override fun getAll(): Observable<List<Bank>> { return Observable.fromCallable { with(Realm.getDefaultInstance()) {
use { where(BankModel::class.java).findAll().map { BankEntityMapper.fromRealmObject(it) } } } } } -
override fun getAll(): Observable<List<Bank>> { return Observable.fromCallable { with(Realm.getDefaultInstance()) {
use { where(BankModel::class.java).findAll().map { BankEntityMapper.fromRealmObject(it) } } } } } -
override fun getAll(): Observable<List<Bank>> { return Observable.fromCallable { with(Realm.getDefaultInstance()) {
use { where(BankModel::class.java).findAll().map { BankEntityMapper.fromRealmObject(it) } } } } } -
open class Bank : RealmObject() { @PrimaryKey open lateinit var
id: String open lateinit var name: String open lateinit var imageUrl: String } -
override fun getAll(): Observable<List<Bank>> { return Observable.fromCallable { with(Realm.getDefaultInstance()) {
use { where(BankModel::class.java).findAll().map { BankEntityMapper.fromRealmObject(it) } } } } } -
object BankEntityMapper : EntityMapper<BankModel, Bank> { override fun fromRealmObject(obj: BankModel):
Bank { return Bank( obj.id, obj.name, obj.imageUrl ) } override fun toRealmObject(entity: Bank): BankModel { return BankModel().apply { id = entity.id name = entity.name imageUrl = entity.imageUrl } } } -
object BankEntityMapper : EntityMapper<BankModel, Bank> { override fun fromRealmObject(obj: BankModel):
Bank { return Bank( obj.id, obj.name, obj.imageUrl ) } override fun toRealmObject(entity: Bank): BankModel { return BankModel().apply { id = entity.id name = entity.name imageUrl = entity.imageUrl } } } -
class BanksNetworkRepository(private val context: Context) : BanksRepository { override fun
getAll(): Observable<List<Bank>> { return context.retrofit.banksApi.getSupportedBanks() .map(GetSupportedBanksResponse::banks) .map { it.map { BankEntityMapper.fromNetworkResponseModel(it) } } } } -
data class Bank( @SerializedName("id") var id: String? = null, @SerializedName("name")
var name: String? = null, @SerializedName("image_url") var imageUrl: String? = null ) : Response -
class OrganizationFragment : BaseFragment<OrganizationPresenter, OrganizationView>(), OrganizationView { override val presenter:
OrganizationPresenter by lazy { OrganizationPresenter(this, act) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_signup_organization, container, false) } override fun onViewCreated(view: View?, saved: Bundle?) { super.onViewCreated(view, saved) presenter.onViewCreated() nextBtn.onClick { presenter.onNextButtonClicked() } } } -
class OrganizationFragment : BaseFragment<OrganizationPresenter, OrganizationView>(), OrganizationView { override val presenter:
OrganizationPresenter by lazy { OrganizationPresenter(this, act) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_signup_organization, container, false) } override fun onViewCreated(view: View?, saved: Bundle?) { super.onViewCreated(view, saved) presenter.onViewCreated() nextBtn.onClick { presenter.onNextButtonClicked() } } } -
class OrganizationFragment : BaseFragment<OrganizationPresenter, OrganizationView>(), OrganizationView { override val presenter:
OrganizationPresenter by lazy { OrganizationPresenter(this, act) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_signup_organization, container, false) } override fun onViewCreated(view: View?, saved: Bundle?) { super.onViewCreated(view, saved) presenter.onViewCreated() nextBtn.onClick { presenter.onNextButtonClicked() } } } -
class OrganizationFragment : BaseFragment<OrganizationPresenter, OrganizationView>(), OrganizationView { override val presenter:
OrganizationPresenter by lazy { OrganizationPresenter(this, act) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_signup_organization, container, false) } override fun onViewCreated(view: View?, saved: Bundle?) { super.onViewCreated(view, saved) presenter.onViewCreated() nextBtn.onClick { presenter.onNextButtonClicked() } } } -
class OrganizationFragment : BaseFragment<OrganizationPresenter, OrganizationView>(), OrganizationView { override val presenter:
OrganizationPresenter by lazy { OrganizationPresenter(this, act) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_signup_organization, container, false) } override fun onViewCreated(view: View?, saved: Bundle?) { super.onViewCreated(view, saved) presenter.onViewCreated() nextBtn.onClick { presenter.onNextButtonClicked() } } } -
class OrganizationPresenter( view: OrganizationView, context: Context, private val getBanks: GetBanks
= GetBanks(BanksNetworkRepository(context)) ) : Presenter<OrganizationView>(view) { override fun onDestroy() { getBanks.unsubscribe() } fun onViewCreated() { getBanks.execute(object : DefaultSubscriber<List<Bank>>() { override fun onNext(t: List<Bank>) { view.showBanks(t) } }) } fun onNextButtonClicked() { with(view.getSelectedOrganizations()) { if (isNotEmpty()) { view.moveToAddCertificatePage(this) } } } } -
class OrganizationPresenter( view: OrganizationView, context: Context, private val getBanks: GetBanks
= GetBanks(BanksNetworkRepository(context)) ) : Presenter<OrganizationView>(view) { override fun onDestroy() { getBanks.unsubscribe() } fun onViewCreated() { getBanks.execute(object : DefaultSubscriber<List<Bank>>() { override fun onNext(t: List<Bank>) { view.showBanks(t) } }) } fun onNextButtonClicked() { with(view.getSelectedOrganizations()) { if (isNotEmpty()) { view.moveToAddCertificatePage(this) } } } } -
class OrganizationPresenter( view: OrganizationView, context: Context, private val getBanks: GetBanks
= GetBanks(BanksNetworkRepository(context)) ) : Presenter<OrganizationView>(view) { override fun onDestroy() { getBanks.unsubscribe() } fun onViewCreated() { getBanks.execute(object : DefaultSubscriber<List<Bank>>() { override fun onNext(t: List<Bank>) { view.showBanks(t) } }) } fun onNextButtonClicked() { with(view.getSelectedOrganizations()) { if (isNotEmpty()) { view.moveToAddCertificatePage(this) } } } } -
class OrganizationPresenter( view: OrganizationView, context: Context, private val getBanks: GetBanks
= GetBanks(BanksNetworkRepository(context)) ) : Presenter<OrganizationView>(view) { override fun onDestroy() { getBanks.unsubscribe() } fun onViewCreated() { getBanks.execute(object : DefaultSubscriber<List<Bank>>() { override fun onNext(t: List<Bank>) { view.showBanks(t) } }) } fun onNextButtonClicked() { with(view.getSelectedOrganizations()) { if (isNotEmpty()) { view.moveToAddCertificatePage(this) } } } } -
class OrganizationPresenter( view: OrganizationView, context: Context, private val getBanks: GetBanks
= GetBanks(BanksNetworkRepository(context)) ) : Presenter<OrganizationView>(view) { override fun onDestroy() { getBanks.unsubscribe() } fun onViewCreated() { getBanks.execute(object : DefaultSubscriber<List<Bank>>() { override fun onNext(t: List<Bank>) { view.showBanks(t) } }) } fun onNextButtonClicked() { with(view.getSelectedOrganizations()) { if (isNotEmpty()) { view.moveToAddCertificatePage(this) } } } } -
data class Amount( var amount: Long, var currency: Currency )
: Parcelable { companion object { @JvmField val CREATOR: Parcelable.Creator<Amount> = object : Parcelable.Creator<Amount> { override fun createFromParcel(source: Parcel): Amount = Amount(source) override fun newArray(size: Int): Array<Amount?> = arrayOfNulls(size) } } constructor(valueObject: AmountValueObject) : this( valueObject.amount, Currency.from(valueObject.currency) ) constructor(source: Parcel) : this( source.readLong(), Currency.from(source.readString()) ) override fun describeContents() = 0 override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.writeLong(amount) dest?.writeString(currency.code) } } -
ۨפझীࢲח • જ ٘ ҳઑо જ ઁಿਵ۽ যҊ ӝী ۞ೠ
ইఃఫܳ ਊೞणפ. -
ۨפझীࢲח • જ ٘ ҳઑо જ ઁಿਵ۽ যҊ ӝী ۞ೠ
ইఃఫܳ ਊೞणפ. • ইఃఫ بੑ ੑ ߷ࠁ ߸ചী ೡ ࣻ ীࢲ য়ח ਬোೣ ؊ чҊ ࢤпೞৈ ۞ೠ ইఃఫܳ ਊ ೞणפ. -
ߛ࢟۞٘ܳ ݅٘ח ۨפझীࢲ ൞ ৬ ೣԋೡ উ٘۽٘ ূפয ٜ࠙ਸ
Ҋ णפ. Kotlin, RxJava, Retrofit, Glide, Realm