【设计模式】之 行为型模式

【设计模式】之 行为型模式

设计模式就是在某些场景下,针对某类问题的某种通用的解决方案。

使用设计模式可以使得代码可复用、让代码更容易被他人理解、保证代码可靠性。

设计模式总体被分为三类:

  • 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程
  • 行为型模式:类和对象如何交互,及划分责任和算法
  • 结构型模式:把类或对象结合在一起形成一个更大的结构

本文将讲述行为型模式的使用总结。

行为型模式

观察者模式

当对象间存在一对多关系时,可以使用观察者模式。

比如,当一个对象被修改时,则会自动通知依赖它的对象。

代码演示:

步骤1:创建一个抽象的观察者类 Observer

abstract class Observer {
    
    
    // 观察的对象
    protected var subject: Subject? = null
    // 当对象更新时,主动触发 update() 方法
    abstract fun update()
}

步骤2:创建 Subject 类,作为被观察者

class Subject {
    
    
    // 持有所有观察者
    private val observers: MutableList<Observer> = ArrayList()

    // 状态发生变化时,通知所有观察者
    var state = 0
        set(state) {
    
    
            field = state
            notifyAllObservers()
        }

    fun attach(observer: Observer) {
    
    
        observers.add(observer)
    }

    private fun notifyAllObservers() {
    
    
        observers.forEach {
    
    
            it.update()
        }
    }
}

步骤3:创建 3个 实体观察者类,继承 Observer,观察对象是 Subject。

// 观察者 1
class BinaryObserver(subject: Subject) : Observer() {
    
    
    override fun update() {
    
    
        println("Binary String: " + Integer.toBinaryString(subject!!.state))
    }

    init {
    
    
        this.subject = subject
        this.subject!!.attach(this)
    }
}
// 观察者 2
class OctalObserver(subject: Subject?) : Observer() {
    
    
    override fun update() {
    
    
        println("Octal String: " + Integer.toOctalString(subject!!.state))
    }

    init {
    
    
        this.subject = subject
        this.subject!!.attach(this)
    }
}
// 观察者 3
class HexaObserver(subject: Subject?) : Observer() {
    
    
    override fun update() {
    
    
        println("Hex String: " + Integer.toHexString(subject!!.state).toUpperCase())
    }

    init {
    
    
        this.subject = subject
        this.subject!!.attach(this)
    }
}

步骤4:测试代码

fun main(args: Array<String>) {
    
    
    val subject = Subject()
    HexaObserver(subject)
    OctalObserver(subject)
    BinaryObserver(subject)
    println("First state change: 15")
    subject.state = 15
    println("Second state change: 10")
    subject.state = 10
}

// 代码输出结果
First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010

策略模式

一个类的行为或其算法需要在运行时更改,可以使用在策略模式

我们可以定义一系列的算法,把它们一个个封装起来,,并且使它们可相互替换。

优点:

  • 算法可以自由切换
  • 避免使用多重条件判断
  • 扩展性良好

