用Kotlin 一步步抄作业写一个Redux

news2024/11/15 4:42:35

前言

我抄的作业

完全理解 redux(从零实现一个 redux) · Issue #22 · brickspert/blog · GitHub

一.架构设计模式

1. mvc>mvp>mvvm->mvi

2.Redux实现类Mvi

Android Mvi 与Redux对比,思想一致,单向数据流,单一数据源 

二.Redux

redux 是一个状态管理器

  1.简单的状态管理

  redux 是一个状态管理器,那什么是状态呢?状态就是数据,比如计数器中的 count。

data class State(var count:Int=0) val state=State()

  我们来使用下状态

Log.d("redux",state.count.toString())

我们来修改下状态

state.count=2

以上就是状态的使用和修改,(很简单这就是redux核心,接下来往下慢慢拓展)

  当然上面的有一个很明显的问题:修改 count 之后,使用 count 的地方不能收到通知。我们可以使用发布-订阅模式来解决这个问题。

val state=State(1)
val listeners= mutableListOf<StateLinsenter>()

/*订阅*/
fun subscribe(listener:StateLinsenter) {
    listeners.add(listener)
}

fun changeCount(count:Int) {
    state.count = count;
    /*当 count 改变的时候,我们要去通知所有的订阅者*/
    listeners.forEach {
        it.invoke()
    }
}

  我们来尝试使用下这个简单的计数状态管理器

 
/*来订阅一下,当 count 改变的时候,我要实时输出新的值*/
subscribe{
    Log.d("redux",state.count.toString());
}

/*我们来修改下 state,当然我们不能直接去改 state 了,我们要通过 changeCount 来修改*/
changeCount(2);
changeCount(3);
changeCount(4);

现在我们可以看到,我们修改 count 的时候,会输出相应的 count 值。

现在有两个新的问题摆在我们面前

  • 这个状态管理器只能管理 count,不通用

  • 公共的代码要封装起来

  我们尝试来解决这个问题,把公共的代码封装起来

 
data class Counter(var count:Int)
data class Info(var name:String)
data class State(val counter: Counter?=null,val info: Info?=null)

class Store(val subscribe: (listener: StateLinsenter)->Unit,val changeState: (state:State)->Unit,state:State)

val createStore = fun(initState:State):Store{
    var mState=initState
    val listeners= mutableListOf<()->Unit>()
    /*订阅*/
    val subscribe = fun(listener: ()->Unit) {
        listeners.add(listener)
    }

    val changeState= fun(state:State) {
        mState=state
        /*当 count 改变的时候,我们要去通知所有的订阅者*/
        listeners.forEach {
            it.invoke()
        }
    }
    
    return Store(subscribe,changeState, mState)
}

进一步简化

 
typealias StateLinsenter=()->Unit
typealias Subscribe=(listener: StateLinsenter)->Unit
typealias ChangeState=(state:State)->Unit

data class Counter(var count:Int)
data class Info(var name:String)
data class State(val counter: Counter?=null,val info: Info?=null)

class Store(val subscribe: Subscribe,val changeState:ChangeState,state:State)

val createStore = fun(initState:State):Store{
    var mState=initState
    val listeners= mutableListOf<StateLinsenter>()
    /*订阅*/
    val subscribe = fun(listener: StateLinsenter) {
        listeners.add(listener)
    }

    val changeState= fun(state:State) {
        mState=state
        /*当 count 改变的时候,我们要去通知所有的订阅者*/
        listeners.forEach {
            it.invoke()
        }
    }
    
    return Store(subscribe,changeState, mState)

}

我们来使用这个状态管理器管理多个状态 counter 和 info 试试

 
val initState =State(Counter(1) , Info("gan"))
val store = createStore(initState);

store.subscribe{
    val counter = store.state.counter
    Log.d("redux","Counter.count=${counter.count}");
}
store.subscribe{
    val info = store.state.info
    Log.d("redux","Info.name=${info.name}");
}

store.changeState(store.state.apply { 
    counter.count=2
})
store.changeState(store.state.apply {
    info.name="redux"
})

  到这里就是简单的状态管理器。

  这里需要理解的是 createStore,提供了 changeStategetStatesubscribe 三个能力。

  2.有计划的状态管理器

  用上面的状态管理器来实现一个自增,自减的计数器

 
val initState = State(counter = Counter(1))
val store = createStore(initState);

