关于单例模式:单例设计模式的优缺点

在软件工程中,设计模式是一种在软件设计中经常出现问题的解决方案。它不是能够间接转化成代码的模板,而是在特定状况下能够利用的一套规定或概念。设计模式能够减速开发过程,通过提供通过测试和证实的开发范式来缩小前期可能呈现的问题和谬误。 在面向对象编程(OOP)畛域中,设计模式被广泛应用,以解决常见的软件设计问题。其中,单例(Singleton)模式是一种十分独特且宽泛应用的设计模式。它的外围目标是确保一个类只有一个实例,并提供一个全局拜访点来获取这个实例。 单例(Singleton)模式的定义单例模式确保了一个类只有一个实例存在,同时提供了一个全局拜访点来获取这个实例。在很多状况下,确保某个类在整个零碎中只有一个实例十分重要,比方配置管理器、线程池、缓存或者日志对象等。应用单例模式能够防止因为多个实例造成的资源应用抵触,或是数据不统一的问题。 实现单例模式实现单例模式通常波及以下几个关键步骤: 私有化构造函数:确保内部代码不能通过结构器创立类的实例。提供一个公有的动态变量:用于保留类的惟一实例。提供一个私有的静态方法:这个办法返回上述动态变量指向的实例。如果实例不存在,该办法会先创立它,而后返回。单例模式的 Java 实现以下是一个简略的单例模式的 Java 实现示例: public class Singleton { // 1. 创立一个公有的动态变量,用于保留单例实例,初始时为 null private static Singleton instance = null; // 2. 私有化构造函数,避免内部通过 new 创立实例 private Singleton() {} // 3. 提供一个私有的静态方法,返回单例实例 public static Singleton getInstance() { if (instance == null) { // 如果实例不存在,进入同步区块 synchronized (Singleton.class) { // 再次查看实例是否存在,防止多线程问题 if (instance == null) { instance = new Singleton(); } } } return instance; }}在这个例子中,Singleton 类通过私有化其构造函数避免内部通过 new 关键字创立实例。它应用一个名为 instance 的公有动态变量来持有类的惟一实例,并提供一个私有的静态方法 getInstance() 来让内部获取这个实例。这个办法首先查看 instance 是否曾经被创立,如果没有,则创立它。为了确保在多线程环境下这个类仍旧只有一个实例,应用了同步块。 ...

March 1, 2024 · 1 min · jiezi

关于单例模式:多线程环境单例模式

多线程环境单例模式 : 双重检测+上锁public class Singleton {/** * 结构器私有化避免被公共创立 */private Singleton() { }//创立多线程锁private static Object lock = new Object();//单例本体private static Singleton singleton = null;//获取单例public static Singleton getInstance() { //当多线程环境中单例本体为空 if (singleton == null) { //上锁避免被其余线程争夺 synchronized (lock) { //双重查看更加平安和合乎业务场景 if (singleton == null) { //加锁和双重检测后初始化单例 singleton = new Singleton(); } } } //返回单例后果 return singleton; }}

February 22, 2022 · 1 min · jiezi

关于单例模式:C11单例模式线程安全

单例模式留神:单例模式会让程序变得难以测试,所以尽可能不要应用单实例模式。保障一个类只有一个实例,并且提供了拜访该实例的全局拜访点。 线程平安的单例模式-C++11Singleton.h#pragma oncetemplate<typename T>class Singleton{public:    static T& instance() {        static T instance{ token{} };        return instance;   }    Singleton(const Singleton&) = delete;    Singleton& operator= (const Singleton) = delete;protected:    struct token {};    Singleton() {}};Example#include <Singleton.h>#include <iostream>class Test final : public Singleton<Test>{public:    Test(token) { std::cout << "constructed" << std::endl; }    ~Test() {  std::cout << "destructed" << std::endl; }    void use() const { std::cout << "in use" << std::endl; };};int main(){    // Test cannot_create; /* ERROR */    std::cout << "Entering main()" << std::endl;   {        auto const& t = Test::instance();        t.use();   }   {        auto const& t = Test::instance();        t.use();   }    std::cout << "Leaving main()" << std::endl;}隐患如果单例类位于 dll 库中,是否还能保障惟一? ...

December 11, 2021 · 1 min · jiezi

关于单例模式:DCL

DCL 单例模式DCL 就是 Double Check Lock 的缩写,即双重查看的同步锁。代码如下, public class Singleton { // volatile避免指令重排 private static volatile Singleton singleton = null; private Singleton(){ } public static Singleton getInstance(){ //进入办法内,先判断实例是否为空,以确定是否须要进入同步代码块 if(singleton == null){ synchronized (Singleton.class){ //进入同步代码块时再次判断实例是否为空 if(singleton == null){ singleton = new Singleton(); } } } return singleton; }}DCL 单例线程平安问题失常状况,能够保障调用 getInstance 办法两次,拿到的是同一个对象。然而,Java 中有个很弱小的性能——反射通过反射,就能够毁坏单例模式,从而调用它的构造函数,来创立不同的对象。 通过 反射 拿对象的hashcode public static void main(String[] args) throws Exception { Singleton singleton1 = Singleton.getInstance(); System.out.println(singleton1.hashCode()); Class<Singleton> clazz = Singleton.class; Constructor<Singleton> ctr = clazz.getDeclaredConstructor(); ctr.setAccessible(true); Singleton singleton2 = ctr.newInstance(); System.out.println(singleton2.hashCode()); }打印后果如下: ...

May 11, 2021 · 2 min · jiezi

关于单例模式:单例模式

什么是单例模式?保障一个类只有一个实例,并提供一个全局变量来拜访这个实例,这就是单例模式,单例模式属于对象创立型模式。 单例模式的几个因素类只能有一个实例这个实例必须由该类自身创立该类必须向整个零碎提供该实例的拜访权限单例模式的构造 单例模式的实现class Singleton{ private static Singleton s_Instance; //公有构造方法,避免内部实例化 private Singleton() { } public static Singleton GetInstance() { if (s_Instance == null) s_Instance = new Singleton(); return s_Instance; }}测试代码 static void Main(){ var s1 = Singleton.GetInstance(); var s2 = Singleton.GetInstance(); if (object.ReferenceEquals(s1, s2)) Console.WriteLine($"s1 s2两个实例是雷同的"); Console.ReadKey();}后果 饿汉式单例与懒汉式单例饿汉式单例(最简略的单例) class EagerSingleton{ //类加载时执行 private static EagerSingleton s_Instance = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton GetInstance() { return s_Instance; }}懒汉式单例//懒汉式单例与饿汉式单例不同的在于:懒汉式单例在第一次被援用时将本人实例化(类被加载时不会实例化本身)class LazySingleton{ private static LazySingleton s_Instance; //公有构造方法,避免内部实例化 private LazySingleton() { } public static LazySingleton GetInstance() { if (s_Instance == null) s_Instance = new LazySingleton(); return s_Instance; }}懒汉式单例的线程平安问题在高并发多线程的环境下运行懒汉式单例的代码,会呈现在某一时刻存在多个线程同时拜访GetInstance办法,可能会创立多个实例,违反了单例模式的设计用意。 ...

May 10, 2021 · 1 min · jiezi

关于单例模式:设计模式之单例模式

单例模式单例模式(Singleton Pattern)是 Java 中最简略的设计模式之一。这种类型的设计模式属于创立型模式,它提供了一种创建对象的最佳形式。 这种模式波及到一个繁多的类,该类负责创立本人的对象,同时确保只有单个对象被创立。这个类提供了一种拜访其惟一的对象的形式,能够间接拜访,不须要实例化该类的对象。‘’ 留神: 1、单例类只能有一个实例。2、单例类必须本人创立本人的惟一实例。3、单例类必须给所有其余对象提供这一实例。介绍用意:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。 次要解决:一个全局应用的类频繁地创立与销毁。 何时应用:当您想管制实例数目,节俭系统资源的时候。 如何解决:判断零碎是否曾经有这个单例,如果有则返回,如果没有则创立。 要害代码:构造函数是公有的。 利用实例: 1、一个班级只有一个班主任。2、Windows 是多过程多线程的,在操作一个文件的时候,就不可避免地呈现多个过程或线程同时操作一个文件的景象,所以所有文件的解决必须通过惟一的实例来进行。3、一些设施管理器经常设计为单例模式,比方一个电脑有两台打印机,在输入的时候就要解决不能两台打印机打印同一个文件。长处: 1、在内存里只有一个实例,缩小了内存的开销,尤其是频繁的创立和销毁实例(比方治理学院首页页面缓存)。2、防止对资源的多重占用(比方写文件操作)。毛病:没有接口,不能继承,与繁多职责准则抵触,一个类应该只关怀外部逻辑,而不关怀里面怎么样来实例化。 应用场景: 1、要求生产惟一序列号。2、WEB 中的计数器,不必每次刷新都在数据库里加一次,用单例先缓存起来。3、创立的一个对象须要耗费的资源过多,比方 I/O 与数据库的连贯等。注意事项:getInstance() 办法中须要应用同步锁 synchronized (Singleton.class) 避免多线程同时进入造成 instance 被屡次实例化。 实现咱们将创立一个 SingleObject 类。_SingleObject_ 类有它的公有构造函数和自身的一个动态实例。 SingleObject 类提供了一个静态方法,供外界获取它的动态实例。_SingletonPatternDemo_ 类应用 SingleObject 类来获取 SingleObject 对象。 步骤 1创立一个 Singleton 类。 SingleObject.javapublic class SingleObject { //创立 SingleObject 的一个对象 private static SingleObject instance = new SingleObject(); //让构造函数为 private,这样该类就不会被实例化 private SingleObject(){} //获取惟一可用的对象 public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("Hello World!"); }}步骤 2从 singleton 类获取惟一的对象。 ...

November 14, 2020 · 2 min · jiezi

关于单例模式:C单例模式实现

最不便罕用的是Meyers' Singleton,多线程平安。gcc 4.0之后的编译器反对这种写法,要求C++11及其当前的版本。 class Singleton{public: // 留神返回的是援用 static Singleton &getInstance() { static Singleton instance; // 动态局部变量 return instance; }private: Singleton() = default; Singleton(const Singleton &) = delete; // 禁用拷贝构造函数 Singleton &operator=(const Singleton &) = delete; // 禁用拷贝赋值运算符};残缺的验证程序: #include <iostream>using namespace std;class Singleton{public: // 留神返回的是援用 static Singleton &getInstance() { static Singleton instance; // 动态局部变量 return instance; }private: Singleton() = default; Singleton(const Singleton &) = delete; // 禁用拷贝构造函数 Singleton &operator=(const Singleton &) = delete; // 禁用拷贝赋值运算符};int main(){ Singleton& s1 = Singleton::getInstance(); // &是援用 cout << &s1 << endl; // &是取地址 Singleton& s2 = Singleton::getInstance(); cout << &s2 << endl; // Singleton s3(s1); // s2 = s1; system("pause"); return 0;}打印出s1和s2的地址是同一个,因为是同一个动态局部变量。 ...

October 10, 2020 · 1 min · jiezi

为什么用枚举类来实现单例模式越来越流行

前言单例模式是 Java 设计模式中最简单的一种,只需要一个类就能实现单例模式,但是,你可不能小看单例模式,虽然从设计上来说它比较简单,但是在实现当中你会遇到非常多的坑,所以,系好安全带,上车。 单例模式的定义单例模式就是在程序运行中只实例化一次,创建一个全局唯一对象,有点像 Java 的静态变量,但是单例模式要由于静态变量,静态变量在程序启动的时候就要创建,会造成大量的资源浪费,好的单例模式不会有这个问题。开发中的很多工具类都应用了单例模式,线程池、缓存、日志对象等,它们都只需要创建一个对象,如果创建多份实例,可能会带来不可预知的问题,比如资源的浪费、结果处理不一致等问题。 单例的实现思路静态化实例对象私有化构造方法,禁止通过构造方法创建实例提供一个公共的静态方法,用来返回唯一实例单例的好处只有一个对象,内存开支少、性能好(当一个对象的产生需要比较多的资源,如读取配置、产生其他依赖对象时,可以通过应用启动时直接产生一个单例对象,让其永驻内存的方式解决)避免对资源的多重占用(一个写文件操作,只有一个实例存在内存中,避免对同一个资源文件同时写操作) 在系统设置全局访问点,优化和共享资源访问(如:设计一个单例类,负责所有数据表的映射处理)单例模式的实现单例模式的主流写法有饿汉模式、懒汉模式、双重检查锁模式、静态内部类单例模式、枚举类实现单例模式五种方式,其中懒汉模式、双重检查锁模式两种模式写法不当,会导致在多线程下不是单例或者单例出异常,后面将会给大家详细介绍。我们从最基本的饿汉模式开始我们的单例编写之路。 饿汉模式饿汉模式采用的是一种简单粗暴的形式,在定义静态属性时,直接实例化了对象。代码如下: //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快public class SingletonObject1 { // 利用静态变量来存储唯一实例 private static final SingletonObject1 instance = new SingletonObject1(); // 私有化构造函数 private SingletonObject1(){ // 里面可能有很多操作 } // 提供公开获取实例接口 public static SingletonObject1 getInstance(){ return instance; }}饿汉模式的优缺点优点JVM层面的线程安全,static关键字保证了在引用这个变量时,关于这个变量的所以写入操作都完成缺点不能实现懒加载,造成空间浪费,如果一个类比较大,我们在初始化的时就加载了这个类,但是我们长时间没有使用这个类,这就导致了内存空间的浪费。懒汉模式懒汉模式就像一个懒汉的时候,只有饿了才会想办法找东西来填饱肚子,从来不会先的准备好食物,以防饿了。懒汉模式实现了懒加载,解决了饿汉模式带来的空间浪费问题,实现了使用时才去初始化类,但是也引入了其他的问题,我们先来看看下面这个懒汉模式 public class SingletonObject2 { // 定义静态变量时,未初始化实例 private static SingletonObject2 instance; // 私有化构造函数 private SingletonObject2(){ } public static SingletonObject2 getInstance(){ // 使用时,先判断实例是否为空,如果实例为空,则实例化对象 if (instance == null) instance = new SingletonObject2(); return instance; }}上面这段懒汉模式实现代码,在多线程的情况下,不能保证是单例模式,主要问题出现在实例化对象的时候,所以我单独把实例化的代码提出来,给大伙讲讲为什么在多线程的情况下有可能会初始化多份实例。 ...

August 28, 2019 · 2 min · jiezi

Java设计模式优化单例模式

单例模式概述单例模式是一种对象创建模式,用于产生一个类的具体事例。使用单例模式可以确保整个系统中单例类只产生一个实例。有下面两大好处: 对于频繁创建的对象,节省初第一次实例化之后的创建时间。由于new操作的减少,会降低系统内存的使用频率。减轻GC压力,从而缩短GC停顿时间创建方式: 单例作为类的私有private属性单例类拥有私有private构造函数提供获取实例的public方法单例模式的角色: 角色作用单例类提供单例的工厂,返回类的单例实例使用者获取并使用单例类类基本结构: 单例模式的实现1.饿汉式public class HungerSingleton { //1.饿汉式 //私有构造器 private HungerSingleton() { System.out.println("create HungerSingleton"); } //私有单例属性 private static HungerSingleton instance = new HungerSingleton(); //获取单例的方法 public static HungerSingleton getInstance() { return instance; }}注意: 单例修饰符为static JVM加载单例类加载时,直接初始化单例。无法延时加载。如果此单例一直未被使用,单Singleton 因为调用静态方法被初始化则会造成内存的浪费。getInstance()使用static修饰,不用实例化可以直接使用Singleton.getInstance()获取单例。由于单例由JVM加载类的时候创建,所以不存在线程安全问题。2.简单懒汉式public class Singleton { //2.1简单懒汉式(线程不安全) //私有构造器 private Singleton() { System.out.println("create Singleton"); } //私有单例属性[初始化为null] private static Singleton instance = null; //获取单例的方法 public static Singleton getInstance() { if(instance == null) { //此处instance实例化 //首次调用单例时会进入 达成延时加载 instance = new Singleton(); } return instance; }}由于未使用 synchronized 关键字,所以当线程1调用单例工厂方法Singleton.getInstance() 且 instance 未初始化完成时,线程2调用此方法会将instance判断为null,也会将instance重新实例化赋值,此时则产生了多个实例!如需线程安全可以直接给getInstance方法上加synchronized关键字,如下:public class Singleton { //2.2简单懒汉式(线程安全) //私有构造器 private Singleton() { System.out.println("create Singleton"); } //私有单例属性[初始化为null] private static Singleton instance = null; //获取单例的方法 将此方法使用synchronized关键字同步 public static synchronized Singleton getInstance() { if(instance == null) { //此处instance实例化 //首次调用单例时会进入 达成延时加载 instance = new Singleton(); } return instance; }}面临的问题: ...

August 7, 2019 · 2 min · jiezi

js常用设计模式实现一单例模式

什么是设计模式设计模式是一种能够被反复使用,符合面向对象特性的代码设计经验的总结,合理的使用设计模式能够让你得代码更容易维护和可靠设计模式的类型共分为创建型模式,结构型模式,行为型模式三种 创建型模式创建型模式是对一个类的实例化过程进行了抽象,把对象的创建和对象的使用进行了分离,创建模式有 单例模式抽象工厂模式建造者模式工厂模式原型模式单例模式单例模式的定义是保证一个类仅有一个实例,单例模式它必须自行创建这个实例,并提供一个访问他的全局的访问点es5的实现var only = function(data) { this.data = data; this.Instance = null;}only.go = function(data) { if(!this.Instance) { this.Instance = new only(data); } return this.Instance;}let obj1 = only.go('1')let obj2 = only.go('2')console.log(obj1 === obj2);console.log(obj1);console.log(obj2);es6class only { constructor(data) { if (only.prototype.Instance === undefined) { this.data = data; only.prototype.Instance = this; } return only.prototype.Instance; }}let ob1 = new only("a");let ob2 = new only("b");ob2.init = 'init';console.log(ob1 === ob2);console.log(ob1);console.log(ob2);上边的代码中,无论怎么new,其结果都是唯一的那个实例 单例模式的优缺点单例模式,因为他的实例是唯一的,所以完全可以通过创建的时候,严格的去控制怎么去创建和访问或者说抛出错误,如果存在频繁的创建和销毁的操作的时候,单例模式事可以提高性能的 但是同样的,单纯的单例模式中是没有抽象操作的,所以说单例模式是一个不便于扩展的模式 ...

July 11, 2019 · 1 min · jiezi

设计模式-从ES5-到-TypeScript-单例模式

Back in 1994, a book was authored by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides that discusses 23 desgin patterns, titled Design Patterns: Elements of Resuable Object-Oriented Software. You may have heard of this book or the authors as Gang of Four (GoF).单例模式单例模式(Singleton Pattern)是最简单的设计模式之一。这种类型的设计模式属于创建型 (Creational) 模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 注意: 单例类只能有一个实例。单例类必须自己创建自己的唯一实例。单例类必须给所有其他对象提供这一实例。单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。 关键点意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 主要解决:一个全局使用的类频繁地创建与销毁。 何时使用:当您想控制实例数目,节省系统资源的时候。 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。 关键代码:构造函数是私有的。 UML我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。 SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo,我们的演示类使用 SingleObject 类来获取 SingleObject 对象。 ...

June 24, 2019 · 2 min · jiezi

Java设计模式单例模式Singleton-Pattern

定义单例模式是一个比较"简单"的模式,其定义如下: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。或者 Ensure a class has only one instance, and provide a global point of access to it.确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 请注意"简单"二字的双引号,说它简单它也简单,但是要想用好、用对其实并不那么简单,为什么这么说? 首先,单例模式的定义比较好理解,应用场景明确,实现思路比较简单;其次,单例模式其实要考虑的因素很多,诸如延迟加载、线程安全以及破坏单例的情况等等。也正是这些因素导致单例模式的实现方式多样,且各有利弊特点单例类只能有一个实例;单例类必须自己创建自己的唯一实例;单例类必须给所有其他对象提供这一实例。基本步骤私有的静态成员变量:在本类中创建唯一实例,使用静态成员变量保存;为保证安全性,私有化这个成员变量私有的构造方法:避免其他类可以直接创建单例类的对象公有的静态方法:供其他类获取本类的唯一实例考虑的因素延迟加载线程安全破坏单例的情况 序列化 如果Singleton类是可序列化的,仅仅在生声明中加上implements Serializable是不够的。为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并且提供一个readResolve方法。否则,每次反序列化一个序列化的实例时,都会创建一个新的对象。反射 授权的客户端可以通过反射来调用私有构造方法,借助于AccessibleObject.setAccessible方法即可做到 。如果需要防范这种攻击,请修改构造函数,使其在被要求创建第二个实例时抛出异常。private Singleton() { System.err.println("Singleton Constructor is invoked!"); if (singleton != null) { System.err.println("实例已存在,无法初始化!"); throw new UnsupportedOperationException("实例已存在,无法初始化!"); } }}对象复制 在Java中,对象默认是不可以被复制的,若实现了Cloneable接口,并实现了clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制是不用调用类的构造函数,因此即使是私有的构造函数,对象仍然可以被复制。在一般情况下,类复制的情况不需要考虑,很少会出现一个单例类会主动要求被复制的情况,解决该问题的最好方法就是单例类不要实现Cloneable接口。类加载器如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。 实现方式1、懒汉式线程不安全(适用于单线程)public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; }}优点:延迟加载缺点:线程不安全,多线程环境下有可能产生多个实例为解决懒汉式"线程安全问题",可以将getInstance()设置为同步方法,于是就有了第二种实现方式: ...

June 13, 2019 · 2 min · jiezi

设计模式单例模式

单例模式是 确保一个类只有一个实例,自行实例化并向系统提供这个实例 一个类只有一个实例对象,避免了重复实例的频繁创建和销毁降低了资源消耗并且共用一个对象有利于数据同步,例如WINDOWS的任务管理器、回收站、网站的计数器、线程池对象、配置文件的读取对象等两种创建方式:1.饿汉单例模式(最常用): 单例实例在类装载时就构建,急切初始化。(预先加载法)特点:线程安全、在类加载的同时已经创建好一个静态对象,调用时反应速度快,有可能从没用到,有一点点的资源浪费 //饿汉单例模式Demopublic class SingletonTest { //1.私有化该类的构造方法(不让别人new,只能自己new) private SingletonTest() { } //2.自己内部new一个对象 public static SingletonTest instance = new SingletonTest(); //3.给一个get方法,让外界取它 public SingletonTest getInstance() { return instance; }}2.懒汉单例模式: 单例实例在第一次被使用时构建,延迟初始化。 //懒汉单例模式Demopublic class SingletonTest2 { //1.私有化该类的构造方法(不让别人new,只能自己new) private SingletonTest2() { } //2.自己内部维护一个null对象(只要被调用一次就不再是了) public static SingletonTest2 instance = null; //3.给一个get方法,让外界取它,只有有人用才会new一个对象出来 public SingletonTest2 getInstance() { if (instance == null) { //TODO 多线程下可能会出现重复new的情况 instance = new SingletonTest2(); } return instance; }}总结: 两种模式各有所长 一种是时间换空间 一种是空间换时间 根据具体场景使用 ...

May 12, 2019 · 1 min · jiezi

从单例模式到HappensBefore

目录 双重检测锁的演变过程利用HappensBefore分析并发问题无volatile的双重检测锁双重检测锁的演变过程synchronized修饰方法的单例模式双重检测锁的最初形态是通过在方法声明的部分加上synchronized进行同步,保证同一时间调用方法的线程只有一个,从而保证new Singlton()的线程安全: public class Singleton { private static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}这样做的好处是代码简单、并且JVM保证new Singlton()这行代码线程安全。但是付出的代价有点高昂: 所有的线程的每一次调用都是同步调用,性能开销很大,而且new Singlton()只会执行一次,不需要每一次都进行同步。 既然只需要在new Singlton()时进行同步,那么把synchronized的同步范围缩小呢? 线程不安全的双重检测锁public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}把synchronized同步的范围缩小以后,貌似是解决了每次调用都需要进行同步而导致的性能开销的问题。但是有引入了新的问题:线程不安全,返回的对象可能还没有初始化。 ...

April 24, 2019 · 2 min · jiezi

GO 单例模式

单例模式是常用的模式之一,一般介绍的单例模式有 饿汉式 和 懒汉式 等,不管那种模式最终目的只有一个,就是值实例化一次,只允许一个实例存在。GO语言实现单例模式相对简单,这里考虑到并发,用到了sync.Mutex 和结构体sync.Once。示例:package mainimport ( “fmt” “sync”)var ( lock *sync.Mutex = &sync.Mutex{} instance *Singleton)type Singleton struct {}func GetInstance() *Singleton { if instance == nil { lock.Lock() defer lock.Unlock() if instance == nil { instance = &Singleton{} fmt.Println(“instance…”) } } return instance}func main() { var s *Singleton s = GetInstance() s = GetInstance() fmt.Println(s)}执行结果:instance…&{}通过结果可以看到只输出了一个instance…。上面的实现方式还可以通过结构体sync.Once更优雅的实现。示例:package mainimport ( “fmt” “sync”)var ( once sync.Once instance *Singleton)type Singleton struct {}func GetInstance() *Singleton { once.Do(func() { instance = &Singleton{} fmt.Println(“instance…”) }) return instance}func main() { var s *Singleton s = GetInstance() s = GetInstance() fmt.Println(s)}输出结果:instance…&{}通过sync.Once的源代码查看它是如何运行的func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { return } // Slow-path. o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() }}sync.Once.Do(f func())使用加锁原子操作(代码包sync/atomic)来保证函数 f 只执行一次。目录 ...

March 19, 2019 · 1 min · jiezi

设计模式-单例模式详解

单例模式 保证一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点 需要隐藏其所有构造方法 优点: 在内存中只有一个实例,减少了内存开销 可以避免对资源的多重占用 设置全局访问点,严格控制访问 缺点: 没有接口,扩展困难 如果要扩展单例对象,只有修改代码,没有别的途径应用场景 ServletContext ServletConfig ApplicationContext DBPool常见的单例模式写法饿汉式单例 饿汉式就是在初始化的时候就初始化实例 两种代码写法如下:public class HungrySingleton { private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton(); private HungrySingleton() { } private static HungrySingleton getInstance() { return HUNGRY_SINGLETON; }}public class HungryStaticSingleton { private static final HungryStaticSingleton HUNGRY_SINGLETON; static { HUNGRY_SINGLETON = new HungryStaticSingleton(); } private HungryStaticSingleton() { } private static HungryStaticSingleton getInstance() { return HUNGRY_SINGLETON; }} 如果没有使用到这个对象,因为一开始就会初始化实例,这种方式会浪费内存空间懒汉式单例 懒汉式单例为了解决上述问题,则是在用户使用的时候才初始化单例public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton = null; private LazySimpleSingleton() { } public static LazySimpleSingleton getInstance() { //加上空判断保证初只会初始化一次 if (lazySimpleSingleton == null) { lazySimpleSingleton = new LazySimpleSingleton();//11行 } return lazySimpleSingleton; }} 上述方式,线程不安全,如果两个线程同时进入11行,那么会创建两个对象,需要如下,给方法加锁public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton = null; private LazySimpleSingleton() { } public synchronized static LazySimpleSingleton getInstance() { //加上空判断保证初只会初始化一次 if (lazySimpleSingleton == null) { lazySimpleSingleton = new LazySimpleSingleton(); } return lazySimpleSingleton; }} 上述方式虽然解决了线程安全问题,但是整个方法都是锁定的,性能比较差,所以我们使用方法内加锁的方式解决提高性能public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton = null; private LazySimpleSingleton() { } public static LazySimpleSingleton getInstance() { //加上空判断保证初只会初始化一次 if (lazySimpleSingleton == null) { synchronized (LazySimpleSingleton.class) {//11行 lazySimpleSingleton = new LazySimpleSingleton(); } } return lazySimpleSingleton; }} 上述方式如果两个线程同时进入了11行,一个线程a持有锁,一个线程b等待,当持有锁的a线程释放锁之后到return的时候,第二个线程b进入了11行内部,创建了一个新的对象,那么这时候创建了两个线程,对象也并不是单例的。所以我们需要在12行位置增加一个对象判空的操作。public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton = null; private LazySimpleSingleton() { } public static LazySimpleSingleton getInstance() { //加上空判断保证初只会初始化一次 if (lazySimpleSingleton == null) { synchronized (LazySimpleSingleton.class) { if (lazySimpleSingleton != null) { lazySimpleSingleton = new LazySimpleSingleton(); } } } return lazySimpleSingleton; }} 上述方式还是有风险的,因为CPU执行时候会转化成JVM指令执行: 1.分配内存给对象 2.初始化对象 3.将初始化好的对象和内存地址建立关联,赋值 4.用户初次访问 这种方式,在cpu中3步和4步有可能进行指令重排序。有可能用户获取的对象是空的。那么我们可以使用volatile关键字,作为内存屏障,保证对象的可见性来保证我们对象的单一。public class LazySimpleSingleton { private static volatile LazySimpleSingleton lazySimpleSingleton = null; private LazySimpleSingleton() { } public static LazySimpleSingleton getInstance() { //加上空判断保证初只会初始化一次 if (lazySimpleSingleton == null) { synchronized (LazySimpleSingleton.class) { if (lazySimpleSingleton != null) { lazySimpleSingleton = new LazySimpleSingleton(); } } } return lazySimpleSingleton; }}静态内部类单例 还有一种懒汉式单例,利用静态内部类在调用的时候等到外部方法调用时才执行,巧妙的利用了内部类的特性,jvm底层逻辑来完美的避免了线程安全问题public class LazyInnerClassSingleton { private LazyInnerClassSingleton() { } public static final LazyInnerClassSingleton getInstance() { return LazyHolder.LAZY; } private static class LazyHolder { private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); }} 这种方式虽然能够完美单例,但是我们如果使用反射的方式如下所示,则会破坏单例public class LazyInnerClassTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class<?> clazz = LazyInnerClassSingleton.class; Constructor c = clazz.getDeclaredConstructor(null); c.setAccessible(true); Object o1 = c.newInstance(); Object o2 = LazyInnerClassSingleton.getInstance(); System.out.println(o1 == o2); }} 怎么办呢,我们需要一种方式控制访问者的行为,通过异常的方式去限制使用者的行为,如下所示public class LazyInnerClassSingleton { private LazyInnerClassSingleton() { throw new RuntimeException(“不允许构建多个实例”); } public static final LazyInnerClassSingleton getInstance() { return LazyHolder.LAZY; } private static class LazyHolder { private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); }} 还有一种方式会破坏单例,那就是序列化破坏我们的单例,如下所示序列化破坏单例 我们写一个序列化的方法来尝试一下上述写法是否是满足序列化的。public class SeriableSingletonTest { public static void main(String[] args) { SeriableSingleton seriableSingleton = SeriableSingleton.getInstance(); SeriableSingleton s2; FileOutputStream fos = null; FileInputStream fis = null; try { fos = new FileOutputStream(“d.o”); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(seriableSingleton); oos.flush(); oos.close(); fis = new FileInputStream(“d.o”); ObjectInputStream ois = new ObjectInputStream(fis); s2 = (SeriableSingleton) ois.readObject(); ois.close(); System.out.println(seriableSingleton); System.out.println(s2); System.out.println(s2 == seriableSingleton); } catch (Exception e) { e.printStackTrace(); } finally { try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }} 为什么序列化会破坏单例呢,我们查看ObjectInputStream的源码首先,我们查看ObjectInputStream的readObject方法查看readObject0方法查看checkResolve(readOrdinaryObject(unshared)方法可以看到 红框内三目运算符内如果desc.isInstantiable()为真就创建新对象,不为空就返回空,此时我们查看desc.isInstantiable()方法此处cons是如果有构造方法就会返回true,当然我们一个类必然会有构造方法的,所以这就是为什么序列化会破坏我们的单例那么怎么办呢,我们只需要重写readResolve方法就行了public class SeriableSingleton implements Serializable { private SeriableSingleton() { throw new RuntimeException(“不允许构建多个实例”); } public static final SeriableSingleton getInstance() { return LazyHolder.LAZY; } private static class LazyHolder { private static final SeriableSingleton LAZY = new SeriableSingleton(); } private Object readResolve() { return getInstance(); }}为什么重写这个readResolve 的方法就能够避免序列化破坏单例呢回到上述readOrdinaryObject方法,可以看到有一个hasReadResolveMethod方法点进去可以看到 readResolveMethod在此处赋值也就是我们如果类当中有此方法则在hasReadResolveMethod当中返回的是true那么会进入readOrdinaryObject的如下部分并且如下所示,调用我们的readResolve方法获取对象,来保证我们对象是单例的 但是重写readResolve方法,只不过是覆盖了反序列化出来的对象,但是还是创建了两次,发生在JVM层面,相对来说比较安全,之前反序列化出来的对象会被GC回收注册式单例枚举单例 枚举式单例属于注册式单例,他把每一个实例都缓存到统一的容器中,使用唯一标识获取实例。也是比较推荐的一种写法,如下所示:public enum EnumSingleton { INSTANCE; private Object data; public static EnumSingleton getInstance() { return INSTANCE; }} 反编译上述文件,可以看到 那么序列化能不能破坏枚举呢 在ObjectInputStream的readObject方法中有针对枚举的判断上述通过一个类名和枚举名字值来确定一个枚举值。从而枚举在序列化上是不会破坏单例的。我们尝试使用反射来创建一个枚举对象public enum EnumSingleton { INSTANCE; private Object data; EnumSingleton() { } public static EnumSingleton getInstance() { return INSTANCE; } public static void main(String[] args) { Class clazz = EnumSingleton.class; try { Constructor c = clazz.getDeclaredConstructor(String.class, int.class); c.newInstance(“dd”, 1); } catch (Exception e) { e.printStackTrace(); } }}抛出异常查看Constructor源码可以看到可以看到jdk层面如果判断是枚举会抛出异常,所以枚举式单例是一种比较推荐的单例的写法。容器式单例这种方式是通过容器的方式来保证我们对象的单例,常见于Spring的IOC容器public class ContainerSingleton { private ContainerSingleton() { } private static Map<String, Object> ioc = new ConcurrentHashMap<>(); public static Object getBean(String className) { if (!ioc.containsKey(className)) { Object obj = null; try { obj = Class.forName(className).newInstance();//12 ioc.put(className, obj); } catch (Exception e) { e.printStackTrace(); } return obj; } return ioc.get(className); } public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(100); final CountDownLatch countDownLatch = new CountDownLatch(1000); for (int i = 0; i < 1000; i++) { executorService.submit(new Runnable() { @Override public void run() { Object o = ContainerSingleton.getBean(“com.zzjson.singleton.register.ContainerSingleton”); System.out.println(o + “”); countDownLatch.countDown(); } }); } countDownLatch.await(); executorService.shutdown(); }}这种方式测试可见出现了几次不同对象的情况因为我们线程在12行可能同时进入,这时候我们需要加一个同步锁如下,这样创建对象才是只会创建一个的public class ContainerSingleton { private ContainerSingleton() { } private static Map<String, Object> ioc = new ConcurrentHashMap<>(); public static Object getBean(String className) { synchronized (ioc) { if (!ioc.containsKey(className)) { Object obj = null; try { obj = Class.forName(className).newInstance(); ioc.put(className, obj); } catch (Exception e) { e.printStackTrace(); } return obj; } } return ioc.get(className); } public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(100); final CountDownLatch countDownLatch = new CountDownLatch(1000); for (int i = 0; i < 1000; i++) { executorService.submit(new Runnable() { @Override public void run() { Object o = ContainerSingleton.getBean(“com.zzjson.singleton.register.ContainerSingleton”); System.out.println(o + “”); countDownLatch.countDown(); } }); } countDownLatch.await(); executorService.shutdown(); }}ThreadLocal单例这种方式只能够保证在当前线程内的对象是单一的public class ThreadLocalSingleton { private ThreadLocalSingleton() { } private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>() { @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private static ThreadLocalSingleton getInstance() { return threadLocalInstance.get(); } }文中源码地址设计模式 ...

March 13, 2019 · 4 min · jiezi

Python版设计模式之单例模式

单例模式在某些场景下,我们希望实体类无论实例化多少次都只会产生一个实体对象,这时候就需要使用单例模式。经常使用的场景就是全局配置类。模式框架方式1:使用修饰器"““使用函数定义装饰器”““def singletons(cls): "”” 定义一个单例装饰器,使用dict保存定义好的实体,key为class的地址而不是名字,这样同名类也不会冲突 "”" instances = {} def wrapper(*args, **kwargs): if cls not in instances.keys(): instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper"““使用类定义装饰器”““class singletons(object): instances = {} def init(self, cls): self.__cls = cls def call(self, *args, **kwargs): if self.__cls not in singletons.instances.keys(): singletons.instances[self.__cls] = self.__cls(*args, **kwargs) return singletons.instances[self.__cls]方式2:重写__new__方法,只能针对当前修改的类有效class SingletonTest(object): __instance = None __isFirstInit = False def new(cls, name): if not cls.__instance: SingletonTest.__instance = super().new(cls) return cls.__instance def init(self, name): if not self.__isFirstInit: self.__name = name SingletonTest.__isFirstInit = True def getName(self): return self.__nameUML图示例@singletonsclass Test(object): def init(self, name): self.__name = name def hello(self): print(“I am {} object {}".format(self.__name, id(self)))if name == “main”: test1 = Test(“test1”) test2 = Test(“test2”) test1.hello() test2.hello()‘‘‘测试输出’’’# I am test1 object 2453169112512# I am test1 object 2453169112512扩展思考单例模式的线程安全问题。 ...

February 15, 2019 · 1 min · jiezi

Java设计模式之单例模式,这是最全最详细的了

一、单例模式作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问入口1、单例模式的常用1.Windows的任务管理器2.Windows的回收站,也是一个单例应用3.项目中的读取配置文件的对象4.数据库的连接池5.Servlet中的Application Servlet6.Spring中的Bean默认也是单例的7.SpringMVC Struts中的控制器2、单例模式的优点1.由于单例模式只生成一个实例,减少了系统给的性能开销,当一个对象需要产生时,当时消耗的资源较多。那么产生对象时构建的方式就可以通过单例去构建。2.单例模式存在全局访问点,所以可以优化共享资源访问。3、常见的单例模式的构建方法1.饿汉式:线程安全 调用率高 但是不能延迟加载2.懒汉式:线程安全 调用率不高 但是可以延迟加载3.双重检测(double check )4.静态内部类(线程安全 可以延迟加载)5.枚举单例 线程安全 不可以延迟加载二、代码案例展示1、饿汉式/** * 饿汉式: * 类只要被加载就会被加载全局变量,所以饿汉式,会被及时加载。(没有懒加载 ) * 并且存在天然的线程安全问题。 * @author 码歌老薛 * @date 创建时间 猴年马月 * @version 1.0 / public class SingleHungry { //提供静态的全局变量 作为访问该类实例的入口 private static SingleHungry sh = new SingleHungry(); /* * 构造器私有 无法创建对象 / private SingleHungry(){ } /* * 对外提供get方法获取 该类的实例 * @return / public static SingleHungry getInstance(){ return sh; } } 2、懒汉式/* * 懒汉式: * 全局变量初始化放到了实例化方法中,延迟产生对象。 * 但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。 * @author 码歌老薛 * @date 创建时间 猴年马月 * @version 1.0 / public class SingleLazy implements Serializable{ //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载 private static SingleLazy sh = null; /* * 构造器私有 无法创建对象 / private SingleLazy(){ System.out.println(“构造函数被调用了”); } /* * 对外提供get方法获取 该类的实例 * @return * @throws InterruptedException / public static synchronized SingleLazy getInstance() { if(sh==null){ sh = new SingleLazy(); } return sh; } } 上海尚学堂java培训 shsxt.com3、双重检测/* * 懒汉式: * 全局变量初始化放到了实例化方法中,延迟产生对象。 * 但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。 * @author 码歌老薛 * @date 创建时间 猴年马月 * @version 1.0 / public class SingleLazy4 { //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载 private volatile static SingleLazy4 sh = null; /* * 构造器私有 无法创建对象 / private SingleLazy4(){ System.out.println(“被调用了”); } /* * 双重校验锁式(也有人把双重校验锁式和懒汉式归为一类)分别在代码锁前后进行判空校验 * ,双重校验锁式是线程安全的。然而,在JDK1.5以前,DCL是不稳定的,有时也可能创建多个实例, * 在1.5以后开始提供volatile关键字修饰变量来达到稳定效果。 * 双重校验锁DCL(double checked locking) * @return * @throws InterruptedException / public static SingleLazy4 getInstance() { if(sh == null){ synchronized(SingleLazy4.class){ if(sh == null){ sh = new SingleLazy4(); //return singleton; //有人提议在此处进行一次返回 } //return singleton; //也有人提议在此处进行一次返回 } } return sh; } } 上海尚学堂Java培训 shsxt.com 获取更多java学习资料4、静态内部类/* 静态内部类 * * @author 码歌老薛 * @date 创建时间 猴年马月 * @version 1.0 / public class SingleInner { / *静态内部类式和饿汉式一样,同样利用了ClassLoader的机制保证了线程安全; 不同的是,饿汉式在Singleton类被加载时(从代码段3-2的Class.forName可见) 就创建了一个实例对象,而静态内部类即使Singleton类被加载也不会创建单例对象, 除非调用里面的getInstance()方法。因为当Singleton类被加载时 ,其静态内部类SingletonHolder没有被主动使用。只有当调用getInstance方法时, 才会装载SingletonHolder类,从而实例化单例对象。 这样,通过静态内部类的方法就实现了lazy loading,很好地将懒汉式和饿汉式结合起来, 既实现延迟加载,保证系统性能,也能保证线程安全 / private static class SingleInnerHolder{ private static SingleInner instance = new SingleInner(); } private SingleInner(){ System.out.println(“我被调用了”); } public static SingleInner getInstance(){ return SingleInnerHolder.instance; } } 5、枚举单例/ * jvm提供底层保证 * 不可能出现序列化、反射产生对象的漏洞 但是不能做到延迟加载 在外部,可以通过EnumSingleton.INSTANCE.work()来调用work方法。默认的枚举实例的创建是线程安全的 、,但是实例内的各种方法则需要程序员来保证线程安全。 总的来说,使用枚举单例模式,有三个好处: 1.实例的创建线程安全,确保单例。2.防止被反射创建多个实例。3.没有序列化的问题。 * @author 码歌老薛 * @date 创建时间 猴年马月 * @version 1.0 / public enum SingleEnum { //实例化对象 INSTANCE; / * 对象需要执行的功能 / void getInstance(){ } } 上海尚学堂java培训 shsxt.com6、反射/序列化,获取对象,以及防止方式import java.io.ObjectStreamException; import java.io.Serializable; / * 懒汉式: * 全局变量初始化放到了实例化方法中,延迟产生对象。 * 但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。 * @author 码歌老薛 * @date 创建时间 猴年马月 * @version 1.0 / public class SingleLazy implements Serializable{ //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载 private static SingleLazy sh = null; / * 构造器私有 无法创建对象 / private SingleLazy(){ if(sh!=null){ throw new RuntimeException(); } System.out.println(“构造函数被调用了”); } / * 对外提供get方法获取 该类的实例 * @return * @throws InterruptedException */ public static synchronized SingleLazy getInstance() { if(sh==null){ sh = new SingleLazy(); } return sh; } private Object readResolve()throws ObjectStreamException{ return sh; } } 上海尚学堂java培训 shsxt.com三、用法总结1、懒汉式效率是最低的。2、占用资源少 不需要延时加载 枚举优于 饿汉式3、占用资源比较多 需要延时加载 静态内部类 优于 懒汉式更多Java技术文章欢迎阅读上海尚学堂Java培训,免费试学和线上公开课培训课程等你学习。 ...

January 23, 2019 · 2 min · jiezi

PHP设计模式范例 — DesignPatternsPHP(1)创建型设计模式

【搬运于GitHub开源项目DesignPatternsPHP】项目地址:戳我1、创建型设计模式在软件工程中,创建型设计模式承担着对象创建的职责,尝试创建适合程序上下文的对象,对象创建设计模式的产生是由于软件工程设计的问题,具体说是向设计中增加复杂度,创建型设计模式解决了程序设计中对象创建的问题。1.1 抽象工厂1.1.1 目的创建一系列相关或依赖的对象,而不指定它们的具体类。通常创建的类都实现相同的接口。抽象工厂的客户端并不关心这些对象是如何创建的,它只知道它们是如何组合在一起的。1.1.2 UML图1.1.3 代码你可以在 GitHub 上查看代码Parser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;interface Parser{ public function parse(string $input): array;}CsvParser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;class CsvParser implements Parser{ const OPTION_CONTAINS_HEADER = true; const OPTION_CONTAINS_NO_HEADER = false; /** * @var bool / private $skipHeaderLine; public function __construct(bool $skipHeaderLine) { $this->skipHeaderLine = $skipHeaderLine; } public function parse(string $input): array { $headerWasParsed = false; $parsedLines = []; foreach (explode(PHP_EOL, $input) as $line) { if (!$headerWasParsed && $this->skipHeaderLine === self::OPTION_CONTAINS_HEADER) { $headerWasParsed = true; continue; } $parsedLines[] = str_getcsv($line); } return $parsedLines; }}JsonParser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;class JsonParser implements Parser{ public function parse(string $input): array { return json_decode($input, true); }}ParserFactory.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;class ParserFactory{ public function createCsvParser(bool $skipHeaderLine): CsvParser { return new CsvParser($skipHeaderLine); } public function createJsonParser(): JsonParser { return new JsonParser(); }}1.2 生成器模式1.2.1 目的生成器的目的是将复杂对象的创建过程(流程)进行抽象,生成器表现为接口的形式。在特定的情况下,比如如果生成器对将要创建的对象有足够多的了解,那么代表生成器的接口 interface 可以是一个抽象类(也就是说可以有一定的具体实现,就像众所周知的适配器模式)。如果对象有复杂的继承树,理论上创建对象的生成器也同样具有复杂的继承树。提示:生成器通常具有流畅的接口,推荐阅读关于 PHPUnit 的 mock 生成器获取更好的理解。1.2.2 例子PHPUnit: Mock 生成器1.2.3 UML图1.2.4 代码你可以在 GitHub 上找到这些代码Director.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;/* * Director is part of the builder pattern. It knows the interface of the builder * and builds a complex object with the help of the builder * * You can also inject many builders instead of one to build more complex objects /class Director{ public function build(BuilderInterface $builder): Vehicle { $builder->createVehicle(); $builder->addDoors(); $builder->addEngine(); $builder->addWheel(); return $builder->getVehicle(); }}BuilderInterface.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;interface BuilderInterface{ public function createVehicle(); public function addWheel(); public function addEngine(); public function addDoors(); public function getVehicle(): Vehicle;}TruckBuilder.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;class TruckBuilder implements BuilderInterface{ /* * @var Parts\Truck / private $truck; public function addDoors() { $this->truck->setPart(‘rightDoor’, new Parts\Door()); $this->truck->setPart(’leftDoor’, new Parts\Door()); } public function addEngine() { $this->truck->setPart(’truckEngine’, new Parts\Engine()); } public function addWheel() { $this->truck->setPart(‘wheel1’, new Parts\Wheel()); $this->truck->setPart(‘wheel2’, new Parts\Wheel()); $this->truck->setPart(‘wheel3’, new Parts\Wheel()); $this->truck->setPart(‘wheel4’, new Parts\Wheel()); $this->truck->setPart(‘wheel5’, new Parts\Wheel()); $this->truck->setPart(‘wheel6’, new Parts\Wheel()); } public function createVehicle() { $this->truck = new Parts\Truck(); } public function getVehicle(): Vehicle { return $this->truck; }}CarBuilder.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;class CarBuilder implements BuilderInterface{ /* * @var Parts\Car / private $car; public function addDoors() { $this->car->setPart(‘rightDoor’, new Parts\Door()); $this->car->setPart(’leftDoor’, new Parts\Door()); $this->car->setPart(’trunkLid’, new Parts\Door()); } public function addEngine() { $this->car->setPart(’engine’, new Parts\Engine()); } public function addWheel() { $this->car->setPart(‘wheelLF’, new Parts\Wheel()); $this->car->setPart(‘wheelRF’, new Parts\Wheel()); $this->car->setPart(‘wheelLR’, new Parts\Wheel()); $this->car->setPart(‘wheelRR’, new Parts\Wheel()); } public function createVehicle() { $this->car = new Parts\Car(); } public function getVehicle(): Vehicle { return $this->car; }}Parts/Vehicle.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;abstract class Vehicle{ /* * @var object[] / private $data = []; /* * @param string $key * @param object $value / public function setPart($key, $value) { $this->data[$key] = $value; }}Parts/Truck.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Truck extends Vehicle{}Parts/Car.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Engine{}Parts/Engine.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Engine{}Parts/Wheel.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Wheel{}Parts/Door.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Door{}1.3 工厂方法1.3.1 目的SimpleFactory的优点是您可以子类化它来实现创建对象的不同方法。对于简单的情况,这个抽象类可能只是一个接口。这个模式是一个 “真正” 的设计模式,因为它遵循了依赖反转原则 Dependency Inversion Principle 众所周知这个 “D” 代表了真正的面向对象程序设计。它意味着工厂方法类依赖于类的抽象,而不是具体将被创建的类,这是工厂方法模式与简单工厂模式和静态工厂模式最重要的区别。1.3.2 UML图1.3.3 代码你可以在 GitHub 上找到这些代码Logger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;interface Logger{ public function log(string $message);}StdoutLogger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class StdoutLogger implements Logger{ public function log(string $message) { echo $message; }}FileLogger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class FileLogger implements Logger{ /* * @var string / private $filePath; public function __construct(string $filePath) { $this->filePath = $filePath; } public function log(string $message) { file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND); }}LoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;interface LoggerFactory{ public function createLogger(): Logger;}StdoutLoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class StdoutLoggerFactory implements LoggerFactory{ public function createLogger(): Logger { return new StdoutLogger(); }}FileLoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class FileLoggerFactory implements LoggerFactory{ /* * @var string / private $filePath; public function __construct(string $filePath) { $this->filePath = $filePath; } public function createLogger(): Logger { return new FileLogger($this->filePath); }}1.4 多例多例模式已经被考虑列入到反模式中!请使用依赖注入获得更好的代码可测试性和可控性!1.4.1 目的使类仅有一个命名的对象的集合可供使用,像单例模式但是有多个实例。1.4.2 例子2 个数据库连接,比如,一个连接MySQL,另一个连接SQLite多个日志记录器(一个记录调试信息,另一个记录错误信息)1.4.3 UML 图1.4.4 代码你可以在 GitHub 上找到这些代码Multiton.php<?phpnamespace DesignPatterns\Creational\Multiton;final class Multiton{ const INSTANCE_1 = ‘1’; const INSTANCE_2 = ‘2’; /* * @var Multiton[] / private static $instances = []; /* * this is private to prevent from creating arbitrary instances / private function __construct() { } public static function getInstance(string $instanceName): Multiton { if (!isset(self::$instances[$instanceName])) { self::$instances[$instanceName] = new self(); } return self::$instances[$instanceName]; } /* * prevent instance from being cloned / private function __clone() { } /* * prevent instance from being unserialized / private function __wakeup() { }}1.5 对象池1.5.1 目的对象池设计模式 是创建型设计模式,它会对新创建的对象应用一系列的初始化操作,让对象保持立即可使用的状态 - 一个存放对象的 “池子” - 而不是对对象进行一次性的的使用(创建并使用,完成之后立即销毁)。对象池的使用者会对对象池发起请求,以期望获取一个对象,并使用获取到的对象进行一系列操作,当使用者对对象的使用完成之后,使用者会将由对象池的对象创建工厂创建的对象返回给对象池,而不是用完之后销毁获取到的对象。对象池在某些情况下会带来重要的性能提升,比如耗费资源的对象初始化操作,实例化类的代价很高,但每次实例化的数量较少的情况下。对象池中将被创建的对象会在真正被使用时被提前创建,避免在使用时让使用者浪费对象创建所需的大量时间(比如在对象某些操作需要访问网络资源的情况下)从池子中取得对象的时间是可预测的,但新建一个实例所需的时间是不确定。总之,对象池会为你节省宝贵的程序执行时间,比如像数据库连接,socket连接,大量耗费资源的代表数字资源的对象,像字体或者位图。不过,在特定情况下,简单的对象创建池(没有请求外部的资源,仅仅将自身保存在内存中)或许并不会提升效率和性能,这时候,就需要使用者酌情考虑了。1.5.2 UML图1.5.3 代码你可以在 GitHub 上找到这些代码WorkerPool.php<?phpnamespace DesignPatterns\Creational\Pool;class WorkerPool implements \Countable{ /* * @var StringReverseWorker[] / private $occupiedWorkers = []; /* * @var StringReverseWorker[] / private $freeWorkers = []; public function get(): StringReverseWorker { if (count($this->freeWorkers) == 0) { $worker = new StringReverseWorker(); } else { $worker = array_pop($this->freeWorkers); } $this->occupiedWorkers[spl_object_hash($worker)] = $worker; return $worker; } public function dispose(StringReverseWorker $worker) { $key = spl_object_hash($worker); if (isset($this->occupiedWorkers[$key])) { unset($this->occupiedWorkers[$key]); $this->freeWorkers[$key] = $worker; } } public function count(): int { return count($this->occupiedWorkers) + count($this->freeWorkers); }}StringReverseWorker.php<?phpnamespace DesignPatterns\Creational\Pool;class StringReverseWorker{ /* * @var \DateTime / private $createdAt; public function __construct() { $this->createdAt = new \DateTime(); } public function run(string $text) { return strrev($text); }}1.6 原型模式1.6.1 目的通过创建一个原型对象,然后复制原型对象来避免通过标准的方式创建大量的对象产生的开销(new Foo())。1.6.2 例子大量的数据对象(比如通过ORM获取1,000,000行数据库记录然后创建每一条记录对应的对象实体)1.6.3 UML图1.6.4 代码你可以在 GitHub 上找到这些代码BookPrototype.php<?phpnamespace DesignPatterns\Creational\Prototype;abstract class BookPrototype{ /* * @var string / protected $title; /* * @var string / protected $category; abstract public function __clone(); public function getTitle(): string { return $this->title; } public function setTitle($title) { $this->title = $title; }}BarBookPrototype.php<?phpnamespace DesignPatterns\Creational\Prototype;class BarBookPrototype extends BookPrototype{ /* * @var string / protected $category = ‘Bar’; public function __clone() { }}FooBookPrototype.php<?phpnamespace DesignPatterns\Creational\Prototype;class FooBookPrototype extends BookPrototype{ /* * @var string / protected $category = ‘Foo’; public function __clone() { }}1.7 简单工厂1.7.1 目的它与静态工厂不同,因为它不是静态的。因此,可以有多个参数化的工厂,可以子类化它,也可以模拟它。它总是比静态工厂更受欢迎!1.7.2 UML图1.7.3 代码你可以在 GitHub 上找到这些代码SimpleFactory.php<?phpnamespace DesignPatterns\Creational\SimpleFactory;class SimpleFactory{ public function createBicycle(): Bicycle { return new Bicycle(); }}Bicycle.php<?phpnamespace DesignPatterns\Creational\SimpleFactory;class Bicycle{ public function driveTo(string $destination) { }}1.7.4 使用 $factory = new SimpleFactory(); $bicycle = $factory->createBicycle(); $bicycle->driveTo(‘Paris’);1.8 单例模式1.8.1 目标使应用中只存在一个对象的实例,并且使这个单实例负责所有对该对象的调用。1.8.2 例子数据库连接器日志记录器 (可能有多个实例,比如有多个日志文件因为不同的目的记录不同到的日志)应用锁文件 (理论上整个应用只有一个锁文件)1.8.3 UML图1.8.4 代码你可以在 GitHub 上找到这些代码Singleton.php<?phpnamespace DesignPatterns\Creational\Singleton;final class Singleton{ /* * @var Singleton / private static $instance; /* * gets the instance via lazy initialization (created on first usage) / public static function getInstance(): Singleton { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } /* * is not allowed to call from outside to prevent from creating multiple instances, * to use the singleton, you have to obtain the instance from Singleton::getInstance() instead / private function __construct() { } /* * prevent the instance from being cloned (which would create a second instance of it) / private function __clone() { } /* * prevent from being unserialized (which would create a second instance of it) / private function _wakeup() { }}1.9 静态工厂1.9.1 目的和抽象工厂类似,静态工厂模式用来创建一系列互相关联或依赖的对象,和抽象工厂模式不同的是静态工厂模式只用一个静态方法就解决了所有类型的对象创建,通常被命名为 Factory 或者 Generators1.9.2 例子Zend Framework: zend_cache 后端或 _Frontend 使用工厂方法创建缓存后端和前端1.9.3 UML图1.9.4 代码你可以在 GitHub 上找到这些代码StaticFactory.php<?phpnamespace DesignPatterns\Creational\StaticFactory;/* * Note1: Remember, static means global state which is evil because it can’t be mocked for tests * Note2: Cannot be subclassed or mock-upped or have multiple different instances. /final class StaticFactory{ /* * @param string $type * * @return Formatter */ public static function factory(string $type): Formatter { if ($type == ’number’) { return new FormatNumber(); } elseif ($type == ‘string’) { return new FormatString(); } throw new \InvalidArgumentException(‘Unknown format given’); }}Formatter.php<?phpnamespace DesignPatterns\Creational\StaticFactory;interface Formatter{ public function format(string $input): string;}FormatString.php<?phpnamespace DesignPatterns\Creational\StaticFactory;class FormatString implements Formatter{ public function format(string $input): string { return $input; }}FormatNumber.php<?phpnamespace DesignPatterns\Creational\StaticFactory;class FormatNumber implements Formatter{ public function format(string $input): string { return number_format($input); }} ...

January 18, 2019 · 6 min · jiezi

创建型模式:单例模式

个人博客原文:创建型模式:单例模式简介姓名:单例模式英文名:Singleton Pattern价值观:我的生活我主宰(只允许自己实例化,不愿意被其他对象实例化)个人介绍:Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)(来自《设计模式之禅》)这里的关注点有 3 个,分别是:只有一个实例自行实例化(也就是主动实例化)向整个系统提供这个实例你要的故事我们脑洞大开来用一个故事讲解一番。小明家里有一辆小汽车,具体什么牌子就不知道了,咱也不关注,反正他家里就这么一辆车,小明比较懒,只要一出门都会开车,例如去旅游、去学校、去聚会都会开车去。下面模拟小明出去的场景。class Car { public void run() { System.out.println(“走。。。。”); }}class XiaoMing { public Car travel() { System.out.println(“小明去旅游”); Car car = new Car(); car.run(); return car; } public Car goToSchool() { System.out.println(“小明去学校”); Car car = new Car(); car.run(); return car; } public Car getTogether() { System.out.println(“小明参加聚会”); Car car = new Car(); car.run(); return car; }}public class SingletonErrorTest { public static void main(String[] args) { XiaoMing xiaoMing = new XiaoMing(); Car car1 = xiaoMing.travel(); Car car2 = xiaoMing.goToSchool(); Car car3 = xiaoMing.getTogether(); }}上面小汽车只有一个方法,就是走。小明去旅游、去学校、参加聚会都开着他唯一的一辆汽车车去。是不是有人有疑问?为什么每个方法都返回 Car 对象?其实只是想在下面做一次检查,检查小明去旅游、去学校和参加聚会的车是不是同一辆。下面是检查代码:System.out.println(“car1 == car2 ? " + (car1 == car2));System.out.println(“car2 == car3 ? " + (car2 == car3));最终结果是啥?很明显是 2 个 false。小明去旅游、去学校和参加聚会的车都不相同,小明不是只有 1 辆车?关键在于 Car car = new Car(); 这一句代码,其实这一句是创建一辆车,每次都重新创建一辆。那应该怎么实现小明只有一辆车呢?这时候就引入了单例模式。上面我们说到了单例模式需要具备的 3 个点:只有 1 个实例,很显然,上面的代码不止 1 个实例,而是有 3 个 Car 实例;自行实例化,Car 本身没有主动实例化,而是在小明需要用到的时候才实例化;向整个系统提供这个实例,因为 Car 没有主动实例化,所以它没法向外部暴露提供自己出来。我们的代码完全不符合单例模式的要求。我们要通过修改,使之符合单例模式的 3 个要点。首先需要实现的是第 2 点,把 Car 实例化从小明转为 Car 本身,如下代码class Car1{ private static Car1 car1 = new Car1(); private Car1() { } public void run(){ System.out.println(“走。。。。”); }}上面代码使用 private 修饰构造方法,使得 Car1 不能被其他使用方实例化,通过 Car1 car1 = new Car1(); 主动实例化自己。接下来再实现第 3 点,向整个系统暴露这个实例,也就是暴露它自己。每个使用方都调用 Car1.getInstance() 方法来获取实例。class Car1{ private static Car1 car1 = new Car1(); public static Car1 getInstance() { return car1; } private Car1() { } public void run(){ System.out.println(“走。。。。”); }}上面代码就实现了单例模式的 2 和 3 要点,第 1 要点要怎么实现呢?告诉你,不用实现,只要满足了 2 和 3 要点就可以,第 1 要点是用来检验是否是单例模式的好思路。我们检验一下class Car1{ private static Car1 car1 = new Car1(); public static Car1 getInstance() { return car1; } private Car1() { } public void run(){ System.out.println(“走。。。。”); }}class XiaoMing1 { public Car1 travel() { System.out.println(“小明去旅游”); Car1 car = Car1.getInstance(); car.run(); return car; } public Car1 goToSchool() { System.out.println(“小明去学校”); Car1 car = Car1.getInstance(); car.run(); return car; } public Car1 getTogether() { System.out.println(“小明参加聚会”); Car1 car = Car1.getInstance(); car.run(); return car; }}public class SingletonRightHungryTest { public static void main(String[] args) { XiaoMing1 xiaoMing1 = new XiaoMing1(); Car1 car1 = xiaoMing1.travel(); Car1 car2 = xiaoMing1.goToSchool(); Car1 car3 = xiaoMing1.getTogether(); System.out.println(“car1 == car2 ? " + (car1 == car2)); System.out.println(“car2 == car3 ? " + (car2 == car3)); }}上面代码最后两行打印出来的结果是啥?是我们想要的:2 个 true。说明小明这几次外出开的车都是同一辆。这是最简单的单例模式的实现方式,我们经常称作饿汉式单例模式。为什么起这么古怪的名字呢?其实和对应的懒汉式单例模式有关,这是 2 个实现方式的差别,饿汉式单例模式实现方式在类加载到内存的时候,就创建好对象了,而懒汉式则是在第一次使用的时候才创建对象,也就是把创建对象的时机从加载延迟到第一次使用,所以才有懒饿之分。下面我们来看怎么实现懒汉式单例模式。先描述一下场景:小明还没有汽车,他也不知道什么时候要买汽车,突然某一天,他想去旅游,觉得是时候买辆车了,然后他就买车去旅游了,旅游回来又开车去学校和参加聚会。class Car2{ private static Car2 car2; public static synchronized Car2 getInstance() { if (null == car2) { System.out.println(“买车啦。。。”); car2 = new Car2(); } return car2; } private Car2() { } public void run(){ System.out.println(“走。。。。”); }}class XiaoMing2{ public Car2 travel() { System.out.println(“小明去旅游”); Car2 car = Car2.getInstance(); car.run(); return car; } public Car2 goToSchool() { System.out.println(“小明去学校”); Car2 car = Car2.getInstance(); car.run(); return car; } public Car2 getTogether() { System.out.println(“小明参加聚会”); Car2 car = Car2.getInstance(); car.run(); return car; }}public class SingletonRightLazyTest { public static void main(String[] args) { XiaoMing2 xiaoMing2 = new XiaoMing2(); Car2 car1 = xiaoMing2.travel(); Car2 car2 = xiaoMing2.goToSchool(); Car2 car3 = xiaoMing2.getTogether(); System.out.println(“car1 == car2 ? " + (car1 == car2)); System.out.println(“car2 == car3 ? " + (car2 == car3)); }}小明去旅游买车啦。。。走。。。。小明去学校走。。。。小明参加聚会走。。。。car1 == car2 ? truecar2 == car3 ? true上面附带了打印出来的结果,小明要去旅游的时候,才去买车。这就是懒汉式单例模式的实现方式。要注意懒汉式单例模式有个很关键的一点就是 getInstance() 方法带上了 synchronized,这个是为什么呢?首先得了解关键字 synchronized 的作用是什么:用于修饰执行方法同步,也就是说多线程并发的情况下,在一个时间点,只允许一个线程执行这个方法。不加上这个会有什么结果?在多线程并发情况下,如果有 2 个线程同时执行到 if(null == car2),那么都判断为 true,这时 2 个线程都会执行 car2 = new Car2(),这样子就不是单例了。总结单例模式可以说是设计模式中最简单的一个,也是在工作中很多场景下经常用到的,比如:项目的配置文件加载、各种工具类等等。我们对于单例模式最重要的一点就是要考虑多线程并发,没有考虑这点就容易引发单例对象不单例的情况。而单例给我们带来最大的好处就是节约内存。上面实现的两种方法是单例模式中最最最简单的 2 种实现,相信也是用得最多的实现方式。网上有不少网友分享了单例模式的很多种实现方法,大家也可以去了解,在了解之前务必已经搞懂文中这 2 种最简单的实现方式,不然会头晕的。参考资料:《大话设计模式》、《Java设计模式》、《设计模式之禅》、《研磨设计模式》、《Head First 设计模式》希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号,第一时间获取文章推送阅读,也可以一起交流,交个朋友。公众号之设计模式系列文章 ...