缺点:

  • 策略类会增多(如果一个系统的策略多于四个,就需要考虑使用混合模式
  • 所有策略类都需要对外暴露

代码演示:

步骤一:创建一个 Strategy 接口

interface Strategy {
    
    
    // 策略方法
    fun doOperation(num1: Int, num2: Int): Int
}

步骤二:创建实现接口的实体类

// 加法策略
class OperationAdd : Strategy {
    
    
    override fun doOperation(num1: Int, num2: Int): Int {
    
    
        return num1 + num2
    }
}
// 减法策略
class OperationSubtract : Strategy {
    
    
    override fun doOperation(num1: Int, num2: Int): Int {
    
    
        return num1 - num2
    }
}
// 乘法策略
class OperationMultiply : Strategy {
    
    
    override fun doOperation(num1: Int, num2: Int): Int {
    
    
        return num1 * num2
    }
}

步骤三:创建 Context 类,用于调度策略方法

// 根据外部传进来的 Strategy,调度具体策略方法
class Context(private val strategy: Strategy) {
    
    
    fun executeStrategy(num1: Int, num2: Int): Int {
    
    
        return strategy.doOperation(num1, num2)
    }
}

步骤四:测试代码

fun main(args: Array<String>) {
    
    
    var context = Context(OperationAdd())
    System.out.println("10 + 5 = " + context.executeStrategy(10, 5))
    context = Context(OperationSubtract())
    System.out.println("10 - 5 = " + context.executeStrategy(10, 5))
    context = Context(OperationMultiply())
    System.out.println("10 * 5 = " + context.executeStrategy(10, 5))
}

// 代码执行结果
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

状态模式

当对象的行为依赖于自己的状态(属性),并且可以根据自己的状态改变,从而改变自己的相关行为,可以使用状态模式。

例如,我们打篮球的时候,可以有正常状态、不正常状态和超常状态。

代码演示:

步骤一:创建一个 Context 类,用于保存状态

class Context {
    
    
    var state: State? = null
}

步骤二:创建一个 State 接口

interface State {
    
    
    // 该状态下的行为方法
    fun doAction(context: Context)
}

步骤三:创建两个实现了 State 接口的实体 状态类

// 开始状态
class StartState : State {
    
    
    override fun doAction(context: Context) {
    
    
        println("Player is in start state")
        context.state = this
    }

    override fun toString(): String {
    
    
        return "Start State"
    }
}

// 结束状态
class StopState : State {
    
    
    override fun doAction(context: Context) {
    
    
        println("Player is in stop state")
        context.state = this
    }

    override fun toString(): String {
    
    
        return "Stop State"
    }
}

步骤四:测试代码

fun main(args: Array<String>) {
    
    
    val context = Context()
    // 开始状态
    val startState = StartState()
    startState.doAction(context)
    println(context.state.toString())
    // 结束状态
    val stopState = StopState()
    stopState.doAction(context)
    println(context.state.toString())
}

// 代码输出结果
Player is in start state
Start State
Player is in stop state
Stop State

责任链模式

责任链模式为请求创建了一个接收者对象的链。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

责任链模式对请求的 发送者 和 接收者 进行了解耦。让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

代码演示:

步骤一:创建抽象的日志记录器类

abstract class AbstractLogger {
    
    
    var level = 0
    //责任链中的下一个元素
    var nextLogger: AbstractLogger? = null

    fun logMessage(level: Int, message: String?) {
    
    
        if (this.level <= level) {
    
    
            write(message)
        }
        if (nextLogger != null) {
    
    
            nextLogger!!.logMessage(level, message)
        }
    }

    protected abstract fun write(message: String?)

    companion object {
    
    
        var INFO = 1
        var DEBUG = 2
        var ERROR = 3
    }
}

步骤二:创建扩展了抽象记录器类的实体类

// 控制台日志
class ConsoleLogger(level: Int) : AbstractLogger() {
    
    
    override fun write(message: String?) {
    
    
        println("Standard Console::Logger: $message")
    }

    init {
    
    
        this.level = level
    }
}

// 错误日志
class ErrorLogger(level: Int) : AbstractLogger() {
    
    
    override fun write(message: String?) {
    
    
        println("Error Console::Logger: $message")
    }

    init {
    
    
        this.level = level
    }
}

// 文件日志
class FileLogger(level: Int) : AbstractLogger() {
    
    
    override fun write(message: String?) {
    
    
        println("File::Logger: $message")
    }

    init {
    
    
        this.level = level
    }
}

步骤三:测试代码

object ChainPatternDemo {
    
    
    // 构建一条责任链为 错误日志 -> 文件日志 -> 控制台日志
    private val chainOfLoggers: AbstractLogger
        get() {
    
    
            val errorLogger: AbstractLogger = ErrorLogger(AbstractLogger.ERROR)
            val fileLogger: AbstractLogger = FileLogger(AbstractLogger.DEBUG)
            val consoleLogger: AbstractLogger = ConsoleLogger(AbstractLogger.INFO)
            errorLogger.nextLogger = fileLogger
            fileLogger.nextLogger = consoleLogger
            return errorLogger
        }

    @JvmStatic
    fun main(args: Array<String>) {
    
    
        val loggerChain = chainOfLoggers
        // 每次执行 logMessage 方法都会依次从错误日志 -> 文件日志 -> 控制台日志尝试执行 write 方法
        loggerChain.logMessage(AbstractLogger.INFO, "This is an information.")
        loggerChain.logMessage(
            AbstractLogger.DEBUG,
            "This is a debug level information."
        )
        loggerChain.logMessage(
            AbstractLogger.ERROR,
            "This is an error information."
        )
    }
}

// 代码输出结果
Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

中介者模式

中介者模式是用来降低多个对象和类之间的通信复杂性。

中介者模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。

例如,在 MVC 框架中,其中 C(控制器)就是 M(模型)和 V(视图)的中介者。

代码演示:

我们通过聊天室实例来演示中介者模式。

实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。

步骤一:创建一个 User 类

class User(var name: String) {
    
    
    // 使用 聊天室 的静态方法来发送消息
    fun sendMessage(message: String) {
    
    
        ChatRoom.showMessage(this, message)
    }
}

步骤二:创建一个中介者类(聊天室)

// 聊天室,所有 user 都可以调用聊天室的静态方法
object ChatRoom {
    
    
    fun showMessage(user: User, message: String) {
    
    
        println(Date().toString() + " [" + user.name + "] : " + message)
    }
}

步骤三:测试代码

fun main(args: Array<String>) {
    
    
    val robert = User("Robert")
    val john = User("John")
    robert.sendMessage("Hi! John!")
    john.sendMessage("Hello! Robert!")
}

// 代码输出结果
Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!

模板模式

在模板模式中,一个抽象类公开定义了执行它的方法的模板

它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

优点:

  • 封装不变部分,扩展可变部分
  • 提取公共代码,便于维护
  • 行为由父类控制,子类实现

缺点:

  • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大

**注意:**为防止恶意操作,一般模板方法都加上 final 关键字,或者模板方法不允许修改

代码演示:

步骤一:定义一个抽象的游戏模板类,且模板方法无法被重写

// 定义一个游戏模板
abstract class Game {
    
    
    abstract fun initialize()
    abstract fun startPlay()
    abstract fun endPlay()
    // 模板方法,该方法无法被重写
    fun play() {
    
    
        //初始化游戏
        initialize()
        //开始游戏
        startPlay()
        //结束游戏
        endPlay()
    }
}

步骤二:创建扩展了上述类的实体类

// 蟋蟀游戏
class Cricket : Game() {
    
    
    override fun initialize() {
    
    
        println("Cricket Game Initialized! Start playing.")
    }

    override fun startPlay() {
    
    
        println("Cricket Game Started. Enjoy the game!")
    }

    override fun endPlay() {
    
    
        println("Cricket Game Finished!")
    }
}

// 足球游戏
class Football : Game() {
    
    
    override fun initialize() {
    
    
        println("Football Game Initialized! Start playing.")
    }

    override fun startPlay() {
    
    
        println("Football Game Started. Enjoy the game!")
    }

    override fun endPlay() {
    
    
        println("Football Game Finished!")
    }
}

步骤三:测试代码

fun main(args: Array<String>) {
    
    
    val game1 = Cricket()
    game1.play()

    val game2 = Football()
    game2.play()
}

// 代码执行结果
Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!

Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!

访问者模式

使用场景:

  • 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
  • 对一个对象结构中的对象进行很多不同且不相关的操作,需要避免让这些操作污染这些对象的类

例如,去朋友家做客,你是访问者,朋友接受你的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。

在访问者模式中,我们使用了一个访问者类,它改变了元素类的执行算法,通过这种方式,元素的执行算法可以随着访问者改变而改变。

根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

优点:

  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活性

缺点:

  • 具体元素对访问者公布细节,违反了迪米特原则
  • 具体元素变更比较困难
  • 违反了依赖倒置原则,依赖了具体类,没有依赖抽象

代码演示:

你去朋友家做客,你是访问者,朋友接受你的访问。

朋友带你访问了他的电脑,你通过访问他的电脑,跟你你的意图,同时也访问到了键盘、鼠标、显示器等其他配件。

步骤一:创建一个表示电脑组成部件的接口

// 表示电脑组成部件的接口
interface ComputerPart {
    
    
    fun accept(computerPartVisitor: ComputerPartVisitor)
}

步骤二:创建继承了 ComputerPart 接口的实体类

// 键盘
    class Keyboard : ComputerPart {
    
    
        override fun accept(computerPartVisitor: ComputerPartVisitor) {
    
    
            computerPartVisitor.visit(this)
        }
    }

    // 显示器
    class Monitor : ComputerPart {
    
    
        override fun accept(computerPartVisitor: ComputerPartVisitor) {
    
    
            computerPartVisitor.visit(this)
        }
    }

    // 鼠标
    class Mouse : ComputerPart {
    
    
        override fun accept(computerPartVisitor: ComputerPartVisitor) {
    
    
            computerPartVisitor.visit(this)
        }
    }

    // 电脑
    class Computer : ComputerPart {
    
    
        // 电脑部件列表
        private var parts = arrayOf(Mouse(), Keyboard(), Monitor())
        override fun accept(computerPartVisitor: ComputerPartVisitor) {
    
    
            // 先对所有电脑部件进行访问
            for (i in parts.indices) {
    
    
                parts[i].accept(computerPartVisitor)
            }
            // 最后对电脑自身进行访问
            computerPartVisitor.visit(this)
        }

    }

步骤三:创建一个访问者接口,以及接口对应的实体类访问者类

// 访问者接口
interface ComputerPartVisitor {
    
    
    fun visit(computer: Computer)
    fun visit(mouse: Mouse)
    fun visit(keyboard: Keyboard)
    fun visit(monitor: Monitor)
}

// 继承访问者接口的实体类 -- 电脑部件的访问者
class ComputerPartDisplayVisitor : ComputerPartVisitor {
    
    
    override fun visit(computer: Computer) {
    
    
        println("Displaying Computer.")
    }

    override fun visit(mouse: Mouse) {
    
    
        println("Displaying Mouse.")
    }

    override fun visit(keyboard: Keyboard) {
    
    
        println("Displaying Keyboard.")
    }

    override fun visit(monitor: Monitor) {
    
    
        println("Displaying Monitor.")
    }
}

步骤四:测试代码

// 朋友只带你访问了他的电脑,但是你根据你的意图,同时也访问到了键盘、鼠标、显示器等其他配件
fun main(args: Array<String>) {
    
    
    val computer: ComputerPart = Computer()
    computer.accept(ComputerPartDisplayVisitor())
}

// 代码输出结果:
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

备忘录模式

备忘录模式保存一个对象的某个状态,以便在适当的时候恢复对象。

所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态。

**缺点:**消耗的资源多,如果类的成员变量过多,会占用较大的资源,而且每一次保存都会消耗一定的内存。为了节约内存,可使用 原型模式 + 备忘录模式。

代码演示:

步骤一:创建一个备忘录类,包含了要被恢复的对象的状态

// 备忘录类,包含了要被恢复的对象的状态
class Memento(val state: String)

步骤二:为了符合迪米特原则,增加一个管理备忘录的类

// 管理备忘录的类,负责从 Memento 中恢复对象的状态
class CareTaker {
    
    
    private val mementoList: MutableList<Memento> = ArrayList()
    fun add(memento: Memento) {
    
    
        mementoList.add(memento)
    }

    operator fun get(index: Int): Memento {
    
    
        return mementoList[index]
    }
}

步骤三:创建一个需要保存状态的类,通过备忘录对象存储状态

// 需要保存状态的类,通过备忘录对象存储状态
class Originator {
    
    
    var state: String? = null

    fun saveStateToMemento(): Memento {
    
    
        return Memento(state!!)
    }

    fun getStateFromMemento(memento: Memento) {
    
    
        state = memento.state
    }
}

步骤四:测试代码

fun main(args: Array<String>) {
    
    
    val originator = Originator()
    val careTaker = CareTaker()
    originator.state = "State #1"
    originator.state = "State #2"
    careTaker.add(originator.saveStateToMemento())
    originator.state = "State #3"
    careTaker.add(originator.saveStateToMemento())
    originator.state = "State #4"
    println("Current State: " + originator.state)
    originator.getStateFromMemento(careTaker[0])
    println("First saved State: " + originator.state)
    originator.getStateFromMemento(careTaker[1])
    println("Second saved State: " + originator.state)
}

// 代码执行结果
Current State: State #4
First saved State: State #2
Second saved State: State #3

迭代器模式

迭代器模式是非常常用的设计模式,例如 java 中的 iterator,用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

代码演示:

步骤一:创建相关接口

// 迭代器接口
interface Iterator {
    
    
    operator fun hasNext(): Boolean
    operator fun next(): Any?
}

interface Container {
    
    
    val iterator: Iterator
}

步骤二:创建一个名称库类,内部拥有若干个名称,以及一个迭代器

// 名称库类
class NameRepository : Container {
    
    
    var names = arrayOf("Robert", "John", "Julie", "Lora")
    override val iterator: Iterator
        get() = NameIterator()

    // 名称迭代器,实现了 Iterator 接口
    private inner class NameIterator : Iterator {
    
    
        var index = 0

        override fun hasNext(): Boolean {
    
    
            return index < names.size
        }

        override fun next(): Any? {
    
    
            return if (hasNext()) {
    
    
                names[index++]
            } else {
    
    
                null
            }
        }
    }
}

步骤三:测试代码

fun main(args: Array<String>) {
    
    
    val namesRepository = NameRepository()
    val iterator: Iterator = namesRepository.iterator
    while (iterator.hasNext()) {
    
    
        val name = iterator.next() as String
        println("Name : $name")
    }
}

// 代码执行结果:
Name : Robert
Name : John
Name : Julie
Name : Lora

解释器模式

解释器模式提供了评估语言的语法或表达式的方式。

这种模式实现了一个表达式接口,该接口解释一个特定的上下文。

这种模式被用在 SQL 解析、符号处理引擎等,平时可利用场景比较少。

如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

代码演示:

步骤一:定义一个表达式接口,内部有一个解释内容的方法

// 定义一个表达式接口,内部有一个解释内容的方法
interface Expression {
    
    
    fun interpret(context: String): Boolean
}

步骤二:创建实现 Expression 的具体类

// 主要解释器
class TerminalExpression(private val text: String) : Expression {
    
    
    // 只要传入的 context 属于 text 的一部分,就解释为 true
    override fun interpret(context: String): Boolean {
    
    
        return context.contains(text)
    }
}

// 或解释器
class OrExpression(private val expr1: Expression, private val expr2: Expression) : Expression {
    
    
    // 表达式1 和 表达式2 只要有一个解释为 true, 结果就为 true
    override fun interpret(context: String): Boolean {
    
    
        return expr1.interpret(context) || expr2.interpret(context)
    }
}

// 与解释器
class AndExpression(private val expr1: Expression, private val expr2: Expression) : Expression {
    
    
    // 表达式1 和 表达式2 都解释为 true, 结果就为 true
    override fun interpret(context: String): Boolean {
    
    
        return expr1.interpret(context) && expr2.interpret(context)
    }
}

步骤三:测试代码

object InterpreterPatternDemo {
    
    
    // 创建一个或表达式
    private val orExpression: Expression
        get() {
    
    
            val robert: Expression = TerminalExpression("Robert")
            val john: Expression = TerminalExpression("John")
            return OrExpression(robert, john)
        }

    // 创建一个与表达式
    private val marriedWomanExpression: Expression
        get() {
    
    
            val julie: Expression = TerminalExpression("Julie")
            val married: Expression = TerminalExpression("Married")
            return AndExpression(julie, married)
        }

    @JvmStatic
    fun main(args: Array<String>) {
    
    
        println("John is male? " + orExpression.interpret("John"))
        println("Julie is a married women? " + marriedWomanExpression.interpret("Married Julie"))
    }
}

// 代码输出结果
John is male? true
Julie is a married women? true

命令模式

命令模式是一种数据驱动的设计模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

平时开发中,我们认为是命令的地方都可以使用命令模式,例如: GUI 中每一个按钮都是一条命令。

优点:

  • 降低了系统耦合度
  • 新的命令可以很容易添加到系统中去

**缺点:**使用命令模式可能会导致某些系统有过多的具体命令类。

代码演示:

步骤一:创建一个命令接口

// 创建一个命令接口
interface Order {
    
    
    fun execute()
}

步骤二:创建命令具体类

// 命令类 -- 买股票
class BuyStock(private val abcStock: Stock) : Order {
    
    
    override fun execute() {
    
    
        abcStock.buy()
    }
}

// 命令类 -- 卖股票
class SellStock(private val abcStock: Stock) : Order {
    
    
    override fun execute() {
    
    
        abcStock.sell()
    }
}

步骤三:创建命令调用类

// 创建命令调用类 -- 经纪人
class Broker {
    
    
    private val orderList: MutableList<Order> = ArrayList()
    
    // 收集命令
    fun takeOrder(order: Order) {
    
    
        orderList.add(order)
    }

    // 执行所有命令
    fun placeOrders() {
    
    
        for (order in orderList) {
    
    
            order.execute()
        }
        orderList.clear()
    }
}

步骤四:创建一个股票类

// 创建一个股票类
class Stock {
    
    
    private val name = "ABC"
    private val quantity = 10

    fun buy() {
    
    
        print("Stock [ Name: $name Quantity: $quantity ] bought")
    }
    fun sell() {
    
    
        print("Stock [ Name: $name Quantity: $quantity ] sold")
    }
}

步骤五:测试代码

fun main(args: Array<String>) {
    
    
    val stock = Stock()
    val buyStockOrder = BuyStock(stock)
    val sellStockOrder = SellStock(stock)
    val broker = Broker()
    broker.takeOrder(buyStockOrder)
    broker.takeOrder(sellStockOrder)
    broker.placeOrders()
}

// 代码执行结果
Stock [ Name: ABC, Quantity: 10 ] bought
Stock [ Name: ABC, Quantity: 10 ] sold

猜你喜欢

转载自blog.csdn.net/yang553566463/article/details/124696537