store.subscribe{
    val counter = store.state.counter
    Log.d("redux","Counter.count=${counter?.count}");
}
/*自增*/
store.changeState(store.state.apply {
    counter?.count=counter?.count?:0+1
})
/*自减*/
store.changeState(store.state.apply {
    counter?.count=counter?.count?:0-1
})
/*我想随便改*/
store.changeState(store.state.apply {
    counter?.count=100000
})

  上面可以看到,修改count不仅可以自增自减,同时可以设置成任意数,如果限制只能自增或者自减呢,分步进行

  1. 制定一个 state 修改计划,告诉 store,我的修改计划是什么。

  2. 修改 store.changeState 方法,告诉它修改 state 的时候,按照我们的计划修改。

  我们来设置一个 plan 函数,接收现在的 state,和一个 action,返回经过改变后的新的 state。

 

typealias Plan=(state: State, action: Action)->State
val plan = fun(state: State, action: Action): State {
    when (action.type) {
        "INCREMENT" ->
            return state.apply {
                counter?.count = counter?.count ?: 0 + 1
            }
        "DECREMENT" ->
            return state.apply {
                counter?.count = counter?.count ?: 0 - 1
            }
        else -> return state
    }
}


typealias StateLinsenter = () -> Unit
typealias Subscribe = (listener: StateLinsenter) -> Unit
typealias ChangeState = (action: Action) -> Unit


class Store(val subscribe: Subscribe, val changeState: ChangeState, val state: State)
class Action(val type: String,val data:Any?=null)

val createStore = fun(plan:Plan,initState: State): Store {
    var mState = initState
    val mplan=plan
    val listeners = mutableListOf<StateLinsenter>()
    /*订阅*/
    val subscribe = fun(listener: StateLinsenter) {
        listeners.add(listener)
    }

    val changeState = fun(action: Action) {
        mState = mplan(mState,action)
        /*当 count 改变的时候,我们要去通知所有的订阅者*/
        listeners.forEach {
            it()
        }
    }

    return Store(subscribe, changeState, mState)

}

接下来用新的plan实现自增自减

 
val plan = fun(state: State, action: Action): State {
    when (action.type) {
        "INCREMENT" ->
            return state.apply {
                counter?.count = counter?.count ?: 0 + 1
            }
        "DECREMENT" ->
            return state.apply {
                counter?.count = counter?.count ?: 0 - 1
            }
        else -> return state
    }
}

val initState = State(counter = Counter(1))

/*把定义好的plan函数传入*/
val store = createStore(plan,initState)
store.subscribe {
    val counter = store.state.counter
    Log.d("redux", "Counter.count=${counter?.count}");
}
/*自增*/
store.changeState(Action("INCREMENT"))
/*自减*/
store.changeState(Action("DECREMENT"))
/*我想随便改 计划外的修改是无效的!*/
store.changeState(Action("abc"))

以上实现了一个有计划的状态管理器

  为了靠近redux,我们给 plan 和 changeState plan 改成 reducer,changeState 改成 dispatch!

 
typealias StateLinsenter = () -> Unit
typealias Subscribe = (listener: StateLinsenter) -> Unit
typealias Dispatch = (action: Action) -> Unit
typealias Reducer=(state: State, action: Action)->State

class Store(val subscribe: Subscribe, val dispatch: Dispatch, val state: State)
class Action(val type: String,val data:Any?=null)


val createStore = fun(plan:Reducer,initState: State): Store {
    var mState = initState
    val mplan=plan
    val listeners = mutableListOf<StateLinsenter>()
    /*订阅*/
    val subscribe = fun(listener: StateLinsenter) {
        listeners.add(listener)
    }

    val dispatch = fun(action: Action) {
        mState = mplan(mState,action)
        /*当 count 改变的时候,我们要去通知所有的订阅者*/
        listeners.forEach {
            it()
        }
    }

    return Store(subscribe, dispatch, mState)

}

3.多文件协作

reducer 的拆分和合并

我们知道 Reducer 是一个计划函数,接收老的 state,按计划返回新的 state,如果我们项目中,有大量的 state,每个 state 都需要计划函数,不可能全部写一起吧

所有的计划写在一个 reducer 函数里面,会导致 reducer 函数及其庞大复杂,我们肯定会按组件维度来拆分出很多个 reducer 函数,然后就需要通过一个函数来把他们合并起来。

上面的reducer只是处理了Counter,那如果同时处理Info呢,就需要两个reducer

 
val counterReducer = fun(state: State, action: Action): State {
    when (action.type) {
        "INCREMENT" ->
            return state.apply {
                counter?.count = counter?.count ?: 0 + 1
            }
        "DECREMENT" ->
            return state.apply {
                counter?.count = counter?.count ?: 0 - 1
            }
        else -> return state
    }
}

val InfoReducer= fun(state: State, action: Action): State {
    when (action.type) {
        "SET_NAME" ->
            return state.apply {
                info?.name = action?.data as String
            }
        else -> return state
    }
}

那写个combineReducers 函数来把多个 reducer 函数合并成一个 reducer 函数,输入值需要是一个list

