28.男人和女人-访问者模式(大话设计模式Kotlin版)

内容大纲

情节分析

 代码设计部分:

最简单的编程实现

简单的面向编程实现

用了模式实现

访问者模式 

 总结 

 访问者模式的使用前提

访问者模式的使用场景

访问者模式的优缺点


情节分析

例子与代码均来自《大话设计模式》程杰,简单记录加深印象。

举一个生活中的场景:男人和女人的区别

男人成功时,背后多半有一个伟大的女人。

女人成功时,背后大多有一个不成功的男人。

男人失败时,闷头喝酒,谁也不用劝。

女人失败时,眼泪汪汪,谁也劝不了。

男人恋爱时,凡事不懂也要装懂。

女人恋爱时,遇事懂也要装作不懂。

男人结婚时,。。。

女人结婚时,。。。

 

 代码设计部分:

将上面的男人和女人在不同情况下的反应用编程语言来实现吧

最简单的编程实现

/**
 * @create on 2020/4/19 00:01
 * @description 最简单的编程实现
 * @author mrdonkey
 */
class Client {

    companion object {
        @JvmStatic
        fun main(vararg args: String) {
            println("男人成功时,背后多半有一个伟大的女人。")
            println("女人成功时,背后大多有一个不成功的男人。")
            println("男人失败时,闷头喝酒,谁也不用劝。")
            println("女人失败时,眼泪汪汪,谁也劝不了。")
            println("男人恋爱时,凡事不懂也要装懂。")
            println("女人恋爱时,遇事懂也要装作不懂。")
        }
    }

}

 

简单的面向编程实现

男人和女人应该抽象出一个抽象类。

所谓成功与失败都是指人的一种状态是一个人属性。

成功如何,失败如何,不过是一种反应。

Person类

/**
 * @create on 2020/4/19 00:14
 * @description 抽象类
 * @author mrdonkey
 */
abstract class Person {

    //状态
    var state: String? = null

    /**
     * 得到结论或者反应
     */
    abstract fun getConclusion()

}

Man类 

/**
 * @create on 2020/4/19 00:19
 * @description 男人
 * @author mrdonkey
 */
class Man : Person() {

    override fun getConclusion() {
        when (state) {
            "成功" -> {
                println("${this::class.java.simpleName}${state}时,背后多半有一个伟大的女人。")
            }
            "失败" -> {
                println("${this::class.java.simpleName}${state}时,闷头喝酒,谁也不用劝。")
            }
            "恋爱" -> {
                println("${this::class.java.simpleName}${state}时,凡事不懂也要装懂。")
            }
        }
    }
}

Woman类

/**
 * @create on 2020/4/19 00:19
 * @description TODO
 * @author mrdonkey
 */
class Woman : Person() {

    override fun getConclusion() {
        when (state) {
            "成功" -> {
                println("${this::class.java.simpleName}${state}时,背后大多有一个不成功的男人。")
            }
            "失败" -> {
                println("${this::class.java.simpleName}${state}时,眼泪汪汪,谁也劝不了。")
            }
            "恋爱" -> {
                println("${this::class.java.simpleName}${state},遇事懂也要装作不懂。")
            }
        }
    }
}

Client类

/**
 * @create on 2020/4/19 00:22
 * @description 客户端
 * @author mrdonkey
 */
class Client {
    companion object {
        @JvmStatic
        fun main(vararg arg: String) {
            val persons = arrayListOf()
            val man1 = Man()
            man1.state="成功"
            val woman1 = Woman()
            woman1.state="成功"
            val man2 = Man()
            man2.state="失败"
            val woman2 = Woman()
            man2.state="失败"
            val man3 = Man()
            man3.state="恋爱"
            val woman3 = Woman()
            woman3.state="恋爱"
            persons.add(man1)
            persons.add(woman1)
            persons.add(man2)
            persons.add(woman2)
            persons.add(man3)
            persons.add(woman3)
            persons.forEach { person ->
                person.getConclusion()
            }

        }
    }
}

结果:

Man成功时,背后多半有一个伟大的女人。
Woman成功时,背后大多有一个不成功的男人。
Man失败时,闷头喝酒,谁也不用劝。
Man恋爱时,凡事不懂也要装懂。
Woman恋爱,遇事懂也要装作不懂。

这种写法,如果要再增加多几个状态,如结婚、两个person子类都需要进行改动(增加新的判断),有没有更好的方式增加的状态不需要改动两个对象的类呢?

用了模式实现

28.男人和女人-访问者模式(大话设计模式Kotlin版)_第1张图片

看类图写代码:

抽象类Action(男人反应和女人反应是相对稳定的方法,不会轻易改动)

