TypeScript设计模式之中介者、观察者

看看用TypeScript怎样实现常见的设计模式,顺便复习一下。 学模式最重要的不是记UML,而是知道什么模式可以解决什么样的问题,在做项目时碰到问题可以想到用哪个模式可以解决,UML忘了可以查,思想记住就好。 这里尽量用原创的,实际中能碰到的例子来说明模式的特点和用处。

中介者模式 Mediator

特点:为减少对象间的互相引用而引入的一个中介对象,用来来封装一系列对象的互相操作。

用处:当多个对象间需要互相引用且互相频繁操作时可以考虑中介者模式,如MVC里的Controller。

注意:中介者本身的复杂度。

下面用TypeScript简单实现一下中介模式: 现在滴滴打车其实就可以算是中介,就以这个例子吧,对象主要就是用户,车主和平台。

先定义用户, 车主和中介者接口: 用户的行为是叫车,车主是接送,中介者则需要维护用户和车主列表并且知道车的状态和提供叫车服务。

interface Client{
    getTaxi();
    pay();
} 

interface Car{
    isWorking: boolean;

    startWork();
    finishWork();
}

interface Mediator{

    registerClient(client: Client);
    registerCar(car: Car);

    getCar(): Car;
    pay(car: Car);
    updateCarStatus(car: Car);
}

接口定义好了就可以写实现了: 用户的实现,持有中介者的引用,用来注册,叫车和付款,本来是没必要存taxi的,只需要个id就可以了,具体是由中介去做匹配,不过这里为了简单就直接存对象了

class User implements Client{
    taxi: Car;

    constructor(private mediator: Mediator){
        this.mediator.registerClient(this);
    }

    getTaxi(){
        this.taxi = this.mediator.getCar();
        if(this.taxi){
            console.log('车来了');
        } else {
            console.log('没叫到车');
        }
    }

    pay(){
        this.mediator.pay(this.taxi);
        console.log('付款');
    }
}

车主的实现,同样需要持有中介者引用来领任务和报状态

class Taxi implements Car{
    isWorking: boolean = false;

    constructor(private mediator: Mediator){
        this.mediator.registerCar(this);
    }

    startWork(){
        console.log('有人叫车');
        this.isWorking = true;
        this.mediator.updateCarStatus(this);
    }

    finishWork(){
        console.log('送完这趟了');
        this.isWorking = false;
        this.mediator.updateCarStatus(this);
    }
}

中介的实现,中介的作用就是提供车子服务,这里为了简单没维护用户与车子的关系

class DiDi implements Mediator{
    private clientList: Array<Client> = [];
    private carList: Array<Car> = [];

    registerClient(client: Client){
        this.clientList.push(client);
    }

    registerCar(car: Car){
        this.carList.push(car);
    }

    getCar(): Car{
        let car = this.carList.find(o=>!o.isWorking);
        car.startWork();
        return car;
    }

    pay(car: Car){
        car.finishWork();
    }

    updateCarStatus(car: Car){
        console.log(`车子状态:${car.isWorking ? '工作' : '闲置'}`);
    }
}

跑一下看看:

let didi = new DiDi();
let taxi = new Taxi(didi);
let user = new User(didi);
user.getTaxi();
user.pay();

//结果
有人叫车
车子状态:工作
车来了
送完这趟了
车子状态:闲置
付款

这样,用户的目的只是叫车,对中介说声,中介派出车,用户不管是什么车,哪来的,把我送到目的地就可以了。 这就是中介者模式的作用,逻辑都在自己这里,用户不需要管车,车子也不用管用户,一个叫车,一个接单,互不干扰,突然想到了拉皮条。。。 当然也是因为这里聚集了各方面的逻辑,所以要注意中介者本身的复杂度,中介者本身也需要良好的设计和模式来提高代码的可读性和可维护性。

观察者模式 Observer

特点:定义了对象间的一对多关系,当对象状态改变时,其他订阅了这个对象的对象就会收到通知。

用处:当一个对象状态的改变时需要其他对象也做出响应时可以考虑观察者模式,如网络聊天里的群。

注意:与中介者的区别。

下面用TypeScript简单实现一下观察者模式: 就以上面说的群聊天为例,群里的每个人都是注册到群里的对象,任何一个人发了信息其他人都能收到。

先定义群和群用户的接口: 群需要知道有哪些用户注册进来了,并且在有人发消息时去通知所有注册的人。 用户则需要发送消息和接收消息。

interface Observer{
    name: string;

    sendMsg(msg: string);
    receiveMsg(sender: Observer, msg: string);
}

interface Subject{
    register(observer: Observer);
    unregister(observer: Observer);
    sendMsg(sender: Observer, msg: string);
}