list生成方式随便写

 
/**
 * 用于合并Reducer
 */
fun MutableList<Reducer>.combineReducers(): Reducer {
    return { state: State, action: Action ->
        this.fold(initial = state) { acc: State, func: Reducer ->
            return@fold func(acc, action)
        }
    }
}

组合后验证

 
val counterReducer = fun(state: State, action: Action): State {
    when (action.type) {
        "INCREMENT" ->
            return state.apply {
                counter?.count = (counter?.count ?: 0) + 1
            }
        "DECREMENT" ->
            return state.apply {
                counter?.count = (counter?.count ?: 0 )- 1
            }
        else -> return state
    }
}

val InfoReducer = fun(state: State, action: Action): State {
    when (action.type) {
        "SET_NAME" ->
            return state.apply {
                info?.name = action?.data as String
            }
        else -> return state
    }
}


val initState = State(counter = Counter(1), info = Info("name"))

/*把定义好的plan函数传入*/
val store = createStore(
    counterReducer.append(InfoReducer).append(InfoReducer).combineReducers(),
    initState
)

store.subscribe {
    val counter = store.state.counter
    Log.d("redux====", "Counter.count=${counter?.count}");
}
store.subscribe {
    val info = store.state.info
    Log.d("redux=====", "Counter.info=${info?.name}");
}
/*自增*/
store.changeState(Action("INCREMENT"))
/*自减*/
store.changeState(Action("DECREMENT"))
/*我想随便改 计划外的修改是无效的!*/
store.changeState(Action("abc"))
store.changeState(Action("SET_NAME", "我的新设置的名字"))

输出

 
 D/redux====: Counter.count=2
 D/redux=====: Counter.info=name
 
 D/redux====: Counter.count=1
 D/redux=====: Counter.info=name
 
 D/redux====: Counter.count=1
 D/redux=====: Counter.info=name
 
 D/redux====: Counter.count=1
 D/redux=====: Counter.info=我的新设置的名字

redux组合了,但是还有一个问题,state是写在一起的,如何把state也拆开呢

首先改写拆分State为CounterState和InfoState

 
data class Info(var name: String) : State
data class Counter(var count: Int) : State

interface State {
    fun getTag(): String {
        return this::class.java.name
    }

    fun <T> getData(clazz: Class<T>): T {
        if (this is StateContainer) {
            return states[clazz.name] as T
        } else {
            return this as T
        }
    }
}

class StateContainer : State {
    val states = mutableMapOf<String, State>()

    companion object {
        fun allState(): State {
            return StateContainer()
        }
    }
}

fun <T : State> T.getState(all: State): T {
    if (this.getTag() == StateContainer::class.java.name) {
        return this
    }
    if (all is StateContainer) {
        return all.states[this.getTag()] as T
    } else {
        return this
    }

}

fun <T : State> T.save(all: State) {
    if (all is StateContainer) {
        all.states[this.getTag()] = this
    }
}

框架代码改造

 

class IStore {
    companion object {
        /**
         * 用于合并Reducer
         */
        fun MutableList<Reducer<out State>>.combineReducers(): CombineReducer {
            return CombineReducer(StateContainer.allState(), this)
        }


        val createStore = fun(plan: Reducer<out State>): Store {
            var mState: State = StateContainer.allState()
            if (!(plan is CombineReducer)) {
                plan.initStatedata.save(mState)
            } else {
                mState = plan.initStatedata
                plan.mutableList.forEach {
                    it.initStatedata.save(mState)
                }
            }
            val listeners = mutableListOf<StateListener>()
            val subscribe = fun(listener: StateListener): () -> Unit {
                listeners.add(listener)
                return fun() {
                    val index = listeners.indexOf(listener)
                    listeners.removeAt(index)
                }
            }
            val dispatch = fun(action: Action) {
                mState = plan(mState, action)
                /*当 count 改变的时候,我们要去通知所有的订阅者*/
                listeners.forEach {
                    it()
                }
            }
            return Store(subscribe, dispatch, mState!!)
        }
    }
}
typealias StateListener = () -> Unit
typealias Subscribe = (listener: StateListener) -> Unit
typealias Dispatch = (action: Action) -> Unit

abstract class Reducer<T : State>(val initStatedata: T) : InnerReducer {

    override fun invoke(state: State, action: Action): State {
        val data = initStatedata.getState()
        val next = nextState(data, action)
        next.save()
        return state
    }

    abstract fun nextState(stateData: T, action: Action): T

}

class CombineReducer(initState: State, val mutableList: MutableList<Reducer<out State>>) :
    Reducer<State>(initState) {
    override fun nextState(stateData: State, action: Action): State {
        mutableList.forEach {
            val state = it.initStatedata.getState()
            val next = it.invoke(state, action)
            next.save()
        }
        return stateData
    }
}
typealias  InnerReducer = (state: State, action: Action) -> State

data class Store(val subscribe: Subscribe, val dispatch: Dispatch, val state: State)
data class Action(val type: String, var data: Any? = null)

inline fun <reified T : State> Store.subscribe(crossinline callBack: (state: T) -> Unit): () -> Unit {
    return this.subscribe {
        val data = state.getData(T::class.java) as T
        callBack(data)
    }
}

自定义reducer

 

class CounterReducer(initState: Counter) : Reducer<Counter>(initState) {

    override fun nextState(stateData: Counter, action: Action): Counter {
        when (action.type) {
            "INCREMENT" ->
                return stateData.apply {
                    count = (count ?: 0) + 1
                }
            "DECREMENT" ->
                return stateData.apply {
                   count = (count ?: 0) - 1
                }
            else -> return stateData
        }
    }

}


class CounterReducer(initState: Counter) : Reducer<Counter>(initState) {

    override fun nextState(stateData: Counter, action: Action): Counter {
        when (action.type) {
            "INCREMENT" ->
                return stateData.apply {
                    count = (count ?: 0) + 1
                }
            "DECREMENT" ->
                return stateData.apply {
                   count = (count ?: 0) - 1
                }
            else -> return stateData
        }
    }

}

使用测试

 
fun test() {

    val counterReducer = CounterReducer(Counter(1))
    val infoReducer = InfoReducer(Info("name"))

    /*把定义好的plan函数传入*/
   val list= mutableListOf<Reducer<out State>>().apply {
       this.add(counterReducer)
       this.add(infoReducer)
    }

    val store = IStore.createStore(list.combineReducers())

    //观察所有数据
    store.subscribe {
        val counter= store.state.getData(Counter::class.java)
        Log.d("redux====", "Counter.count=${counter?.count}");
    }
    //观察指定类型
    store.subscribe<Info> {
        Log.d("redux=====", "Info.name=${it?.name}");
    }
    /*自增*/
    store.dispatch(Action("INCREMENT"))
    /*自减*/
    store.dispatch(Action("DECREMENT"))
    /*我想随便改 计划外的修改是无效的!*/
    store.dispatch(Action("abc"))
    store.dispatch(Action("SET_NAME", "我的新设置的名字"))
}

4.中间件 middleware

中间件是对 dispatch 的扩展,或者说重写,增强 dispatch 的功能!

记录日志

我现在有一个需求,在每次修改 state 的时候,记录下来 修改前的 state ,为什么修改了,以及修改后的 state。我们可以通过重写 store.dispatch 来实现,直接看代码

 
val store = IStore.createStore(counterReducer)
val next = store.dispatch
/*重写了store.dispatch*/
store.dispatch = fun(action: Action){
    Log.d("redux====", store.state.getData(Counter::class.java).toString())
    Log.d("redux====", action.type.toString())
    next(action)
    Log.d("redux====", store.state.getData(Counter::class.java).toString())
}

使用下

 
store.dispatch(Action("INCREMENT"))

输出

 
D/redux====: Counter(count=1)
D/redux====: INCREMENT
D/redux====: Counter(count=2)

现在我们已经实现了一个的记录 state.counter 修改日志的功能!

记录异常

我又有一个需求,需要记录每次数据出错的原因,我们扩展下 dispatch

 
val store = IStore.createStore(counterReducer)
val next = store.dispatch
store.dispatch =fun(action: Action) {
    try {
        next(action);
    } catch (e:Exception) {
        Log.e("redux====","错误报告: ${e.message}" )
    }
}

这样每次 dispatch 出异常的时候,我们都会记录下来。

多中间件的合作

我现在既需要记录日志,又需要记录异常,怎么办?那就要两个函数合起来

 
store.dispatch = fun(action: Action) {
    try {
        Log.d("redux====", store.state.getData(Counter::class.java).toString())
        Log.d("redux====", action.type.toString())
        next(action)
        Log.d("redux====", store.state.getData(Counter::class.java).toString())
    } catch (e:Exception) {
        Log.e("redux====","错误报告: ${e.message}" )
    }
}

如果又来一个需求怎么办?接着改 dispatch 函数?那再来10个需求呢?到时候 dispatch 函数肯定庞大混乱到无法维护了!这个方式不可取呀!如果又来一个需求怎么办?接着改 dispatch 函数?那再来10个需求呢?到时候 dispatch 函数肯定庞大混乱到无法维护了!这个方式不可取呀!

我们需要考虑如何实现扩展性很强的多中间件合作模式。

1.我们把 loggerMiddleware 提取出来

 
val store = IStore.createStore(counterReducer)
val next = store.dispatch

val loggerMiddleware = fun(action: Action){
    Log.d("redux====", store.state.getData(Counter::class.java).toString())
    Log.d("redux====", action.type.toString())
    next(action)
    Log.d("redux====", store.state.getData(Counter::class.java).toString())
}
store.dispatch =fun(action: Action){
    try {
        loggerMiddleware(action);
    } catch (e:Exception) {
        Log.e("redux====","错误报告: ${e.message}" )
    }
}

2.我们把 exceptionMiddleware 提取出来

 
val exceptionMiddleware =fun(action: Action) {
    try {
        /*next(action)*/
        loggerMiddleware(action);
    } catch (e:Exception) {
        Log.e("redux====","错误报告: ${e.message}" )
    }
}

3.现在的代码有一个很严重的问题,就是 exceptionMiddleware 里面写死了 loggerMiddleware,我们需要让 next(action)变成动态的,随便哪个中间件都可以

 
val exceptionMiddleware = fun(next: Dispatch): Dispatch {
    return fun(action: Action) {
        try {
            next(action)
        } catch (e: Exception) {
            Log.e("redux====", "错误报告: ${e.message}")
        }
    }
}
val logMiddleware = fun(next: Dispatch): Dispatch {
    return fun(action: Action) {
        Log.d("redux====", store.state.getData(Counter::class.java).toString())
        Log.d("redux====", action.type.toString())
        next(action)
        Log.d("redux====", store.state.getData(Counter::class.java).toString())
    }
}

这样使用的话,就可以随意嵌套

 
val next = store.dispatch
/*可以loggerMiddleware 变成参数传进去*/
store.dispatch = exceptionMiddleware(logMiddleware(next))
/*也可以exceptionMiddleware变成参数传进去*/
store.dispatch = logMiddleware(exceptionMiddleware(next))

这时候我们开开心心的新建了一个 loggerMiddleware.kt,一个exceptionMiddleware.kt文件,想把两个中间件独立到单独的文件中去。会碰到什么问题吗?如下图。报错

loggerMiddleware 中包含了外部变量 store,导致我们无法把中间件独立出去。那我们把 store 也作为一个参数传进去好了~

 
val store = IStore.createStore(counterReducer)
val next = store.dispatch
val exceptionMiddleware = fun(store: Store): ((next: Dispatch) -> Dispatch) {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            try {
                next(action)
            } catch (e: Exception) {
                Log.e("redux====", "错误报告: ${e.message}")
            }
        }
    }
}
val logMiddleware = fun(store: Store): ((next: Dispatch) -> Dispatch) {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            Log.d("redux====", store.state.getData(Counter::class.java).toString())
            Log.d("redux====", action.type.toString())
            next(action)
            Log.d("redux====", store.state.getData(Counter::class.java).toString())
        }
    }
}
val exception=exceptionMiddleware(store)
val logger=logMiddleware(store)
/*可以loggerMiddleware 变成参数传进去*/
store.dispatch = logger(exception(next))
/*也可以exceptionMiddleware变成参数传进去*/
store.dispatch = exception(logger(next))

到这里为止,实现了两个可以独立的中间件

现在我有一个需求,在打印日志之前输出当前的时间戳。用中间件来实现!

 
val store = IStore.createStore(counterReducer)
val next = store.dispatch
val exceptionMiddleware = fun(store: Store): ((next: Dispatch) -> Dispatch) {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            try {
                next(action)
            } catch (e: Exception) {
                Log.e("redux====", "错误报告: ${e.message}")
            }
        }
    }
}
val logMiddleware = fun(store: Store): ((next: Dispatch) -> Dispatch) {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            Log.d("redux====", store.state.getData(Counter::class.java).toString())
            Log.d("redux====", action.type.toString())
            next(action)
            Log.d("redux====", store.state.getData(Counter::class.java).toString())
        }
    }
}
val exception=exceptionMiddleware(store)
val logger=logMiddleware(store)
/*可以loggerMiddleware 变成参数传进去*/
store.dispatch = logger(exception(next))
/*也可以exceptionMiddleware变成参数传进去*/
store.dispatch = exception(logger(next))

测试一下

 
val store = IStore.createStore(counterReducer)
val next = store.dispatch


val exception = exceptionMiddleware(store)
val logger = logMiddleware(store)
val time = timeMiddleware(store)
/*也可以exceptionMiddleware变成参数传进去*/
store.dispatch = exception(logger(time(next)))

================>
D/redux====: Counter(count=1)
D/redux====: INCREMENT
D/redux====: 1669277764203
D/redux====: Counter(count=2)

自定义中间件貌似看起来比较难,那就定义两种创建中间件抽象

