Skip to content

单例模式:一只独一无二的猫 #13

@bitfishxyz

Description

@bitfishxyz

单例模式

什么是单例模式,如何实现单例模式这个话题,网络上已经谈论的非常多了。我就不废话了,这里我谈谈我对单例模式的理解以及Java和JavaScript中的单例模式的不同。

下面是Java中实现单例模式的两种方法。

/**
 * 饿汉式
 */
class BitFish{
    private static BitFish bitfish = new BitFish();

    private BitFish(){};

    public static BitFish getInstance(){
        return bitfish;
    }
}

/**
 * 懒汉式
 */
class SaltFish{
    private static volatile SaltFish saltfish;

    private SaltFish(){};

    public static SaltFish getInstance(){
        if (saltfish == null){
            synchronized (SaltFish.class){
                if (saltfish == null){
                    saltfish = new SaltFish();
                }
            }
        }
        return saltfish;
    }
}

public class Test {
    public static void main(String[] args) {
        BitFish bitFish1 = BitFish.getInstance();
        BitFish bitFish2 = BitFish.getInstance();
        assert bitFish1 == bitFish2;
        
        SaltFish saltFish1 = SaltFish.getInstance();
        SaltFish saltFish2 = SaltFish.getInstance();
        assert saltFish1 == saltFish2;
    }
}

单例模式在Java中的需求非常高,Spring框架解决的一个痛点就是通过IOC容器提供了单例模式的经典实现。
我觉这种需求主要原因是Java语言的设计特色。Java语言中,一切对象都是类的实例。我们编写的代码都以是一个个class文件的形式存在。

如果我们想创建一个对象,那么必须先要有类,然后通过类来实例化对象。

我家有只猫,名叫妙妙,它特别能吃。同时有一个其他猫不具有的能力:敲键盘,写代码!!不满你说,你现在看到的这段话,其实就是妙妙敲asdfqdfaeweal,miao m

那么现在问题来了,如果我想在java中创建一个妙妙对象,那么我要怎么做呢?

神说:必须先有类,才能有对象。

那么怎么写呢?妙妙是一只喵喵,所以我可以先写一个Cat类。

class Cat{
    public String name;

    public Cat(String name) {
        this.name = name;
    }
    
    public void eat(){
        System.out.println(name + " 又吃了一条鱼");
    }
}

public class Test {
    public static void main(String[] args) {
        Cat miaomiao = new Cat("miaomiao");
    }
}

这可能是一个写法,但是问题来了,我们的妙妙和外面的妖艳贱货不同啊,它还能帮我敲代码adfwafe

这时候我们就必须扩展下Cat这个类,可能是这样的写法:

class MiaoMiao extends Cat{
    public MiaoMiao(String name) {
        super(name);
    }
    public void coding(){
        System.out.println("I love coding");
    }
}

public class Test {
    public static void main(String[] args) {
        MiaoMiao miaomiao = new MiaoMiao("miaomiao");
        miaomiao.coding();
    }
}

这样是可以,但是问题又来了,我们的MiaoMiao类在逻辑上有什么意义吗?这个类的存在完全是为了我们的妙妙对象服务的。这是因为Java语法的限制,我们没办法绕过类来直接创建对象。我们凭空的写出了MiaoMiao这个逻辑上没有意义的类。同时我们这个类只应该有一个实例,毕竟妙妙是宇宙中唯一的一个个体。

其实这个问题在JavaScript中不存在,那么在JS中我们可以怎么写呢?

假设这是个Cat构造函数:

function Cat(name){
  this.name = name;
}
Cat.prototype.eat = function(){
  console.log(this.name + " 又吃了一条鱼");
}

如果我们现在想创建一个妙妙,我们不需要编写一个MiaoMiao构造函数,我们直接使用对象字面量的写法就行了。

let miaomiao = {
  name: "miaomiao",
  coding(){
    console.log("I love coding");
  }
}

当然同时我们还需要维护下正确的原型链

Object.setPrototypeOf(miaomiao, Cat.prototype)

这样我们就创建了一个妙妙,完全没有一种所谓的MiaoMiao构造函数的生成。

我看过很多关于JS的设计模式的教程,很多教程都是把基于类的面对对象的单例模式拿到JS中来套用。我个人觉得这是完全没有必要的,JS是基于原型继承的,支持对象字面量写法,我们随手写的一个对象就是天然的单例。

其实这个问题在Java中很严重。我们知道软件工程毕竟是对现实世界的模拟,而在现实中,你、我、妙妙,都是独一无二的,不可复制的。所谓的类,就是对工厂函数的进一步抽象,提供了批量的创建对象的方式。
诚然,或许我们可以同一个Person类创建这个对象,或者再准确点用Chinese类来创建。但是无论多么细分,想要精准的描述,只能为单独的创建一个"MySelf"类,这样才能够足够准确的描述,而这个"MySelf"类也只能有这一个实例。这样也算是Java语言设计的一个副作用吧。

而在JS中,提供了对象字面量写法以及动态的修改对象的能力,不需要被类束缚。

上面就是我对单例模式的理解。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions