1.场景

App 的付出流程,增加多种付出办法,不同的付出办法,对应的操作不一样,有的会跳转到一个新的webview,有的会调用体系浏览器,有的会进去一个新的表单页面,等等。

而且能够增加的付出办法也是不确定的,由后台动态下发。

如下图所示:

Android 多种支付方式的优雅实现!

依据上图 ui 理一下履行流程:

  1. 点击不同的增加付出办法 item。
  2. 进入相对应的增加付出办法流程(表单页面、webview、弹框之类的)。
  3. 在第三方回调里边依据不同的付出办法履行不同的操作。
  4. 调用后台接口查询增加是否成功。
  5. 依据接口结果展现不同的成功或许失利的ui.

2.曾经的完成办法

用一个 Activity 承载,上述一切的流程都在 Activity 中。Activity 包含了列表展现、多种付出办法的完成和 ui。

伪代码如下:

class AddPaymentListActivity : AppCompatActivity(R.layout.activity_add_card) {
    private val addPaymentViewModel : AddPaymentViewModel = ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        addPaymentViewModel.checkPaymentStatusLiveData.observer(this) { isSuccess ->
            // 从后台结果判别是否增加成功
            if (isSuccess) {
                addCardSuccess(paymentType)
            } else {
                addCardFailed(paymentType)
            }
        }
    }
    private fun clickItem(paymentType: PaymentType) {
        when (paymentType) {
            PaymentType.ADD_GOOGLE_PAY -> //履行增加谷歌付出流程
            PaymentType.ADD_PAY_PEL-> //履行增加PayPel付出流程
            PaymentType.ADD_ALI_PAY-> //履行增加付出宝付出流程
            PaymentType.ADD_STRIPE-> //履行增加Stripe付出流程
        }
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (resultCode) {
            PaymentType.ADD_GOOGLE_PAY -> {
                // 依据第三方回调的结果,拿到key
                // 依据key调用后台的Api接口查询是否增加成功
            }
            PaymentType.ADD_PAY_PEL -> // 同上
            // ...
        }
    }
    private fun addCardSuccess(paymentType: PaymentType){
        when (paymentType) {
            PaymentType.ADD_GOOGLE_PAY -> // 增加对应的付出办法成功,展现成功的ui,然后履行下一步操作
            PaymentType.ADD_PAY_PEL-> // 同上
            // ...
        }
    }
    private fun addCardFailed(paymentType: PaymentType){
        when (paymentType) {
            PaymentType.ADD_GOOGLE_PAY -> // 增加对应的付出办法失利,展现失利的ui
            PaymentType.ADD_PAY_PEL-> // 同上
            // ...
        }
    }
    enum class PaymentType {
        ADD_GOOGLE_PAY, ADD_PAY_PEL, ADD_ALI_PAY, ADD_STRIPE
    }
}

虽然看起来依据 paymentType 来判别,逻辑条理也还过得去,可是实际上复杂度远远不止如此。

• 不同的付出办法跳转的页面相差很大。

• 结果的回调获取也相差很大,并不是一切的都在onActivityResult中。

• 成功和失利实际上也不能一致来处理,里边包含许多的if…else…判别。

• 假如付出办法是后台动态下发的,处理起来判别逻辑就更多了。

此外,最大的问题:扩展性问题。

当新来一种付出办法,例如微信付出之类的,改动代码就很大了,根本便是将整个Activity中的代码都要改动。能够说上面这种办法的可扩展性为零,便是简略的将代码都揉在一起。

3.优化后的代码

要想完成高内聚低耦合,最简略的便是套用常见的规划形式,回想一下,发现战略形式+简略工厂形式十分这种适合这种场景。

先看下优化后的代码:

class AddPlatformActivity : BaseActivity() {
    private var addPayPlatform: IAddPayPlatform? = null
    private fun addPlatform(payPlatform: String) {
        // 将后台回来的付出渠道字符串变成枚举类
        val platform: PayPlatform = getPayPlatform(payPlatform) ?: return
        addPayPlatform = AddPayPlatformFactory.getCurrentPlatform(this, platform)
        addPayPlatform?.getLoadingLiveData()?.observe(this@AddPlatformActivity) { showLoading ->
                if (showLoading) startLoading() else stopLoading()
            }
        addPayPlatform?.addPayPlatform(AddCardParameter(platform))
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // 将onActivityResult的回调转接到需求监听的战略类里边
        addPayPlatform?.thirdAuthenticationCallback(requestCode, resultCode, data)
    }
}

4.战略形式

意图: 界说一系列的算法,把它们一个个封装起来, 而且使它们可彼此替换。

主要处理: 在有多种算法类似的情况下,运用if…else所带来的复杂和难以保护。

