服务定位器

        这种设计模式很简单很好理解。我们使用它为某服务提供一个全局访问入口来避免使用者与该服务具体实现类之间产生耦合。 它将一个服务是“是什么”(具体实现类型)和“在什么地方”(我们如何得到它的实例)与需要使用这个服务的代码解耦了。

        由于我们需要使用这个服务的地方都使用服务定位器获得,这样我们服务内部的改动或者外观接口的变化都被服务定位器封装了,也就不需要在改动服务时,更新所有服务使用的代码模块了。而且我们还可以自由的替换一个服务给调用者来满足我们的一些需求,比如不同的日志功能功夫帮助我们显示我们关注的日志。你也可以定义一个服务类,里面的服务实现什么也不做,这样服务调用者可以无感调用,从而达到一些功能禁用的效果。

        服务定位器用起来像一个更灵活、更可配置的单例模式,当被合理地使用时,它能够让你的代码更有弹性,而且几乎没有运行时的损失。但它毕竟还是一个全局性的东西,想想全局变量会给后期维护带来的麻烦,你还是需要谨慎使用它。

下面是这个模式的类图:

 下面是一个简单的代码示例:

// 音频服务接口
export interface Audio{
    playSound(soundID: number);
    stopSound(soundID: number);
    stopAllSounds();
}

// 一种具体的音频播放服务实现
export class ConsoleAudio implements Audio {
    playSound(soundID: number) {
        // Play sound using console audio api...
    }

    stopSound(soundID: number) {
        // Stop sound using console audio api...
    }

    stopAllSounds() {
        // Stop all sounds using console audio api...
    }
}

// 一个什么也不做的音频服务实现,相当于一个NULL
export class NullAudio implements Audio {
    playSound(soundID: number) {
        // do nothing
    }

    stopSound(soundID: number) {
        // do nothing
    }

    stopAllSounds() {
        // do nothing
    }
}
// 日志装饰器装饰播放音频服务,在调用服务接口时输出我们想要的日志
export class LoggedAudio implements Audio {
    private _wrapped: Audio;

    constructor(wrapped: Audio) {
        this._wrapped = wrapped;
    }

    log(str: string) {
        // Code to log message...
    }

    playSound(soundID: number) {
        this.log("play sound");
        this._wrapped.playSound(soundID);
    }

    stopSound(soundID: number) {
        this.log("stop sound");
        this._wrapped.stopSound(soundID);
    }

    stopAllSounds() {
        this.log("stop all sounds");
        this._wrapped.stopAllSounds();
    }
}

// 服务定位器
export class Locator {
    static private _nullService: NullAudio = new NullAudio();
    static private _service: Audio;

    // 保证都能获取一个服务,哪怕什么也不做,这样客户程序获取服务时就不需要判空了
    // 通过使用一个空服务,我们也可以达到暂时禁用一个系统的目的
    static initialize() {
        this._service = this._nullService;
    }

    // 获取服务
    static getAudio(): Audio {
        return this._service;
    }

    // 设置服务
    static provide(service: Audio) {
        // 保证都能获取一个服务,哪怕什么也不做,这样客户程序获取服务时就不需要判空了
        if (service == null) {
            return this._nullService;
        }
        this._service = service;
    }
}

test() {
    // 创建一个音频服务并把它注册到服务定位器中
    let consoleAudio = new ConsoleAudio();
    Locator.provide(consoleAudio);

    let audio = Locator.getAudio();
    audio.playSound(10010);
}

enableAudioLogging() {
    // 装饰音频服务
    let service = new LoggedAudio(Locator.getAudio());
    // 替换当前服务
    Locator.provide(service);
}

下面是该模式的一些参考:

1. 服务定位器模式在很多方面和单例模式非常相近,所以值得考虑两者来决定哪一个更适合你的需求。

2. Unity框架和Cocos Creator把这个模式和组件模式结合起来,并使用在了GetComponent()方法中。

3. Microsoft的XNA游戏开发框架将这个模式内嵌到它的核心Game类中。每个实例有一个GameServices对象,能够用来注册和定位任何类型的服务。

参考:Service Locator · Decoupling Patterns · Game Programming Patterns

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值