typealias MiddlewareInner = ((next: Dispatch) -> Dispatch)
typealias Middleware = (store: Store) -> ((next: Dispatch) -> Dispatch)

fun registerWrapperMiddleware(
    before: ((store: Store, action: Action) -> Unit)? = null,
    after: ((store: Store, action: Action) -> Unit)? = null
): (store: Store) -> MiddlewareInner {
    return fun(store: Store): MiddlewareInner {
        return fun(next: Dispatch): Dispatch {
            return fun(action: Action) {
                before?.invoke(store, action)
                next(action)
                after?.invoke(store, action)
            }
        }
    }
}

fun registerAroundMiddleware(
    around: (store: Store, next: Dispatch, action: Action) -> Unit
): (store: Store) -> MiddlewareInner {
    return fun(store: Store): MiddlewareInner {
        return fun(next: Dispatch): Dispatch {
            return fun(action: Action) {
                around.invoke(store, next, action)
            }
        }
    }
}

上面的两种中间件实现也可以改写成下面的

val logMiddleware = fun(store: Store): MiddlewareInner {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            Log.d("redux====修改之前", store.state.getData(Counter::class.java).toString())
            Log.d("redux====", action.type.toString())
            next(action)
            Log.d("redux====修改之后", store.state.getData(Counter::class.java).toString())
            Log.d("redux====","         \n")
        }
    }
}
等同于下面
val logMiddleware1 = registerWrapperMiddleware({ store, action ->
    Log.d("redux====修改之前", store.state.getData(Counter::class.java).toString())
    Log.d("redux====", action.type.toString())
}) { store, action ->
    Log.d("redux====修改之后", store.state.getData(Counter::class.java).toString())
    Log.d("redux====","         \n")
}




val timeMiddleware = fun(store: Store): MiddlewareInner {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            Log.d("redux====", Date().getTime().toString());
            next(action);
        }
    }
}
等同于下面
val timeMiddleware1 = registerWrapperMiddleware (after = {s, a ->
    Log.d("redux====", Date().getTime().toString());
})




val exceptionMiddleware = fun(store: Store): MiddlewareInner {
    return fun(next: Dispatch): Dispatch {
        return fun(action: Action) {
            try {
                next(action)
            } catch (e: Exception) {
                Log.e("redux====", "错误报告: ${e.message}")
            }
        }
    }
}

等同于下面
val exceptionMiddleware1 = registerAroundMiddleware { store, next, action ->
    try {
        next(action)
    } catch (e: java.lang.Exception) {
        Log.e("redux====", "错误报告: ${e.message}")
    }
}

中间件使用方式优化

上一节我们已经完全实现了正确的中间件,但是中间件的使用方式不是很友好

其实我们只需要知道三个中间件,剩下的细节都可以封装起来!我们通过扩展 createStore 来实现!先看下最终目标

 
val createStore = IStore.createStore
val newCreateStore = applyMiddleware(exceptionMiddleware, timeMiddleware, logMiddleware)(createStore)
val storeNew = newCreateStore(counterReducer)

1.实现 applyMiddleware

 
typealias RewriteCreateStoreFunc=(oldCreateStore: CreateStore)-> CreateStore
typealias MiddlewareInner =((next: Dispatch) -> Dispatch)
typealias Middleware = (store: Store) -> ((next: Dispatch) -> Dispatch)
typealias CreateStore= (plan: Reducer<out State>) -> Store


fun applyMiddleware(vararg middlewares: Middleware):RewriteCreateStoreFunc{
    return fun(oldCreateStore: CreateStore):  CreateStore{
        return fun(reducer: Reducer<out State>) : Store{
            val store = oldCreateStore(reducer)
            var dispatch = store.dispatch
            /* 实现 exception(time((logger(dispatch))))*/
            middlewares.reverse()
            //创建中间件
            //模拟  val exception = exceptionMiddleware(store)
            //        val logger = logMiddleware(store)
            //        val time = timeMiddleware(store)
            val chain= mutableListOf<MiddlewareInner>()
            middlewares.forEach {
                chain.add(it(store))
            }
            //反转一下 实现 exception(time((logger(dispatch))))
            chain.reverse()
            chain.forEach {
                dispatch = it(dispatch);
            }
            /*2. 重写 dispatch*/
            store.dispatch = dispatch;
            return store
        }
    }
}

但是现在有个问题,有createStore 和newCreateStore两种创建方式了

 
val createStore = IStore.createStore
/*没有中间件的 createStore*/
val store = createStore(counterReducer)

/*有中间件的 createStore*/
val rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware,logMiddleware, timeMiddleware )
val newCreateStore = rewriteCreateStoreFunc(createStore)
val store2 = newCreateStore(counterReducer)