何时运用: 一个体系有许多许多类,而区分它们的仅仅他们直接的行为。

怎么处理: 将这些算法封装成一个一个的类,恣意地替换。

要害代码: 完成同一个接口。

**长处: **

1、算法能够自在切换。

2、避免运用多重条件判别。

3、扩展性杰出。

缺陷

1、战略类会增多。

2、一切战略类都需求对外露出。

**运用场景: **

1、假如在一个体系里边有许多类,它们之间的区别仅在于它们的行为,那么运用战略形式能够动态地让一个目标在许多行为中选择一种行为。

2、一个体系需求动态地在几种算法中选择一种。

3、假如一个目标有许多的行为,假如不用恰当的形式,这些行为就只好运用多重的条件选择句子来完成。

5.需求完成的目标

5.1 解耦宿主 Activity

现在宿主Activity中代码太重了,包含多种付出办法完成,还有列表ui的展现,网络恳求等。

现在目标是将 Activity 中的代码拆分开来,让宿主 Activity 变得小而轻。

假如产品说新增一种付出办法,只需求改动很少的代码,就能够垂手可得的完成。

5.2 抽取成独立的模块

由于公司中有可能存在多个项目,付出模块的分层应该处于可复用的层级,今后很有可能将其封装成一个独立的 mouble,给不同的项目运用。

现在代码全在 Activity 中,今后若是抽取 mouble 的话,相当于整个需求重做。

5.3 组件黑盒

“组件黑盒”这个名词是我自己的一个界说。大致意思:

将一个 View 或许一个类进行高度封装,尽可能少的露出public办法给外部运用,自成一体。

事务方在运用时,能够直接黑盒运用某个事务组件,不必关怀其中的逻辑。

事务方只需求一个简略的操作,例如点击按钮调用办法,然后逻辑都在组件内部完成,组件内处理外部事情的一切操作,例如:Loading、恳求网络、成功或许失利。

事务方都不需求知道组件内部的操作,做到宿主和组件的隔离。

当然这种处理也是要分场景考虑的,其中一个重点便是这个组件是偏事务还是偏功用,也便是是否要将事务逻辑一致包进组件,想清楚这个问题后,才能去开发一个事务组件。 摘自xu’yi’sheng博客。xuyisheng.top/author/xuyi…

由于增加付出办法是一个偏事务的功用,我的规划思路是:

外部 Activity 点击增加对应的付出办法,将付出办法的枚举类型和付出办法有关的参数经过传递,然后不同的战略类组件履行自己的增加逻辑,再经过一层回调将第三方付出的回调从 Activity 中转接过来,每个战略类内部处理自己的回调操作,详细的战略类自己保护成功或许失利的ui。

6.详细完成

6.1 界说顶层战略接口

interface IAddPayPlatform {
    fun addPayPlatform(param: AddCardParameter)
    fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, data: Intent?)
    fun addCardFailed(message: String?)
    fun addCardSuccess()
}

6.2 通用付出参数类

open class AddCardParameter(val platform: PayPlatform)
class AddStripeParameter(val card: Card, val setPrimaryCard: Boolean, platform: PayPlatform)
    : AddCardParameter(platform = PayPlatform.Stripe)

由于有许多种增加付出办法,不同的付出办法对应的参数都不一样。

所以先创立一个通用的卡片参数基类AddCardParameter, 不同的付出办法去完成不同的详细参数。这样的话战略接口就能够只要写一个增加卡片的办法addPayPlatform(param:AddCardParameter)

6.3 Loading 的处理

由于我想完成的是黑盒组件的作用,一切增加卡片的loading也是封装在每一个战略完成类里边的。

Loading的呈现和消失这儿有几种常见的完成办法:

• 传递BaseActivity的引证,由于我的loading有关的办法是放在BaseActivity中,这种办法简略可是会耦合BaseActivity。

• 运用音讯事情总线,例如EventBus之类的,这种办法解耦强,可是音讯事情不好控制,还要增加多余的依赖库。

• 运用LiveData,在战略的通用接口中增加一个办法回来Loading的LiveData, 让宿主Activity自己去完成。

interface IAddPayPlatform {
    // ...
    fun getLoadingLiveData(): LiveData<Boolean>?
}

6.4 提取BaseAddPayStrategy

由于每一个增加卡的战略会存在许多相同的代码,这儿我抽取一个BaseAddPayStrategy来存放模板代码。

需求完成黑盒组件的作用,宿主Activity中都不需求去重视增加付出办法是不是存在网络恳求这一个进程,所以网络恳求也分装在每一个战略完成类里边。

