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

Advanced MVP(refactoring MVP)

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Gorita Gorita
April 05, 2019

Advanced MVP(refactoring MVP)

MVP 패턴의 의도에 대해 다시 알아보고 해당 의도를 더 드러낼 수 있도록 리팩토링한 경험에 대해 공유하고자 합니다.

Avatar for Gorita

Gorita

April 05, 2019
Tweet

More Decks by Gorita

Other Decks in Technology

Transcript

  1. )PXUPJNQMFNFOU HPPHMFTBNQMF public interface AddEditTaskContract { interface View {…} interface

    Presenter {…} } public class AddEditTaskPresenter implements AddEditTaskContract.Presenter { private final AddEditTaskContract.View mAddTaskView; } public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View { private AddEditTaskContract.Presenter mPresenter; } $POUSBDUীࢲ7JFX৬1SFTFOUFS JOUFSGBDFܳ੿੄ $POUSBDU7JFXܳҳഅೞҊ JOUFSGBDFܳా೧QSFTFOUFS৬ాन $POUSBDU1SFTFOUFSܳҳഅೞҊ JOUFSGBDFܳా೧WJFX৬ాन
  2. .7$UP.71 XIZ   .7$7JFX $POUSPMMFS .PEFM  ৵൨ٜ঻঻૑ View

    Controller Model User input Modifies Updates xml? activity? fragment?
  3. .7$UP.71 XIZ   .7$7JFX $POUSPMMFS .PEFM  ৵൨ٜ঻঻૑ View

    Controller Model User input Modifies xml? activity? fragment?
  4. .7$UP.71 XIZ   .7$7JFX $POUSPMMFS .PEFM  ৵൨ٜ঻঻૑ View

    Controller Model User input Modifies xml? activity? fragment? Updates MVC(android)
  5. .7$UP.71 XIZ   .7$7JFX $POUSPMMFS .PEFM  ৵൨ٜ঻঻૑ View

    Controller Model User input Modifies xml? activity? fragment? Updates MVC(android)
  6. .7$UP.71 XIZ   .7$7JFX $POUSPMMFS .PEFM  ৵൨ٜ঻঻૑ 

    ࠺؀೧૑ח$POUSPMMFSਬ૑ࠁࣻоয۰਑  $POUSPMMFSо"OESPJE੄ઓࢿ੉֫ইVOJUపझ౟оয۰਑
  7. 4P -FUsT45"35  7JFX 1SFTFOUFS .PEFM੄ҙब੄ܻ࠙ܳઑӘ؊ݺഛೞѱೡࣻ੓׮  7JFX৬QSFTFOUFSܳוटೞѱ JOUFSGBDF োѾ

     "OESPJE੄ઓࢿ9੗زചVOJUపझ౟оਊ੉ೞ׮  .71ಁఢ੄੄بܳ؊૑ఆࣻ੓ب۾  WJFX QSFTFOUFS NPEFMܻಂష݂  6OJUUFTUܻܳಂష݂
  8. 7JFX 1SFTFOUFS .PEFM  t੼੼7JFX੄௏٘۝੉݆ই૓׮u View View .7$YNM .71YNM BDUJWJUZ

    GSBHNFOU BDUJWJUZ੄ઓ੤۽ੋ೧7JFX੄௏٘۝੉݆ই૗যڌѱ೧Ѿೡөਃ
  9. 7JFX 1SFTFOUFS .PEFM  WJFXחQBTTJWFೞѱ੘ࢿ೧ঠೠ׮  QBTTJWFೞѱ੘ࢿೞݶ   ߸زоמࢿ੉о੢௾VJࠗ࠙ীࢲ۽૒੉ܻ࠙ػ׮

     పझ౟оয۰਍VJ۽ࠗఠ۽૒ਸܻ࠙೧೧׼۽૒੄VOJUపझ౟оמ যڌѱQBTTJWFೞѱ੘ࢿೡࣻ੓ਸөਃ  ӓױ੸ਵ۽QBTTJWFೞѱ੘ࢿೞחߑߨਸా೧ೞաঀঌইࠁѷणפ׮
  10. 7JFXQBTTJWF ۽૒ܻ࠙ confirmButton.setOnClickListener { if (titleEditText.text.isBlank()) presenter.showMessageEmptyError() else { presenter.setInputMessage(…)

    } } confirmButton.setOnClickListener { presenter.onConfirmButtonClicked(…) }  ৉ೡ۽૒ VTFSJOQVU੹׳  VOJUపझ౟ࠛоמ  ৉ೡVTFSJOQVU੹׳  VOJUపझ౟QSFTFOUFSܳా೧оמ
  11. 7JFXQBTTJWF 1SFTFOUFSҳઑܳށۄঠೠ׮ override fun onCreate(…) { super.onCreate(…) setContentView(…) refreshButton.setOnClickListener {

    presenter.getItemList() } presenter.getItemList() } override fun onCreate(…) { super.onCreate(…) setContentView(…) refreshButton.setOnClickListener { presenter.refreshButtonClicked() } presenter.onCreate() }  ৉ೡVTFSJOQVU੹׳ ۽૒  VOJUపझ౟ ୶о۽૒ਸҊ۰ оמ  ৉ೡVTFSJOQVU੹׳  VOJUపझ౟VTFSJOQVUӝ߈ਵ۽оמ
  12. 7JFXQBTTJWF JOUFSGBDFٍীऀӝ૑݈ӝ fun onCreate() { view.showLoading() … } QSFTFOUFSܳഐ୹ೞחद੼ࡺ݅ইפۄ QSFTFOUFSী੄೧प೯غחೣࣻউীࢲب۽૒ܻ࠙

    fun showLoading() { progressBar.visibility = View.VISIBLE errorView.visibility = View.INVISIBLE guideView.visibility = View.INVISIBLE } Presenter view fun onCreate() { showLoading() … } private fun showLoading() { view.showProgressBar() view.hideErrorView() view.hideGuideView() } Presenter fun showProgressBar() { progressBar.visibility = View.VISIBLE } view ೞա੄WJFX੄࢚క߸҃ਸৈ۞ೣࣻীࢲ૓೯ؼ҃਋ ҙܻ੄য۰਑੉ߊࢤ
  13.  1SFTFOUFS  VJ۽૒  WJFX৬JOUFSBDU  .PEFMҗJOUFSBDU  .PEFM

     ࠺ૉפझبݫੋ۽૒ 7JFX 1SFTFOUFS .PEFM
  14.  1SFTFOUFS  VJ۽૒  WJFX৬JOUFSBDU  .PEFMҗJOUFSBDU  .PEFM

     ࠺ૉפझبݫੋ۽૒ ৉ೡ੉؊ݺഛ೧૓׮ ௏٘ࠂ੟ب  WJFXܳӒܻӝਤೠVJ۽૒݅Ҋ۰ 7JFX 1SFTFOUFS .PEFM
  15. .PEFMSFQPTJUPSZ VTFDBTF class GetUserBankAccount( private val repository: UserBankAccountsRepository, … )

    : SingleUseCase<UserBankAccount>(…, …) { lateinit var userBankAccountId: String override fun buildUseCaseSingle(): Single<UserBankAccount> = repository.get(userBankAccountId) } interface UserBankAccountsRepository : Repository { fun get(bankAccountId: String): Single<UserBankAccount> … } class UserBankAccountsRepository(private val context: Context) : UserBankAccountsRepository { override fun get(bankAccountId: String): Single<UserBankAccount> = context.retrofit.userBankAccountsApi .getUserBankAccount(bankAccountId) .map { UserBankAccountResponseMapper.from(it.userBankAccount) } }
  16. .PEFMSFQPTJUPSZ VTFDBTF class GetUserBankAccount( private val repository: UserBankAccountsRepository, … )

    : SingleUseCase<UserBankAccount>(…, …) { lateinit var userBankAccountId: String override fun buildUseCaseSingle(): Single<UserBankAccount> = repository.get(userBankAccountId) } interface UserBankAccountsRepository : Repository { fun get(bankAccountId: String): Single<UserBankAccount> … } class UserBankAccountsRepository(private val context: Context) : UserBankAccountsRepository { override fun get(bankAccountId: String): Single<UserBankAccount> = context.retrofit.userBankAccountsApi .getUserBankAccount(bankAccountId) .map { UserBankAccountResponseMapper.from(it.userBankAccount) } }
  17. .PEFMSFQPTJUPSZ VTFDBTF class GetUserBankAccount( private val repository: UserBankAccountsRepository, … )

    : SingleUseCase<UserBankAccount>(…, …) { lateinit var userBankAccountId: String override fun buildUseCaseSingle(): Single<UserBankAccount> = repository.get(userBankAccountId) } interface UserBankAccountsRepository : Repository { fun get(bankAccountId: String): Single<UserBankAccount> … } class UserBankAccountsRepository(private val context: Context) : UserBankAccountsRepository { override fun get(bankAccountId: String): Single<UserBankAccount> = context.retrofit.userBankAccountsApi .getUserBankAccount(bankAccountId) .map { UserBankAccountResponseMapper.from(it.userBankAccount) } }
  18. .PEFMSFQPTJUPSZ VTFDBTF class GetUserBankAccount( private val repository: UserBankAccountsRepository, … )

    : SingleUseCase<UserBankAccount>(…, …) { lateinit var userBankAccountId: String override fun buildUseCaseSingle(): Single<UserBankAccount> = repository.get(userBankAccountId) } interface UserBankAccountsRepository : Repository { fun get(bankAccountId: String): Single<UserBankAccount … } class UserBankAccountsRepository(private val context: Context) : UserBankAccountsRepository { override fun get(bankAccountId: String): Single<UserBankAccount> = context.retrofit.userBankAccountsApi .getUserBankAccount(bankAccountId) .map { UserBankAccountResponseMapper.from(it.userBankAccount) } }
  19. SFQPTJUPSZҳഅ୓ীઓ੤ class GetUserBankAccount( private val repository: UserBankAccountsRepository, … ) :

    SingleUseCase<UserBankAccount>(…, …) { lateinit var userBankAccountId: String override fun buildUseCaseSingle(): Single<UserBankAccount> = repository.get(userBankAccountId) } SFQPTJUPSZ৬VTFDBTFопп੄৉ೡਸࣻ೯ೠ׮
  20. SFQPTJUPSZҳഅ୓ীઓ੤ interface UserBankAccountsRepository : Repository { fun get(bankAccountId: String): Single<UserBankAccount>

    fun getDeleted(): Single<List<UserBankAccount>> fun getHidden(): Single<List<UserBankAccount>> … } VTFDBTFী੓যঠೞח పझ౟غযঠೞח MPHJD੉SFQPTJUPSZೣࣻղࠗীઓ੤
  21. SFQPTJUPSZҳഅ୓ীઓ੤ SFQPTJUPSZ੄ҳഅ୓੄҃਋BOESPJEBQJ੄ઓࢿਵ۽ੋ೧VOJUపझ౟оࠛоמೣ VTFDBTFী੓যঠೞח పझ౟غযঠೞח MPHJD੉SFQPTJUPSZೣࣻղࠗীઓ੤ interface UserBankAccountsRepository : Repository {

    fun get(bankAccountId: String): Single<UserBankAccount> fun getDeleted(): Single<List<UserBankAccount>> fun getHidden(): Single<List<UserBankAccount>> … } QSFTFOUFSীࢲపझ౟೧ঠغח۽૒ਸWJFX੄ೣࣻ۽ਤ੐ೞחѪҗ࠺तೠपࣻ
  22. 1SFTFOUFSীઓ੤ SFQPTJUPSZՙܻ੄Ѿ೤җؘ੉ఠоҕ੉VTFDBTFղࠗীࢲੌযթ class ConfigurationService( private val userRepository: UserRepository, private val

    userDeviceRepository: UserDeviceRepository, private val firebaseRepository: FirebaseRepository, private val organizationsRepository: OrganizationsRepository, ) : CompletableUseCase(…, …) { override fun buildUseCaseCompletable(): Completable = organizationsRepository.initialize() .andThen( Single.zip( userDeviceRepository.getCertificatedCount(), firebaseRepository.getFirebaseToken().toSingle(""), BiFunction { certificatedCount: Int, firebaseToken: String -> … } ).flatMapCompletable { userDeviceRepository.upsertUserDevice(…) } ).andThen(userRepository.syncUser()) } (PPEDBTF
  23. 1SFTFOUFSীઓ੤ SFQPTJUPSZՙܻ੄Ѿ೤җؘ੉ఠоҕ੉QSFTFOUFSীࢲੌযթ private val syncUser: SyncUser = SyncUser(userRepository) private val

    syncUserDevice: SyncUserDevice = SyncUserDevice(userDeviceRepository, firebaseRepository) private val fetchOrganizations: FetchOrganizations = FetchOrganizations(OrganizationsRepository(context)) … #BEDBTF syncUser.execute(object : DefaultCompletableObserver() { override fun onComplete() { syncUserDevice() } } private fun syncUserDevice() { syncUserDevice.execute(object : DefaultCompletableObserver() { override fun onComplete() { … } } }
  24. 1SFTFOUFSীઓ੤ SFQPTJUPSZՙܻ੄Ѿ೤җؘ੉ఠоҕ੉QSFTFOUFSীࢲੌযթ private val syncUser: SyncUser = SyncUser(userRepository) private val

    syncUserDevice: SyncUserDevice = SyncUserDevice(userDeviceRepository, firebaseRepository) private val fetchOrganizations: FetchOrganizations = FetchOrganizations(OrganizationsRepository(context)) … #BEDBTF syncUser.execute(object : DefaultCompletableObserver() { override fun onComplete() { syncUserDevice() } } private fun syncUserDevice() { syncUserDevice.execute(object : DefaultCompletableObserver() { override fun onComplete() { … } } } 1SFTFOUFS੄௏٘۝җࠂ੟بо
  25. 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF @Test fun destroy() { … } @Test fun getUserAssets()

    { … } @Test fun onAllButtonChecked() { … } @Test fun onAllButtonChecked() { … } @Test fun onDepositButtonChecked() { … } 5FTUDPEFԝԝೞѱ੘ࢿ FOUJUZ NPEFM чೞա߸҃ೞפj
  26. @Test fun destroy() { … } @Test fun getUserAssets() {

    … } @Test fun onAllButtonChecked() { … } @Test fun onAllButtonChecked() { … } @Test fun onDepositButtonChecked() { … } ❌ ❌ ❌ ݆਷పझ౟௏٘पಁ ੹ࠗࣻ੿ೞӝীח ߓࠁ׮ߓԞ੉ցޖ௼׮Ҋࢤп੉ٜٸ 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF
  27. /* @Test fun destroy() { … } @Test fun getUserAssets()

    { … } @Test fun onAllButtonChecked() { … } @Test fun onAllButtonChecked() { … } @Test fun onDepositButtonChecked() { … } */ $Jాҗܳਤ೧઱ࢳ୊ܻೞҊ 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF
  28. // TODO: apply new codes /* @Test fun destroy() {

    … } @Test fun getUserAssets() { … } @Test fun onAllButtonChecked() { … } @Test fun onAllButtonChecked() { … } @Test fun onDepositButtonChecked() { … } */ $Jాҗܳਤ೧઱ࢳ୊ܻೞҊ ݃૑݄নबਵ۽50%0௏ݭ౟ܳ୶о 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF
  29. private val mockName = "user" private val mockPhoneNumber = "01012341234"

    private val mockRequestId = "testId" ߸ࣻী੄޷হחчਸࢶ঱ೞѢա 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF పझ౟ܳਤ೧
  30. private val mockName = "user" private val mockPhoneNumber = "01012341234"

    private val mockRequestId = "testId" 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF UserExpenseTransaction( "id", Amount(1000.0, ""), "title", null, testDate, TransactionProduct(“productid", "type", "name"), TransactionCategorySummary("", "", TransactionType.EXPENSE, null, null, false), false, null, null, null, null ) ੄޷হחё୓ܳࢤࢿೠ҃೷ పझ౟ܳਤ೧ ߸ࣻী੄޷হחчਸࢶ঱ೞѢա
  31. private val mockName = "user" private val mockPhoneNumber = "01012341234"

    private val mockRequestId = "testId" 5FTUDPEF੄ਬ૑ࠁࣻ੄য۰਑DBTF UserExpenseTransaction( "id", Amount(1000.0, ""), "title", null, testDate, TransactionProduct(“productid", "type", "name"), TransactionCategorySummary("", "", TransactionType.EXPENSE, null, null, false), false, null, null, null, null ) ੄޷হחё୓ܳࢤࢿೠ҃೷ పझ౟ܳਤ೧ ߸ࣻী੄޷হחчਸࢶ঱ೞѢա DBTF੄࢚ട੉੤അ
  32. 6OJUUFTU UserExpenseTransaction( "id", Amount(1000.0, ""), "title", null, testDate, TransactionProduct(“productid", "type",

    "name"), TransactionCategorySummary("", "", TransactionType.EXPENSE, null, null, false), false, null, null, null, null ) 1SFTFOUFSחঌ೙ਃبহחࣁࠗ੿ࠁܳঌѱغҊ పझ౟ܳਤ೧ࢲ೙ਃহחࣁࠗ੿ࠁܳࣁ౴೧ঠೣ fun onButtonClicked(transaction: UserExpenseTransaction) { … }
  33. 6OJUUFTU interface UserTransaction Class UserExpenseTransaction(…) : UserTransaction fun onButtonClicked(transaction: UserTransaction)

    { … } @Mock var userTransaction: UserTransaction or var userTransaction: UserTransaction = SimpleUserTransaction() ࣁࠗ੸ੋ੿ࠁহ੉೙ਃೠ݅ఀ੄੿ࠁ పझ౟௏٘੘ࢿदীب ࠛ೙ਃೠё୓ܳࢤࢿೞ૑ঋইبغҊ ূ౭౭о߸೧بపझ౟௏٘ࣻ੿੉೙ਃহ׮