那就重载一个

    fun createStore(
            middlewares: MutableList<Middleware>,
            plans:MutableList<Reducer<out State>>
        ): Store {
           return applyMiddleware(middlewares)(createStore)(plans.combineReducers())
        }

用法

       val store = IStore.createStore(counterReducer)

        val store2= applyMiddleware(mutableListOf(exceptionMiddleware1, timeMiddleware1, logMiddleware1))(IStore.createStore)(list.combineReducers())

        val store3 = IStore.createStore(mutableListOf(exceptionMiddleware1, timeMiddleware1, logMiddleware1),mutableListOf(counterReducer,infoReducer))

5.退订

typealias Subscribe = (listener: StateListener) -> ()->Unit


val subscribe =fun(listener: StateListener):()->Unit{
    listeners.add(listener)
    return fun() {
        val index = listeners.indexOf(listener)
        listeners.removeAt(index)
    }
}

退订用法

 
val createStore = IStore.createStore
/*没有中间件的 createStore*/
val store = createStore(counterReducer)

val unsubscribe= store.subscribe {
     val counter= store.state.getData(Counter::class.java)
     Log.d("redux====", "Counter.count=${counter?.count}");
 }
/*自增*/
store.dispatch(Action("INCREMENT"))
unsubscribe()
/*自减*/
store.dispatch(Action("DECREMENT"))
/*我想随便改 计划外的修改是无效的!*/
store.dispatch(Action("abc"))
store.dispatch(Action("SET_NAME", "我的新设置的名字"))



=============>
D/redux====: Counter.count=2

最后放在代码地址

GitHub - 971638267/ReduxByKotlin: 一个kotlin实现的Redux

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/439942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最优控制 3:最优控制理论中的极小值原理与动态规划

最优控制 3&#xff1a;使用极小值原理求解最优控制问题 引言极小值原理 t f t_f tf​ 固定的情况 t f t_f tf​ 自由的情况 动态规划连续系统 HJB 方程的推导 引言 经典变分法是一种特别强大的工具&#xff0c;但是它要求控制量必须可导且无界&#xff0c;这在很多问题中都是…

pandas读取列数不同的CSV文件

使用pandas读取每行不同列的CSV文件 对于序列模型而言&#xff0c;每条数据的大小都不一定相等&#xff0c;但对于一般的神经网络要求输入大小相等。目前的一种方法是选取当前数据集中最大长度的数据作为基准数据大小&#xff0c;其余的数据末尾补零来规范整个数据集每条数据的…

计算机:理解操作系统:内存篇(下)

内存 1. 指针与引用2. 进程的内存模型3. 幻象大师---操作系统4. 总结 本篇是 关于计算机内存最后一篇文章 什么是内存 C/C内存模型 堆区与栈区的本质 Java、Python等内存模型 Java内存模型 Jave中的堆区与栈区是如何实现的 Python内存模型 指针与引用 进程的内存模型 幻想大师-…

css tooltip (web.dev)

目录 版权介绍tool-tip在上居中动画效果宽度边界 tool-tip::after范围锥形渐变-webkit-mask尖角怎么来的? 附录完整代码 版权 本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/130262213. 介绍 https://web.dev…

【周末闲谈】AI作图,你真的了解它吗?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️周末闲谈】 系列目录 ✨第一周 二进制VS三进制 ✨第二周 文心一言&#xff0c;模仿还是超越&#xff1f; ✨第二周 畅想AR 文章目录 系列目录前言AI绘画&#x1f916;&#x1f916;&#x1f916;工作…

[C++]:万字超详细讲解多态以及多态的实现原理(面试的必考的c++考点)

文章目录 前言一、多态的定义及实现1.多态的构成条件2.c11的override和final3.重载&#xff0c;重写&#xff0c;重定义的比较4.抽象类5.多态的原理6.多继承中的虚函数表7.动态绑定和静态绑定总结 前言 多态的概念&#xff1a; 多态的概念&#xff1a;通俗来说&#xff0c;就是…

【Linux】使用systemd设置开机自启动命令

目录 1 使用使用systemd实现开机自动运行命令1.1 新建一个.service文件1.2 编写.service文件1.2.1 [Unit]1.2.2 [Service]1.2.3 [Install] 1.3 启动服务并设置自启动 2 编写Systemd服务文件的要点2.1 Systemd服务文件的位置2.2 Systemd服务文件的格式2.3 Systemd服务文件的基本…

【基础】Kafka -- 基础架构及核心概念

Kafka -- 基础架构及核心概念 初识 KafkaKafka 基本架构Kafka 主题与分区主题与分区分区副本机制 Replica高水位 HW 生产者生产者客户端必要的参数配置消息的发送序列化分区器生产者拦截器 原理分析重要的生产者参数 消费者消费者与消费者组消费者客户端必要的参数配置订阅主题…

