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

Clean Architecture in Android

Clean Architecture in Android

Droid Knights 2017에서 발표한 [Clean Architecture in Android] 발표 슬라이드입니다. (https://droidknights.github.io/2017/)

Sunghyun Hwang

March 25, 2017
Tweet

More Decks by Sunghyun Hwang

Other Decks in Programming

Transcript

  1. • 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟ •

    Ր੐হח ࢎਊ੗ ੋఠ࠭ܳ ాೠ ѐࢶ
  2. • 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟ •

    Ր੐হח ࢎਊ੗ ੋఠ࠭ܳ ాೠ ѐࢶ • ҅ࣘ ѐࢶغҊ ߸ചೞח ઁಿ
  3. • 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟ •

    Ր੐হח ࢎਊ੗ ੋఠ࠭ܳ ాೠ ѐࢶ • ҅ࣘ ѐࢶغҊ ߸ചೞח ઁಿ • ௏٘о ҅ࣘ ߄Ո׮
  4. ߸ചী ੜ ؀਽ೡ ࣻ ੓׮. = ߸ചী ٮܲ ௏٘੄ ߸҃੉

    ੸׮. = ௏٘о ੜ ܻ࠙غয੓׮. = ௏٘о ࠄ૕ী ݏѱ ࢸ҅غয੓׮.
  5. 1. Independent of Frameworks 2. Testable 3. Independent of UI

    4. Independent of Database 5. Independent of External Configuration
  6. Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬੄ ੄ઓࢿ੉ হ਺ •

    بݫੋ(࠺ૉפझ ۽૒)ীࢲ ౵ࢤغח ѐ֛ਸ ಴അ
  7. Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬੄ ੄ઓࢿ੉ হ਺ •

    بݫੋ(࠺ૉפझ ۽૒)ীࢲ ౵ࢤغח ѐ֛ਸ ಴അ • (э਷ ࢲ࠺झ) Android - iOS - ࢲߡ ݽف زੌೠ ഋక
  8. override fun getAll(): Observable<List<Bank>> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {


    use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it) 
 }
 } 
 }
 }
 }
  9. override fun getAll(): Observable<List<Bank>> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {


    use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it) 
 }
 } 
 }
 }
 }
  10. override fun getAll(): Observable<List<Bank>> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {


    use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it) 
 }
 } 
 }
 }
 }
  11. open class Bank : RealmObject() {
 @PrimaryKey open lateinit var

    id: String
 open lateinit var name: String
 open lateinit var imageUrl: String
 }
  12. override fun getAll(): Observable<List<Bank>> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {


    use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it)
 }
 } 
 }
 }
 }
  13. 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
 }
 }
 }
  14. 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
 }
 }
 }
  15. 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) } }
 }
 }
  16. data class Bank(
 @SerializedName("id") var id: String? = null,
 @SerializedName("name")

    var name: String? = null,
 @SerializedName("image_url") var imageUrl: String? = null
 ) : Response
  17. 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() }
 }
 }
  18. 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() }
 }
 }
  19. 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() }
 }
 }
  20. 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() }
 }
 }
  21. 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() }
 }
 }
  22. 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)
 }
 }
 }
 }
  23. 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)
 }
 }
 }
 }
  24. 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)
 }
 }
 }
 }
  25. 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)
 }
 }
 }
 }
  26. 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)
 }
 }
 }
 }
  27. 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)
 }
 }
  28. ۨ੉פझ౟ীࢲח • જ਷ ௏٘੄ ҳઑо જ਷ ઁಿਵ۽ ੉য૓׮Ҋ ޺ӝী ੉۞ೠ

    ইఃఫ୊ܳ ੸ਊೞ৓णפ׮. • ইఃఫ୊ بੑ੄ ૓ੑ ੢߷ࠁ׮ ߸ചী ੸਽ೡ ࣻ ੓਺ীࢲ য়ח ਬোೣ੉ ؊ ч૑׮Ҋ ࢤпೞৈ ੉۞ೠ ইఃఫ୊ܳ ੸ਊ ೞ৓णפ׮.