abstract class BaseAddPayStrategy(val activity: AppCompatActivity, val platform: PayPlatform) : IAddPayPlatform {
  private val loadingLiveData = SingleLiveData<Boolean>()
  protected val startActivityIntentLiveData = SingleLiveData<Intent>()
  override fun getLoadingLiveData(): LiveData<Boolean> = loadingLiveData
  protected fun startLoading() = loadingLiveData.setValue(true)
  protected fun stopLoading() = loadingLiveData.setValue(false)
  private fun reloadWallet() {
      startLoading()
      // 增加卡片完成后,重新刷新钱包数据
  }
  override fun addCardSuccess() {
      reloadWallet()
  }
  override fun addCardFailed(message: String?) {
      stopLoading()
      if (isEWalletPlatform(platform)) showAddEWalletFailedView() else showAddPhysicalCardFailedView(message)
  }
   /**
    * 增加实体卡片失利展现ui
    */
  private fun showAddPhysicalCardFailedView(message: String?) {
       showSaveErrorDialog(activity, message)
  }
   /**
    * 增加实体卡片成功展现ui
    */
  private fun showAddPhysicalCardSuccessView() {
      showCreditCardAdded(activity) {
          activity.setResult(Activity.RESULT_OK)
          activity.finish()
      }
  }
  private fun showAddEWalletSucceedView() {
      // 增加电子钱包成功后的履行
      activity.setResult(Activity.RESULT_OK)
      activity.finish()
  }
  private fun showAddEWalletFailedView() {
      // 增加电子钱包失利后的履行
  }
  // ---默认空完成,有些付出办法不需求这些办法---
  override fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, data: Intent?) = Unit
  override fun getStartActivityIntent(): LiveData<Intent> = startActivityIntentLiveData
}

6.5 详细的战略类完成

经过传递过来的AppCompatActivity引证获取增加卡片的ViewModel实例AddPaymentViewModel,然后经过AddPaymentViewModel去调用网络恳求查询增加卡片是否成功。

class AddXXXPayStrategy(activity: AppCompatActivity) : BaseAddPayStrategy(activity, PayPlatform.XXX) {
  protected val addPaymentViewModel: AddPaymentViewModel by lazy {
      ViewModelProvider(activity).get(AddPaymentViewModel::class.java)
  }
  init {
      addPaymentViewModel.eWalletAuthorizeLiveData.observeState(activity) {
          onSuccess { addCardSuccess()}
          onError { addCardFailed(it.detailed) }
      }
  }
  override fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, result: Intent?) {
      val uri: Uri = result?.data ?: return
      if (uri.host == "www.xxx.com") {
          uri.getQueryParameter("transactionId")?.let {
              addPaymentViewModel.confirmEWalletAuthorize(platform.name, it)
          }
      }
  }
  override fun addPayPlatform(param: AddCardParameter) {
      startLoading()
      addPaymentViewModel.addXXXCard(param)
  }
}

7.简略工厂进行优化

由于我不想在Activity中去引证每一个详细的战略类,只想引证笼统接口类IAddPayPlatform, 这儿经过一个简略工厂来优化。

object AddPayPlatformFactory {

fun setCurrentPlatform(activity: AppCompatActivity, payPlatform: PayPlatform): IAddPayPlatform? {
    return when (payPlatform) {
        PayPlatform.STRIPE -> AddStripeStrategy(activity)
        PayPlatform.PAYPAL -> AddPayPalStrategy(activity)
        PayPlatform.LINEPAY -> AddLinePayStrategy(activity)
        PayPlatform.GOOGLEPAY -> AddGooglePayStrategy(activity)
        PayPlatform.RAPYD -> AddRapydStrategy(activity)
        else -> null
    }
}

}

8.再增加一种付出办法

假如再增加一种付出办法,宿主Activity中的代码都能够不要改动,只需求新建一个新的战略类,完成顶层战略接口即可。

这样,不管是删除还是新增一种付出办法,保护起来就很容易了。

战略形式的好处就清楚明了了。

今天共享到此结束,对你有帮助的话,点个赞再走呗,每日一个面试小技巧

重视大众号:Android老皮
解锁 《Android十大板块文档》 ,让学习更靠近未来实战。已形成PDF版

内容如下

1.Android车载应用开发体系学习指南(附项目实战)
2.Android Framework学习指南,助力成为体系级开发高手
3.2023最新Android中高档面试题汇总+解析,离别零offer
4.企业级Android音视频开发学习道路+项目实战(附源码)
5.Android Jetpack从入门到通晓,构建高质量UI界面
6.Flutter技能解析与实战,跨渠道首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高档Android插件化与组件化(含实战教程和源码)
9.Android 功能优化实战+360全方面功能调优
10.Android零基础入门到通晓,高手进阶之路