最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 设计模式系列 -- 单例模式

    正文概述 掘金(Do_Better)   2021-03-28   604

    核心概念

    基本要求:

    1. 保证此系统中只有一个类实例
    2. 必须由创建本类自行创建
    3. 必须提供创建的方法

    创建的两种方式:

    1. 饿汉式:在类加载时就创建好,
      1. 不会发生线程问题,创建速度快。
      2. 会消耗内存,一些无用的类也会被实例化。
    2. 懒汉式:是在调用时就会创建
      1. 可能回发生线程安全的问题,但在 js 中不会产生这种状况。
      2. 防止垃圾产生

    类图

    单例模式的类图比较简单,如下一个基本的单例模式类图:

    设计模式系列 --  单例模式

    在 java 里可以通过在类里面实例化类,来实现饿汉式的创建方法,但必须私有化类的构造方法,在提供返回实习的方法,懒汉式的实现时在提供的调用方法里判断是否已经实现,如过有则直接返回实例,否则实例化对象后再返回,但这样有锁就会产生线程安全问题,所以要加上同步锁关键字 synchronized 来解决线程安全问题。

    javaScript 中无类加载的概念,可能不太能体现出单例模式的优势,但思想可以借鉴,


    应用

    单例模式的思想无非就是在整个系统中只向外提供一个实例,利用这种思想,我们可以对比其他场景中的应用。比如

    1. windows 在操作文件时,它就使用的是单线程,如果使用多线程的话,可能会造成意想不到的结果。
    2. 数据库生成唯一主键,如果使用多个实力去生成唯一主键,那写入的时候可能会出现错误的情况。
    3. 登录组件,弹框组件等这些系统想要单例的组件,如果调用时候会再生成一个,那就达不到我们想要的结果,我们可以想办法将这个组件单立起来,每次调用只返回已将创建好的的那一个。

    要知道应用这些思想并不只适用于代码层面,也可以是模快之间,系统之间,服务器等。


    栗子

    唯一实例化

    
    // 单例构造函数生成器
    function SingletionNew(Constructor, ...params) {
        let instance;
        return function () {
            if (instance){ 
                return instance
            } 
            return instance = new Constructor(...params)
        }
    }
    
    // 想要生成唯一的 登录实例
    let login = function (name) { 
        console.log('create')
        this.name = name
        this.login = function(){
            console.log('login')
        }
    }
    
    // 不通过单例构造器创建
    let a = new login()
    let b = new login()
    // 生成多个实例
    console.log(a === b) // false
    
    
    // 通过单例构函数创建
    let singletion_Login_Constructor = SingletionNew(login, 'tom')
    // 生成一个能生成单例的构造函数
    let singletionLoginA = singletion_Login_Constructor();
    let singletionLoginB = singletion_Login_Constructor();
    let singletionLoginC = singletion_Login_Constructor();  
    let singletionLoginD = singletion_Login_Constructor()
    
    // 即时创建再多,也只会打印一次 create,,也就是只创一次
    console.log(singletionLoginA === singletionLoginB) // true
    

    唯一调用

    // 单次调用方法生成器
    function SingletionApply(fn, ...params) {
        let result;
        return function () {
            if (result) return result 
            console.log(fn,params)
            return result = fn(...params)
        }
    }
    
    // 想要唯一调用的函数
    let doneSomeThing = function (thing) { 
        console.log('havedoneSome:', thing)
        return true // 返回 true 是为了做标记说已将创建了
    } 
    
    // 生成唯一调用方法的函数
    let singletion_login_call = SingletionApply(doneSomeThing, 'eat') 
    singletion_login_call()
    singletion_login_call()
    singletion_login_call()
    
    // 多次调用控制台只会打印一次 havedoneSome: eat
    

    可以看到,不管是方法调用还是实例生成,都是使用到了一个构造器,这个构造器的功能就是能够让这个构造方法生成单一实例,或生成一个唯一调用的方法 的一个类似于包装函数一样,这样可以让功能单一化,抽取共用的部分,达到复用的效果,遵守单一职责原理

    但美中不足的是,原对象还可以直接被改变,也没有提供向外获取实例的方法,所以这并不符合单一职责概念,只是是用了一些 js 中的特殊的“手段”使其产生单例模式的效果。

    终极大招

    使用装饰器模式模拟类加载过程,其接口定义为单例模式的类图,动态的扩展且不回改变对象原有的封装性符合开闭原则,这也是为什么我不使用函数内变量来模拟私有变量的原因。

    function Login(name) {
        this.name = name
        // 自己的业务逻辑  
    }
    
    // 要单例化的对象target,及创建时传参params
    // 返回值覆盖原来的对象
    function SingletionDecorater(target, ...params) {
        // 为类对象上添加实例化对象
        target.getInstance = function () {
            return target.instance
        }
    
        target.instance = new target(...params) // 实例化一次
    
        // 添加权限
        target = new Proxy(target, {
            set(target, key, value) { // 设置只读属性
                if (key === 'instance' || key === 'getInstance') {
                    console.warn(`warn:can not set a new value for ${key}`)
                    return
                }
                target[key] = value
            }, // 调用 new 时返回创建好的实例
            construct(target) {
                return target.instance
            }
        })
        return target
    }
    
    // 使用 SingletionDecorater 装饰的函数,保证三大要点 
    Login = SingletionDecorater(Login, 'login') 
    
    // 尝试修改
    Login.instance = 555 // can not set a new value for instance
    
    let login = Login.getInstance() // Singletion { name: 'Login' }  
    let a = new Login('Login1') // 无论怎么创建都返回第一次创建的   
    let c = new Login('Login2') //  Singletion { name: 'Login' }  
    console.log(a) //Singletion { name: 'Login' } 
    console.log(c === a)// true
    console.log(c === login) // true
    
    

    可以看到 Login = SingletionDecorater(Login, 'login')  类似 java 中创建单例对象的类加载过,并且此过程可懒汉,可饿汉,根据自己的装饰时机实现不同的效果。

    这样就暂且完美的解决了单例模式不能再 JavaScript 中完美体现的问题,更多装饰器模式相关请移步装饰器章节。

    为什么不适用 JS 内置修饰器,是因为我的 node 版本较低,当然,其原理都是一样的。

    参考:

    JavaScript 设计模式与开发实战 -- 曾探
    JavaScript 设计模式 -- 张容铭
    设计模式实训 --刘伟

    文章有不妥之处还请指出,持续更新


    下载网 » 设计模式系列 -- 单例模式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元