/**
 * @create on 2020/4/19 09:32
 * @description 状态抽象类
 * @author mrdonkey
 */
abstract class Action {

    /**
     * 得到男人的结论或反应
     */
    abstract fun getManConclusion(concreteElementA: Man)

    /**
     * 得到女人结论或反应
     */
    abstract fun getWomanConclusion(concreteElementB: Woman)

}

具体的Action

/**
 * @create on 2020/4/19 09:34
 * @description 成功
 * @author mrdonkey
 */
class Success:Action() {
    override fun getManConclusion(concreteElementA: Man) {
        println("${concreteElementA::class.java.simpleName} ${this::class.java.simpleName}时,背后多半有一个伟大的女人。")
    }

    override fun getWomanConclusion(concreteElementB: Woman) {
        println("${concreteElementB::class.java.simpleName} ${this::class.java.simpleName}时,背后大多有一个不成功的男人。")
    }
}
/**
 * @create on 2020/4/19 09:35
 * @description 失败
 * @author mrdonkey
 */
class Failing : Action() {
    override fun getManConclusion(concreteElementA: Man) {
        println("${concreteElementA::class.java.simpleName} ${this::class.java.simpleName}时,闷头喝酒,谁也不用劝。")
    }

    override fun getWomanConclusion(concreteElementB: Woman) {
        println("${concreteElementB::class.java.simpleName} ${this::class.java.simpleName}时,眼泪汪汪,谁也劝不了。")
    }
}
/**
 * @create on 2020/4/19 09:36
 * @description 恋爱
 * @author mrdonkey
 */
class Amativeness :Action(){
    override fun getManConclusion(concreteElementA: Man) {
        println("${concreteElementA::class.java.simpleName} ${this::class.java.simpleName}时,凡事不懂也要装懂。")
    }

    override fun getWomanConclusion(concreteElementB: Woman) {
        println("${concreteElementB::class.java.simpleName} ${this::class.java.simpleName}时,遇事懂也要装作不懂。")
    }
}

抽象Person

/**
 * @create on 2020/4/19 09:42
 * @description 抽象
 * @author mrdonkey
 */
abstract class Person {
    //接受状态
    abstract fun accept(visitor: Action)
}

具体Person

/**
 * @create on 2020/4/19 09:45
 * @description 男人
 * @author mrdonkey
 */
class Man:Person() {
    override fun accept(visitor: Action) {
        visitor.getManConclusion(this)
    }
}
/**
 * @create on 2020/4/19 09:46
 * @description Woman
 * @author mrdonkey
 */
class Woman : Person() {
    override fun accept(visitor: Action) {
        visitor.getWomanConclusion(this)
    }
}

具体的Person类中用到的双分派技术:

客户端将具体的状态作为参数传递给男人accept方法,完成一次分派,然后男人调用作为具体状态中的方法“男人的反应”,同时将自己作为参数传递进去。这便完成了第二次分派。双分派意味着得到请求的操作决定于请求的种类和两个接收者的类型。accept方法就是不仅决定于状态类的具体状态,还决定于它访问的人的类别

对象结构(针对不同的person来遍历得到不同的反应)

/**
 * @create on 2020/4/19 09:47
 * @description 对象结构
 * @author mrdonkey
 */
class ObjectStructure {

    private val elements = arrayListOf()

    //增加
    fun attach(element: Person) {
        elements.add(element)
    }

    //减少
    fun detach(element: Person) {
        elements.remove(element)
    }

    //展示
    fun display(visitor: Action) {
        elements.forEach { person ->
            person.accept(visitor)
        }
    }
}

客户端代码

/**
 * @create on 2020/4/19 09:51
 * @description 客户端
 * @author mrdonkey
 */
class Client {

    companion object {
        @JvmStatic
        fun main(vararg arg: String) {
            val objectStructure = ObjectStructure()
            objectStructure.attach(Man())
            objectStructure.attach(Woman())
            //成功时的反应
            objectStructure.display(Success())
            //失败时的反应
            objectStructure.display(Failing())
            //恋爱时的反应
            objectStructure.display(Amativeness())
        }
    }
}

测试结果:

Man Success时,背后多半有一个伟大的女人。
Woman Success时,背后大多有一个不成功的男人。
Man Failing时,闷头喝酒,谁也不用劝。
Woman Failing时,眼泪汪汪,谁也劝不了。
Man Amativeness时,凡事不懂也要装懂。
Woman Amativeness时,遇事懂也要装作不懂。

如果再加一个结婚状态,只需要新增一个具体的结婚状态类,然后在Client中添加即可。

访问者模式 

访问者模式(Visitor),表示一个作用与某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提定义作用于这些元素的新操作