实现用户和群,用户在发消息时需要往群里发,群收到消息后通知所有注册的人

class User implements Observer{
    constructor(public name: string, private subject: Subject){
        this.subject.register(this);
    }

    sendMsg(msg: string){
        console.log(`${this.name} 发送 ${msg}`);
        this.subject.sendMsg(this, msg);
    }

    receiveMsg(sender: Observer, msg: string){
        console.log(`${this.name} 收到来自${sender.name}的消息: ${msg} `);
    }
}

class Group implements Subject{
    private userList: Array<Observer> = [];

    register(observer: Observer){
        this.userList.push(observer);
    }

    unregister(observer: Observer){
        var index = this.userList.indexOf(observer);
        if (index > -1) {
            this.userList.splice(index, 1);
        }
    }

    sendMsg(sender: Observer, msg: string){
        console.log(`群收到${sender.name}发信息:${msg},通知所有人`);
        this.notify(sender, msg);
    }

    private notify(sender: Observer, msg: string){
        this.userList.forEach(user=>user.receiveMsg(sender, msg));
    }
}

写段代码测试一下:

let group = new Group();
let jim = new User1('jim', group);
let brook = new User1('brook', group);
let lucy = new User1('lucy', group);

jim.sendMsg('hello');
lucy.sendMsg('well done!');
//结果:
jim 发送 hello
群收到jim发信息:hello,通知所有人
jim 收到来自jim的消息: hello 
brook 收到来自jim的消息: hello 
lucy 收到来自jim的消息: hello 

lucy 发送 well done!
群收到lucy发信息:well done!,通知所有人
jim 收到来自lucy的消息: well done! 
brook 收到来自lucy的消息: well done! 
lucy 收到来自lucy的消息: well done! 

只有要人发消息,所有注册的人都会收到,跟广播一样。 其实观察者模式可以做得更通用,类似一个消息中心,所有注册的对象按照一定协议实现匹配事件的方法来获取通知,消息中心不需要知道是什么类型的对象注册了,只要实现这个方法,那相关事件有通知时这个方法就会被调到,这样基本没有耦合度,有兴趣的朋友可以参考我之前写的一个win10开源库:LLQNotify,就是用这种方式实现的。

另外,与中介者模式的区别在于:虽然都是注册回复,但观察者是分发性的,注册的人都能收到,而且中介者则是单一的,使用者发个请求,中介者回一个,使用者不需要知道到底是谁回的,中介隐藏了对象之间的交互。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏项勇

笔记78 | 解读一个闹钟代码

最近因工作需要做一个定时器,一看需求,深思极恐: 1.定时发送开关指令; 2.可设置周期循环; 这不就是一个标准的闹钟吗? 哎呀,烧脑~

652
来自专栏运维一切

php docker web终端的折腾 原

#php是天下最好的语言,没有之一。 我非常喜欢php,我听过一个高手的讲座,讲php的编译原理,发现如果就php语言开发而言的确技术上有高低之分。一比较才发现...

581
来自专栏编程思想之路

Android6.0锁屏源码分析之界面布局分析

大致先介绍一下锁屏界面 Android的锁屏界面可以分为两级, 一级锁屏界面暂且称之为锁屏界面LockScreen,即平常用到的无需任何输入和验证,只需滑动解锁...

2828
来自专栏非著名程序员

Android新组件RecyclerView介绍,其效率更好

RecyclerView介绍 非著名程序员 ? 今天我们首先来说为什么要介绍这个新组件RecyclerView,因为前几天我发布了一个常用面试题ListVie...

1869
来自专栏HT

通过HTML5的Drag and Drop生成拓扑图片Base64信息

HTML5 原生的 Drag and Drop是很不错的功能,网上使用例子较多如 http://html5demos.com/drag ,但这些例子大部分没实际...

1878
来自专栏落花落雨不落叶

史上最“脑残”的“抢火车票”程序(node.js版)

3066
来自专栏刘望舒

lifecycle-mvp,像前端那样组合式写页面

1714
来自专栏Java成神之路

Java企业微信开发_04_消息推送之发送消息(主动)

(1)流程不同:发送消息是第三方服务器主动通知微信服务器向用户发消息。而被动回复消息是 用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接收...

1055
来自专栏Flutter入门

Weex是如何在Android客户端上跑起来的

Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,template、style、script,借鉴了成熟的MV...

1614
来自专栏机器学习原理

爬虫篇(4)——qq音乐文件的爬取

前言:qq音乐文件的批量爬取,涉及到的json对网站的解析,请求的有效伪装,字符串的操作等。 目的:爬取想要的音乐资源,包括需要付费下载的音乐。 流程 包括...

3077

扫码关注云+社区