January 15, 2019 · 3 min · jiezi

JS设计模式之Singleton(单例)模式

定义限制类的实例化次数只能是一次。如果该实例不存在的情况下,可以通过一个方法创建一个类来实现创建类的新实例,如果实例已经存在,它会简单返回该对象的引用。适用场景需要频繁实例化然后销毁的对象。频繁访问数据库或文件的对象。创建对象时耗时过多或者耗资源过多,但又经常用到的对象。Talk is cheap通过打印privateRandomNumber来验证是否为同一个实例let mySingleton = (()=> { let instance; let init = ()=> { let privateMethod = ()=> {//私有方法 console.log(‘I am privateMethod’); } let privateVariable = ‘I am also private’; let privateRandomNumber = Math.random(); return {//共有方法和变量 publicMethod: ()=> { console.log(‘I am public’); }, publicProperty: ‘I am also public’, getRandomNumber: ()=> { return privateRandomNumber; } } } return {//获取Singleton的实例,如果存在就返回,不存在就创建新实例 getInstance: ()=> { if(!instance) { instance = init(); } return instance; } }})();let singleA = mySingleton.getInstance();let singleB = mySingleton.getInstance();console.log(singleA.getRandomNumber() === singleB.getRandomNumber());//ture简单封装node连接mongodb数据库const MongoDB = require(‘mongodb’)const MongoClient = require(‘mongodb’).MongoClientconst ObjectID = MongoDB.ObjectIDconst Config = require(’./config’)class Db { static getInstance() { if(!Db.instance) { Db.instance = new Db() } return Db.instance } constructor() { this.dbClient = ’’ this.connect() } connect() {//连接数据库 let that = this return new Promise((resolve, reject)=> { if(!that.dbClient) { MongoClient.connect(Config.dbUrl, {useNewUrlParser:true}, (err, client)=> { if(err) { reject(err) }else { that.dbClient = client.db(Config.dbName) resolve(that.dbClient) } }) }else { resolve(that.dbClient) } }) } find(collectionName, json) { return new Promise((resolve, reject)=> { this.connect().then((db)=> { let result = db.collection(collectionName).find(json) result.toArray((err, docs)=> { if(err) { reject(err) return } resolve(docs) }) }) }) } update(collectionName, json1, json2) { return new Promise((resolve, reject)=> { this.connect().then((db)=> { db.collection(collectionName).updateOne(json1, { $set: json2 },(err, result)=> { if(err) { reject(err) }else { resolve(result) } }) }) }) } insert(collectionName, json) { return new Promise((resolve, reject)=> { this.connect().then((db)=> { db.collection(collectionName).insertOne(json, (err, result)=> { if(err) { reject(err) }else { resolve(result) } }) }) }) } remove(collectionName, json) { return new Promise((resolve, reject)=> { this.connect().then((db)=> { db.collection(collectionName).removeOne(json, (err, result)=> { if(err) { reject(err) }else { resolve(result) } }) }) }) } getObjectId(id) { return new ObjectID(id) }}module.exports = Db.getInstance()参考《Javascript设计模式》 ...

January 5, 2019 · 2 min · jiezi

python之单例模式

什么是单例模式?通俗点讲:单例模式就是在程序执行的过程中,类只有一个实例,这不是说单例模式只能去创建一个实例,而是你创建的所有实例(也就是对象)都指的是同一个实例。如何做到这一点呢?我们的__new__特殊方法就派上用场了,可能大家对这个方法熟悉又陌生,那么接下来通过一个小程序简单的过渡一下class A(): def new(cls): return “你好” def init(self): print(self)print(A())猜想一下,这里控制台会输出什么?输出结果:你好这里再创建A对象的时候就会先执行__new__方法,然后将返回值传递给__init__方法的self,所以结果会是:你好,这这段程序中我们知道对象的创建是依靠__new__方法的,那么我们就可以通过这个方法入手,每次创建对象的时候在__new__方法内部进行判断,不拐弯抹角了,直接附上为小伙伴们准备的代码。class A(): _instance = None def new(cls): if cls._instance is None: cls._instance = super().new(cls) return cls._instance def init(self): print(self) A() #输出结果:<main.A object at 0x0000020CED433FD0>A() #输出结果:<main.A object at 0x0000020CED433FD0>可以清晰对比到两个对象地址是一样的,可见我们的目的就达到了,上面是通过一个私有变量_instance控制实现的,当然小伙伴可以其他方式实现,如:开关(定义一个bool变量进行控制)等。谢谢大家的支持!

January 4, 2019 · 1 min · jiezi