携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情
前言
平常我们在开发应用的过程中我们大多数情况下都是 if(){} 。稍微复杂一点的条件我们也可以使用 if(a && b){} 或者 if(a || b){} 。
当a和b的判断条件都满足,或者只有一个条件满足,则运行到制定逻辑。
那么如果现实的场景是这样的,判断条件很多,并且灵活的变换,那么我们这么硬编码写死的情况会不会改起来很麻烦呢?
以之前遇到的一个情况为例,场景如下:
比如我需要申请一个工作,那么在调用接口之后我需要校验一些条件
- 是否有新冠报告
- 工作时间是否符合
- 是否是VIP客户
- 用户的国籍
- 用户的护照状态
- 身份证号码判断是否是学生
- 年龄是否满足工作需求
- 语言是否满足工作需求
- 性别是否满足工作需求
- 是否已经晚上了详细信息
- 是否需要卫生许可证
- 是需要完成了在线培训
- 工作是否需要押金
- 是否是自营工作
- 是否是Vip顾客等
- 等等
多的不说,假如就只有就15种状态判断,我们if else if 写完了,好了,现在逻辑变了,只是第一个条件变了,现在不需要新冠报告了,我们就需要修改if else的顺序。如果是并且的逻辑变了,或者的逻辑变了,那么也要到处找if esle的逻辑去修改。
这种情况下,有没有一种可能,我们把逻辑判断封装为一个对象,然后通过一个执行器来执行判断的逻辑,就可以很方便的修改判断的逻辑的变动。
我们之前讲过策略模式,我们把判断的逻辑封装为接口,通过实现不同的接口完成对应逻辑的判断,然后再执行器中执行这些逻辑。
基类的封装
定义一个策略接口
interface BaseRule<T> {
fun execute(rule: T): RuleResult
}
而我们的规则执行器就可以定义一些策略的执行规则
class RuleExecute<T>(private val check: T, private val hashMap: MutableMap<String, List<BaseRule<T>>>) {
companion object {
@JvmStatic
fun <T> create(check: T): Builder<T> {
return Builder(check)
}
private const val AND = "&&"
private const val OR = "||"
}
// 构建者模式。
open class Builder<T>(private val check: T) {
private val hashMap: MutableMap<String, List<BaseRule<T>>> = HashMap()
var indexSuffix = 0
//并且的逻辑
fun and(ruleList: List<BaseRule<T>>): Builder<T> {
val key = AND + (indexSuffix++).toString()
hashMap[key] = ruleList
return this
}
//或者的逻辑
fun or(ruleList: List<BaseRule<T>>): Builder<T> {
val key = OR + (indexSuffix++).toString()
hashMap[key] = ruleList
return this
}
fun build(): RuleExecute<T> {
return RuleExecute(check, hashMap)
}
}
//执行任务
fun execute(): RuleResult {
for ((key, ruleList) in hashMap) {
when (key.substring(0, 2)) {
AND -> {
val result = and(check, ruleList)
if (!result.success) {
return result
}
}
OR -> {
val result = or(check, ruleList)
if (!result.success) {
return result
}
}
}
}
return RuleResult(true)
}
private fun and(check: T, ruleList: List<BaseRule<T>>): RuleResult {
for (rule in ruleList) {
val execute = rule.execute(check)
if (!execute.success) {
//失败一次就要return
return execute
}
}
// 全部匹配成功,返回 true
return RuleResult(true)
}
private fun or(check: T, ruleList: List<BaseRule<T>>): RuleResult {
val stringBuilder = StringBuilder()
for (rule in ruleList) {
val execute = rule.execute(check)
if (execute.success) {
// 从前往后遍历,只要一个满足条件就return
return RuleResult(true)
} else {
stringBuilder.append(execute.message)
}
}
// 一个都匹配不到,才返回false
val result = RuleResult(false, stringBuilder.toString())
stringBuilder.clear()
return result
}
}
内部使用构建者模式构建对象,支持多次的and 和 or。整体的流程其实也是一个and,当一个执行者方法走完如果有错误就不会继续执行了。
其中自定义返回的结果对象为
data class RuleResult(val success: Boolean, val message: String = "")
T则是我们的泛型入参,不同的场景使用不同的类型。
定义不同的判断器
下面我们开始定义执行场景下的一些判断逻辑。
假如我们的逻辑如下:(伪代码)
if(hasCovidTest){
if(nationality || visa){
if(!isVip && fillProfile){
if(gender && language && age){
if(disposit && isSelfProvide){
if(isTrained){
applyJob()
}
}
}else{
toast("xxx)
}
}else{
gotoProfilePage()
}
}else{
showErrorPopuowindow()
}
}else{
showDialogTips()
}
我们使用对象封装的方式来定义一些规则器:
先定义一个校验对象,一般由服务器返回
data class JobCheck(
//人物要求
val nric: String,
val visa: String,
val nationality: String,
val age: Float,
val workingYear: Float,
val isFillProfile: Boolean,
val hasCovidTest: Boolean,
//工作要求
val language: String,
val gender: Int,
val deposit: String,
val isSelfProvide: Boolean,
val isTrained: Boolean,
)
比如我们先判断是否有新冠检测报告,那么我们定义一个规则对象,由于失败要弹窗,所以我们定义一个失败回调。
class COVIDRule(private val callback: () -> Unit) : BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (rule.hasCovidTest) {
RuleResult(true)
} else {
callback()
RuleResult(false, "你没有新冠检测报告")
}
}
}
如果不想要回调,我们则可以很简单的定义一些规则:
class AgeRule : BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (rule.age > 16 && rule.age < 50) {
RuleResult(true)
} else {
RuleResult(false, "年龄不满足")
}
}
}
class FillProfileRule : BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (rule.isFillProfile) {
RuleResult(true)
} else {
RuleResult(false, "请完善用户详细信息")
}
}
}
class GenderRule(private val requirGender: Int) : BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (requirGender == 2) {
RuleResult(true)
} else {
RuleResult(
rule.gender == requirGender,
if (rule.gender == requirGender) "" else "性别不符合此工作"
)
}
}
}
class LanguageRule(private val requirLanguages: List<String>) :BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (requirLanguages.contains(rule.language)) {
RuleResult(true)
} else {
RuleResult(false, "语言不符合此工作")
}
}
}
class NationalityRule(private val requirNationalitys: List<String>) : BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (requirNationalitys.contains(rule.nationality)) {
RuleResult(true)
} else {
RuleResult(false, "国籍不符合此工作")
}
}
}
class VisaRule : BaseRule<JobCheck> {
override fun execute(rule: JobCheck): RuleResult {
return if (rule.visa.lowercase() == "Singapore".lowercase()) {
RuleResult(true)
} else {
RuleResult(false, "签证不满足工作需求")
}
}
}
当然这都是Demo级别的,我们做的最简单的示例,真实逻辑是需要在里面判断自定义的逻辑的,比如 age 要求18,19, 20 然后 25 -35 然后小于50岁,等等这些都需要在 AgeRule 对象中精准的校验。根据工作的具体要求然后做具体的比对。
使用
在使用的使用我们就需要先封装好检测的对象,一般是由服务器返回,和工作的要求等等信息的一些组合。
我们这里Demo就最简单的new一个对象 然后我们初始化
val jobCheck = JobCheck(
"S9876543A", "Singapore", "Singapore", 20F,
3.5F, false, false,
"Chinese", 1, "25", true,
false
)
然后我们初始化制定的一些规则对象,使用规则执行器来执行。
val covidRule = COVIDRule {
//如果不满足新冠,首先就被排除了
//如果没有新冠 - 弹窗提示他
FangIOSDialog(mActivity).apply {
setTitle("你没有新冠表单")
setMessage("老哥你快去做核酸吧,老哥你快去做核酸吧,老哥你快去做核酸吧")
setPositiveButton("好的") {
dismiss()
}
setNegativeButton("就不去") {
dismiss()
}
show()
}
} //false
val ageRule = AgeRule() //true
val fillProfileRule = FillProfileRule() //true
val genderRule = GenderRule(2) //false
val languageRule = LanguageRule(listOf("Chinese", "English")) //true
val nationalityRule = NationalityRule(listOf("China", "Malaysia")) //true
val visaRule = VisaRule() //true
//构建规则执行器
val result = RuleExecute.create(jobCheck)
.and(listOf(covidRule))
.or(listOf(nationalityRule, visaRule))
.and(listOf(ageRule, fillProfileRule, genderRule, languageRule))
.build()
.execute()
YYLogUtils.w("执行规则器的结果:$result")
可以看到我们先添加的新冠校验,直接就不通过,后面的就不会走,直接走到else中弹窗的逻辑
那么我们手动的把新冠测试通过
jobCheck.hasCovidTest = true
在运行看看:
第一个and 第二个or 都通过了,走到第三个and中的用户信息没有完善,如果需要去跳转到用户信息页面,那么我们和新冠的Rule一样,在 FillProfileRule 的规则中加入失败的回调即可。
如果想在某一个节点的成功之后做一些别的逻辑,也可以在指定的Rule或and方法加上一些成功的回调,这些都不难,大家如果有兴趣也可以自行扩展。
总结
我们通过策略模式加上构建者模式实现的封装框架。如果在一些判断条件很多并且可能随时变化的一些场景中,这样的封装是很实用的,修改起来也是非常的快捷。
在一些购物商场的一些场景中也会用到,判断用户和商品,商家,平台的一些活动,折扣等一些较为复杂的判断场景,使用这样的封装会更加方便一点。
本文全部代码都已全部贴出,如果大家有兴趣也可以查看源码自取。
好了,本期内容如讲的不到位或错漏的地方,希望同学们可以指出交流。
如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。
Ok,这一期就此完结。