28.男人和女人-访问者模式(大话设计模式Kotlin版)_第2张图片

 访问者模式的基本代码

抽象访问者

/**
 * @create on 2020/4/19 10:16
 * @description 抽象访问者
 * @author mrdonkey
 */
abstract class Visitor {
    abstract fun visitorConcreteElementA(concreteElementA: ConcreteElementA)
    abstract fun visitorConcreteElementB(concreteElementB: ConcreteElementB)

}

具体访问者 

/**
 * @create on 2020/4/19 10:21
 * @description 具体访问者A
 * @author mrdonkey
 */
class ConcreteVisitorA : Visitor() {
    override fun visitorConcreteElementA(concreteElementA: ConcreteElementA) {
        println("${concreteElementA::class.java.simpleName}被${this::class.java.simpleName}访问")
    }

    override fun visitorConcreteElementB(concreteElementB: ConcreteElementB) {
        println("${concreteElementB::class.java.simpleName}被${this::class.java.simpleName}访问")
    }
}

/**
 * @create on 2020/4/19 10:21
 * @description 具体访问者B
 * @author mrdonkey
 */
class ConcreteVisitorB : Visitor() {
    override fun visitorConcreteElementA(concreteElementA: ConcreteElementA) {
        println("${concreteElementA::class.java.simpleName}被${this::class.java.simpleName}访问")
    }

    override fun visitorConcreteElementB(concreteElementB: ConcreteElementB) {
        println("${concreteElementB::class.java.simpleName}被${this::class.java.simpleName}访问")
    }
}

抽象Elemet

/**
 * @create on 2020/4/19 10:17
 * @description 抽象元素
 * @author mrdonkey
 */
abstract class Element {
    //接受访问
    abstract fun accept(visitor: Visitor)
}

具体的Element

/**
 * @create on 2020/4/19 10:19
 * @description 具体元素A
 * @author mrdonkey
 */
class ConcreteElementA:Element() {
    override fun accept(visitor: Visitor) {
        visitor.visitorConcreteElementA(this)
    }
    /**
     * 具体的其他方法
     */
    fun operationA(){

    }
}
/**
 * @create on 2020/4/19 10:19
 * @description 具体元素B
 * @author mrdonkey
 */
class ConcreteElementB : Element() {
    override fun accept(visitor: Visitor) {
        visitor.visitorConcreteElementB(this)
    }

    /**
     * 具体的其他方法
     */
    fun operationB(){

    }
}

对象结构

/**
 * @create on 2020/4/19 10:24
 * @description 对象结构
 * @author mrdonkey
 */
class ObjectStructure {
    private val elements = arrayListOf()
    fun attach(element: Element) {
        elements.add(element)
    }

    fun detach(element: Element) {
        elements.remove(element)
    }

    fun accept(visitor: Visitor) {
        elements.forEach { element ->
            element.accept(visitor)
        }
    }
}

 客户端

/**
 * @create on 2020/4/19 10:24
 * @description 对象结构
 * @author mrdonkey
 */
class ObjectStructure {
    private val elements = arrayListOf()
    fun attach(element: Element) {
        elements.add(element)
    }

    fun detach(element: Element) {
        elements.remove(element)
    }

    fun accept(visitor: Visitor) {
        elements.forEach { element ->
            element.accept(visitor)
        }
    }
}

测试结果

ConcreteElementA被ConcreteVisitorA访问
ConcreteElementB被ConcreteVisitorA访问
ConcreteElementA被ConcreteVisitorB访问
ConcreteElementB被ConcreteVisitorB访问

 总结 

 访问者模式的使用前提

数据结构相对稳定 。男人和女人的这个情景可以用访问者模式来实现,是因为在性别上只有男人和女人两类。如果性别不止男女,而是可以有很多中性别,那就意味着状态的抽象类就不可能稳定了,每增加一种类别,都需要在状态类及其子类中增加一个方法,这就不符合开放-封闭原则。

访问者模式的目的:

把处理从数据结构分离除了。(把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化)

访问者模式的使用场景

系统有比较稳定的数据结构,又有易于变化的算法,使用访问者模式就是比较合适的,因为访问者模式将有关的行为集中到一个访问者对象中。反之系统数据结构对象易于变化,经常有新的数据结构对象要新加,就不适合使用访问者模式。

GoF四人中的一个作者就说过:‘大多数时候你并不需要访问者模式,但当一旦你需要访问者模式的时候,那就是真正的需要它了’。事实上,我们很难找到数据结构不变化的情况,所以访问者模式的机会也就不会多了。

访问者模式的优缺点

优点:增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中

缺点:增加新的数据结构变得困难

 

你可能感兴趣的:(设计模式)