MySQL 按关键字进行截取

一、问题背景 取MySQL数据表中某个字段中的IP信息。 如&#xff1a;t_log 表中的 user_ip 字段值为 {“username”:“miracle”,“ip”:“110.230.128.186”}&#xff0c;取出IP信息 110.230.128.186。 建表和初始化SQL语句&#xff0c;如下&#xff1a; SET NAMES utf8mb4…

GORM操作mysql数据库

对象就是程序的数据结构&#xff0c;关系是数据库。就是将程序的数据结构与数据库表对应起来。 在GORM是Go语言的ORM框架&#xff0c;将go的数据结构转化为数据库表&#xff0c;例如将结构体转化为数据库表。 引入gorm框架 远程下载gorm框架 go get -u gorm.io/driver/mysq…

HTTP中的Content-type详解

Content-Type Content-Type&#xff08;MediaType&#xff09;&#xff0c;即是Internet Media Type&#xff0c;互联网媒体类型&#xff0c;也叫做MIME类型。在互联网中有成百上千中不同的数据类型&#xff0c;HTTP在传输数据对象时会为他们打上称为MIME的数据格式标签&#x…

关于java.io的学习记录(写入文本)

可以通过字节流&#xff08;FileOutputStream&#xff09;、字符流&#xff08;OutputStreamWriter&#xff09;、字符缓冲流&#xff08;BufferedWriter&#xff09;读取文本中的数据。 FileOutputStream写入文本 public void write(){String path "writeTest.txt"…

【是C++,不是C艹】 什么是C++ | C++从哪来 | 学习建议

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e; &#x1f449;专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 我知道你急着学C&#xff0c;但你先别急&#xff0c;薛之谦认识认识C还是很有必要的。本期跟大家聊…

文件夹改名,如何在改名之后批量复制文件夹名称

在日常时候中会遇到给文件夹改名的时候&#xff0c;那么我们又如何在改名之后批量复制文件夹名称&#xff1f;今天就由小编来给大家分享一下操作办法。 首先第一步&#xff0c;我们要进入文件批量改名高手&#xff0c;并在板块栏里选择“文件夹批量改名”板块。 第二步&#xf…

SpringBoot 接入chatGPT API

SpringBoot 接入chatGPT API 一、准备工作二、补全接口示例三、申请API-KEY**四、JavaScript调用API**五、SpringBoot整合ChatGPT六、使用curl模拟请求ChatGPT平台已经为技术提供了一个入口了,作为一个Java程序员,我们第一时间想到的就是快速开发一个应用,接入ChatGPT的接口…

第十四天本地锁、Redis分布锁、Redisson锁三者的区别

一、为什么要有redis分布式锁&#xff0c;它解决了什么问题&#xff1f; 在传统单体架构的项目下&#xff0c;使用本地锁synchronized和lock锁就可以锁住当前进程&#xff0c;保证线程的安全性&#xff0c;但是本地锁解决不了分布式环境下多个服务资源共享的问题&#xff0c;而…

产品研发流程管理

先看一张图&#xff0c;该图适应绝大部分的产品的 研发流程 &#xff08;需要的可以去下 产品研发流程| ProcessOn免费在线作图,在线流程图,在线思维导图&#xff09; 该图详细描述了&#xff0c;不同阶段应该做什么&#xff0c;具体的来说&#xff0c;是确定了什么时候 “开会…

高精度人员定位系统源码,采用vue+spring boot框架,支持二次开发

智慧工厂人员定位系统源码&#xff0c;高精度人员定位系统源码&#xff0c;UWB定位技术 文末获取联系&#xff01; 在工厂日常生产活动中&#xff0c;企业很难精准地掌握访客和承包商等各类人员的实际位置&#xff0c;且无法实时监控巡检人员的巡检路线&#xff0c;当厂区发生灾…

【Python】实战:生成无关联单选问卷 csv《精神状态评估表》

目录 一、适用场景 二、业务需求 三、Python 文件 &#xff08;1&#xff09;创建文件 &#xff08;2&#xff09;代码示例 四、csv 文件 一、适用场景 实战场景&#xff1a; 问卷全部为单选题问卷问题全部为必填问题之间无关联关系每个问题的答案分数不同根据问卷全部问…

使用pandas和seaborn绘图

使用pandas和seaborn绘图 matplotlib实际上是一种比较低级的工具。要绘制一张图表&#xff0c;你组装一些基本组件就行&#xff1a;数据展示 &#xff08;即图表类型&#xff1a;线型图、柱状图、盒形图、散布图、等值线图等&#xff09;、图例、标题、刻度标签以及其他注解型…