[程序员的自我修养-做一个牛逼的程序员,就得从设计模式入手]-外观模式

大家好,我是杰森小哥哥。

很久没有动手写设计模式的笔记了。今天继续了,做事要有始有终。

最近刚换了砖厂,深感学习和自我提升甚是重要。

今天要写的是外观模式(facade ,这个法语词的读音很有味道,建议百度下,别读错哦)。

每每说到设计模式,我都要想一个问题。这个鬼东西到底是为了什么问题而存在的。有动机才会有动力。

说个例子。我今年去体检过两次。一次是体检机构,一次是一家私立医院。

先说结论,这个体检机构的效率和服务比私立医院好。(具体医院,具体分析,不代表行业现状)

有一个点我想说下:体检机构的登记台只要一张身份证就能完成录入资料,缴费的工作,所有的项目科室还都在同一层楼。

且体检表上有每一个项目的门牌号,分配了足够的人力对人群进行分流,不会造成客户排队过久的情况。

而私立医院这些做的不够好。

不得不说,术业有专攻啊。

客户体验真真重要。

回到主题,是不是能联想到我们开发APP的情形呢?用户其实不在乎你的后台运作涉及多少个环节。就像我去体检,你只要告诉我交身份证,交钱,体检,拿结果就可以了。其它的我不需要知道。

而facade模式就等同于上面所说的体检的前台,它就是这个和用户直接对话的窗口(facade是原意就是建筑物的正面)。

这个模式下只会会告诉用户必须的步聚是哪些,而其它的细节是不曝露给用户的。

那么, 我们就可以归纳出facade模式下的基本结构 ,如下:

main或者client -> 直接和客户对话的入口;

facade类-> 整个系统和用户交互的对象类;

handlers->受facade类调配的处理类;不用曝露给用户;可以是多个;

以上是基础结构,文末再说说一种优化方案。

下面的codeTime.我的两点建议。

一,如果你是用电脑看的,请打开IDE,写一个facade模式的demo.

二,自己设定一种场景,不用照搬我下面的场景。

以下代码是使用kotlin写的,但并不障碍大家对主题内容的学习。

因为我觉得kotlin源于java,而优于java。

是值得学习的一门语言,所以, 我保留了。

我以买保险举例。

以下文字过多,总结起来就是:用户买保险,其实只是选择产品,输入信息,提交订单,购买的过程而已。用户只需要一个对接的页面,而后台有很多的子系统在为之服务。

package imeegaa.pattern.facade

/**今天看了facade(外观)设计模式,首先要说的是这个单词的发音不是大家所看到的那样/feikeid/的读法,
而是读作 /fesade/(大致是这样,正确音标查百度),这是源自一个法文词,说的是建筑物的正面
用一个手机APP买保险的例子来说明下,大家就能明白。

在手机上买保险,忽略一些非必要操作,最后的操作可以简化成以下的三个操作:选择产品,填写信息,付款。
而其实app后台要做的事情却不止这些:结合与用户的交互就是以下的步骤:判断该地区和用户是否可售;判断该产品是否有优惠;
判断该用户是否有优惠等等;用户填写信息;生成订单;付款;检查付款状态;出单。

facade模式就是一个入口,用户只需要做对他来说,需要做的事情就行了。不需要去关注后台的复杂处理;

所以我们的代码结构就是下面这样的:
main:入口类
facade: 接待类,处理用户的请求;在该类中控制用户的操作流程;屏蔽用户无须关注的流程
handler1、handler2、handler3...: 多个处理类;针对不同的事件生成不同的处理方案;
 注意:结合了结城浩的《图解设计模式》和其它的网上资料。facade模式因为系统要添加一个新的功能,就要对facade类进行修改,违背了“开闭原则”。
 网上有教程建议是建立一个abstractFacade类。所有的具体外观类都继承它,可以解决一些问题。
 */
fun main() {
    PolicyOrderFacade().buyPolicy()
}

接下来,是对接用户的facade类。

package imeegaa.pattern.facade

import java.time.LocalDateTime
import java.util.*
import kotlin.random.Random


class PolicyOrderFacade {

    fun buyPolicy() {
        val scanner = Scanner(System.`in`)
        println("please tell me which product you want to buy,input the number? 1 健康险;2 财产险;3 寿险")
        val productCode = scanner.nextLine()

        println("please input your name")
        val realName = scanner.nextLine()
        println("please input your gender 'm' or 'f'")
        val gender = scanner.nextLine()
        println("please input your age")
        val age = scanner.nextInt()

        val policy = UserInfoHandler().fillPaper(realName, age, gender)
        policy.productCode = productCode
        DiscountHandler().getDiscount(policy)

        OrderHandler().createdOrder(policy)
        println("do you want to pay the order now ? 'y' or 'n' (ignore case)")
        val payFlag = scanner.nextLine()
        PaymentHandler().pay(payFlag, policy)
    }


}

上面的代码是通过和控制台输入文本模拟交互的,没有考虑校验等细节。

其实,大家能看出来,有多个类在这里分别处理不同的工作。而我所举的保险这个行业,真的就是一个流程会有很多复杂的子系统在工作。外观模式必不可少。

 

以下是保险类。

package imeegaa.pattern.facade

class Policy {
    var realName: String = ""
    var gender: String = "M"
    var age: Int = 0
    var productCode: String = ""
    var orderId: Int = 0
    var price: Double = 100.00
    var discount:Double = 1.0
    var status :String = "0" // 0 init ; 1 paid
}

记录用户信息。

package imeegaa.pattern.facade

class UserInfoHandler {
    // 填写用户名称
    fun fillPaper(name: String, age1: Int, gender1: String):Policy {
        return  Policy().apply {
            realName = name
            age = age1
            gender = gender1
        }
    }
}

 判断是否有优惠。

package imeegaa.pattern.facade

import kotlin.random.Random

class DiscountHandler {
    //查看该用户是否有优惠项
    fun getDiscount(policy: Policy) {
        policy?.apply {
            discount = Random(123).nextDouble()
            println("the discount is $discount")
        }
    }
}

最后,生成订单。

package imeegaa.pattern.facade

import kotlin.random.Random

class OrderHandler {
    //生成订单
    fun createdOrder(policy: Policy) {
        policy?.apply {
            orderId = Random(12).nextInt(1000,9999)
            println("the applyOrder $orderId is created.")
        }
    }
}

 

由用户决定是否付款。

 

package imeegaa.pattern.facade

import java.time.LocalDateTime

class PaymentHandler {
    //付款
    fun pay(status:String,policy: Policy) {
        policy.status = status
        println("the policy $policy is valid from ${LocalDateTime.now()}")
    }
}

 

需要说明下,看似每个handler的代码量极少,完全可以在main里写完。但是上百人参与的大系统里,一个类的功能是极复杂的,牵一发而动全身。

都说专业的人做专业的事,那代码也是。

我们要以小见大。

大家看到的很多handler里都有apply这个方法。这是kotlin特有的内联扩展函数,我们可以理解为apply是对一个对象实例进行批量的get/set操作。

 

代码是在我自己的Matebook上跑过的,大家只需要把包名改下应该就可以使用了。

但是,我的建议是自己去设定应用场景,再去写自己的demo。

建议大家自己写过之后,把链接发到评论里,我们一起学习。

开头说的,这个facade模式是有缺陷的,因为只要系统中加入一个子系统,那么facade类就要进行修改,而我们设计模式的提倡设计要符开“开闭原则”。反对修改,提倡扩展。

方法就是建立abstractFacade类,facade作为它的子类,需要添加子系统(handler)时,只是修改具体的facade类,而面向用户的抽象类依旧不变。

说到这,其实我想说,设计模式本身并不是死的比。

如这种先建立抽象类,再使用具体子类进行功能操作的套路其实模板方法里也有类似的结构。万物相通,积累多了,也就能明白了。

这就是我今天分享的facade(外观)设计模式。谢谢大家的阅读。

文中有错误之处的话,请大家在评论区留言。

我是杰森,愿把知识用快乐的方式和他人分享的美男子。

 

 

你可能感兴趣的:(个人笔记,设计模式)