相信 “大多数” 伙伴看了相关设计模式
书籍、文章,一看就会,一写就 ***
在实际开发中也不多用上,或者说不知道在哪用,缺乏应用场景
,长此以往就忘记了~设计模式
在这篇文章中,我分享一下,我在Android 开发中使用到到设计模式——状态模式
,但愿你们喜欢~ide
假设咱们有这样一个需求 (文章类 App
)学习
当用户点击头像的时候ui
未登陆状态
:点击头像,则跳转到 登陆页面
在进行登陆登陆状态
:点击头像,则不采起任何操做接到需求后,这不是so easy
吗?一顿霹雳吧啦敲代码this
class MainActivity :AppCompatActivity(){
private val isLogin: Boolean by Preference("isLogin", false)
...省略部分代码
override fun initView() {
// 点击头像
mIvPortrait.setOnClickListener {
if (isLogin) {
startActivity<LoginActivity>()
}
}
}
...省略部分代码
}
复制代码
Preference
封装了SharedPreferences
,Kotlin 的委托
语法,不是本文重点,这里就不过多介绍了,若有不了解的小伙伴,能够私下百度学习~编码
经过 SharedPreferences
保存一个 用户是否登陆(isLogin)
boolean 变量,而后根据该变量,来判断是否进行登陆页面跳转。spa
逻辑相对简单,暂时看起来没啥问题
, 接下来咱们在进一步看看扩展需求
设计
当用户点击文章收藏的时候code
未登陆状态
:点击收藏,则跳转到 登陆页面
在进行登陆登陆状态
:点击收藏,则发起收藏请求,文章进行收藏class MainActivity :AppCompatActivity(){
private val isLogin: Boolean by Preference("isLogin", false)
...省略部分代码
override fun initView() {
// 点击头像
mIvPortrait.setOnClickListener {
if (isLogin) {
startActivity<LoginActivity>()
}
}
// 点击收藏按钮
mBtnCollect.setOnClickListener {
if (isLogin) {
viewModel.collect(articleId)
} else {
startActivity<LoginActivity>()
}
}
}
...省略部分代码
}
复制代码
同理,咱们在 点击收藏按钮处理逻辑上,也是根据 isLogin
变量,来进行判断是否收藏,再进行相应逻辑处理。这里咱们能够嗅获得代码的坏味道
,重复的 登陆状态
判断。对象
若咱们在增长一个 当用户点击分享
按钮进行文章分享,咱们再次添加判断逻辑,以下代码:
// 点击收藏按钮
mBtnShare.setOnClickListener {
if (isLogin) {
viewModel.share(articleId)
} else {
startActivity<LoginActivity>()
}
}
复制代码
这样在咱们 Android 项目中,会充满大量 登陆逻辑
判断,这样代码的质量大打折扣,不利于后续开发
和维护
。
若是是你,你会怎么作?
固然是 CV
代码,又不是不能用 :)
若CV 代码,那么你确定会给同事/大佬口吐芬芳: ***
废话很少说,进入正文,设计模式——状态模式
介绍:状态模式中的
行为
是由状态
来决定的,不一样的状态下有不一样的行为。
定义:当一个对象的内在状态改变时容许改变其行为,这个对象看起来像是改变来其类。
就如上文所说,当用户登陆状态下,会有收藏
、点赞
、分享
等操做行为。
若用户没有登陆,则须要跳转到 登陆页面进行 登陆,才能够进行后续操做。
而这个用户状态的改变
(未登陆-> 登陆),是在一个对象内部的,外在是无感知的。但实际行为应 状态的改变而改变。
提及来可能有点绕哈。咱们来看看实际代码演示
interface UserState {
fun collect(context: Context?, block: () -> Unit)
fun share(context: Context?, block: () -> Unit)
fun login(context: Context?)
}
复制代码
咱们先建立一个 UserState
接口类,结合上面背景,这里定义 login
、collect
、share
方法 (行为),而后咱们分别建立 登陆状态
(LoginState) 类,未登陆状态
(LogoutState)类,并实现 UserState 接口
class LoginState : UserState {
override fun collect(context: Context?, block: () -> Unit) {
// 发起收藏
block()
}
override fun share(context: Context?, block: () -> Unit) {
// 发起分享
block()
}
// 已登陆状态 无须登陆 不作任何操做
override fun login(context: Context?) {}
}
复制代码
在登陆状态下,咱们执行相应的逻辑操做,登陆状态下,无须登陆,因此这里不作任何操做
class LoginState : UserState {
// 收藏
override fun collect(context: Context?, block: () -> Unit) {
goLoginActivity(context)
}
// 分享
override fun share(context: Context?, block: () -> Unit) {
goLoginActivity(context)
}
// 登陆
override fun login(context: Context?) {
goLoginActivity(context)
}
// 跳转到登陆
private fun goLoginActivity(context: Context?) {
context?.run {
toast(getString(R.string.please_login))
startActivity<LoginActivity>()
}
}
}
复制代码
这里相信你们也已经猜到,未登陆状态下,统一跳转到 登陆页面进行登陆,在进行后续操做
而后咱们在定一个 管理状态的类 UserContext
,方便管理咱们的用户状态
object UserContext{
// 持久化存储 登陆状态
private var isLogin: Boolean by Preference(Key.LOGIN, false)
// 设置默认状态
var mState: UserState = if (isLogin) LoginState() else LogoutState()
// 收藏
fun collect(context: Context?, block: () -> Unit) {
mState.collect(context, block)
}
// 分享
fun share(context: Context?, block: () -> Unit) {
mState.share(context, block)
}
// 登陆
fun login(context: Activity?) {
mState.login(context)
}
// 切换成 登陆状态
fun setLoginState(){
// 改变 sharedPreferences isLogin值
isLogin = true
mState = LoginState()
}
// 切换成 未登陆状态
fun setLogoutState(){
// 改变 sharedPreferences isLogin值
isLogin = false
mState = LogoutState()
}
}
复制代码
UserContext
类管理
着 用户的状态,mState
变量默认初始化为 未登陆状态
,并声明切换登陆状态
、未登陆状态
切换的方法,方便后续切换状态使用,最后回到 MainActivity
中,咱们来看看 实际如何使用。
class MainActivity :AppCompatActivity(){
//private val isLogin: Boolean by Preference("isLogin", false)
...省略部分代码
override fun initView() {
// 点击头像
mIvPortrait.setOnClickListener {
// 调用 登陆
UserContext.login(this)
}
// 点击收藏按钮
mBtnCollect.setOnClickListener {
// 调用 收藏
UserContext.collect(this) {
viewModel.collect(articleId)
}
}
mBtnShare.setOnClickListener {
// 调用 分享
UserContext.share(this) {
viewModel.share(articleId)
}
}
}
...省略部分代码
}
复制代码
在 MainActivity中,咱们能够优雅
实现登陆
、收藏
、分享
功能,在也不用 if-else
判断。
至于切换状态
,在登陆成功回调中,或者是 退出登陆中,能够调用 UserContext
进行状态切换
便可。是以下代码:
// 点击退出登陆 按钮
mBtnLogout.setOnClickListener {
// 设置当前状态 为 未登陆状态
UserContext.setLogoutState()
}
复制代码
// 登陆成功 回调
private fun loginSuccess() {
// 设置当前状态为 登陆状态
UserContext.setLoginState()
}
复制代码
切换了状态,对于 MainActivity
而言,无需作任何修改,但其里面的行为
却发生了变化。
至此,这是一个完整的状态模式实现,从一开始没有使用设计模式,代码中充满了 if-else
的逻辑判断,缺乏美感
,到后面使用设计模式,先后对比,虽然 类有所增长类
,可是把烦琐
的状态,转换成结构清晰的状态类族
,从而避免重复代码,也保证了可扩展性和可维护性,用心感觉——状态模式 的魅力吧~
还不赶忙用起来? :)
说到这里,确定会有 机智
的小伙伴想到,若是我封装成一个通用方法,效果不也是同样吗?
代码以下:
object UserContext{
// 持久化存储 登陆状态
private var isLogin: Boolean by Preference(Key.LOGIN, false)
private fun execute(context: Context, block: () -> Unit) {
if (isLogin) {
block()
} else {
startActivity<LoginActivity>()
}
}
}
复制代码
乍一看,好像是这么回事,效果同样,并且少建立一些类。
可是仅仅局限于两种
用户状态—— 登陆
、未登陆
。
假若,在增长一个用户状态,一个只有管理员
才能分享的功能呢?
代码最终会变成以下:
object UserContext{
// 持久化存储 登陆状态
private var isLogin: Boolean by Preference(Key.LOGIN, false)
private fun execute(context: Context, block: () -> Unit) {
if (isLogin) {
block()
} else {
startActivity<LoginActivity>()
}
}
private fun adminExecute(context: Context, block: () -> Unit) {
if (isLogin) {
if (isAdmin) {
block()
return
}
toast("没法分享,没有对应权限")
} else {
startActivity<LoginActivity>()
}
}
}
复制代码
代码上又增长多一条 if-else
,我好像嗅到了代码的坏味道
:)
反观状态模式,不只消除了 if-else
逻辑,结构更为清晰,也使得这模块更加灵活
只须要在新建一个状态类,AdminState
便可~
孰好孰坏? 一看便知~