工厂、抽象工厂、代理模式、建造者、单例模式(图解)

GOF23
一种思维,一种态度,一种进步

  • 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  • 行为模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

工厂模式
作用:
实现了创建者和调用者的分离

核心本质:

  1. 实例化对象不使用new,用工厂方法代替
  2. 将选择实现类、创建对象统一管理和控制。从而将调用方跟实现类解耦

三种形式的工厂模式

  1. 简单工厂模式
    用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
  2. 工厂方法模式
    用来生产同一等级结构中的固定产品(支持增加任意产品)
  3. 抽象工厂模式
    围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂

不使用工厂模式的时候
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第1张图片
简单工厂的使用
方法一:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第2张图片方法二:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第3张图片
思路图:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第4张图片

工厂方法的使用
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第5张图片
思路图:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第6张图片

抽象工厂的使用

  1. 定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
  2. 使用场景:
    1. 客户端(应用层)不依赖于产品类实例如何被创建,实现等细节
    2. 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
    3. 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第7张图片工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第8张图片
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第9张图片工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第10张图片

优点:
具体产品在应用层的代码隔离,无需关系创建的细节
将一个系列的产品统一到一起创建
缺点:
规定了所有可能被创建的产品集合,产品族中扩展新的产品困难
增加了系统的抽象性和理解难度

思路图:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第11张图片工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第12张图片

总结:

  1. 简单工厂模式(静态工厂模式)
    虽然某种程度上不符合设计原则,但实际使用最多
  2. 工厂方法模式
    不修改已有类的前提下,通过增加新的工厂类实现扩展
  3. 抽象工厂模式
    不可以增加产品,可以增加产品族

代理模式
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第13张图片
代理模式的分类:

  1. 静态代理
  2. 动态代理

静态代理
角色分析:

  1. 抽象角色:一般会使用接口或者抽象类解决
  2. 真实角色:被代理的角色
  3. 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  4. 客户:访问代理对象的人

初始模式模拟:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第14张图片
代理模式模拟:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第15张图片
代理模式模拟功效:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第16张图片思路图:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第17张图片
代理模式的好处:

  1. 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  2. 公共内容交给代理角色,实现业务的分工
  3. 公共业务发生扩展的时候,方便集中管理

缺点:

  1. 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

动态代理

动态代理的类别

	基于接口的代理(JDK)

	基于类的动态代理(CGLIB)

静、动态代理(jdk,cglib)的异同

	1.静态代理简单,其代理模式是动态代理的理论基础

	2.jdk动态代理,需要有顶层接口才能使用,且在只有顶层接口的时候也可以使用,常见的有Mybatis
	  的mapper文件是代理,使用反射完成的,使用了动态生成字节码技术

	3.cglib动态代理,可以直接代理类,使用字节码技术,不能对final类进行继承。使用了动态生成字节码
	  技术

	4.动态代理的代理类是动态生成的,不是开发者手动写好的
	
动态代理的目的

	为了在业务中给需要实现的方法添加预处理或添加后续操作,但是不干预实现类的正常业务,把一些基本

	业务和主要的业务逻辑分离,我们所熟知的spring的aop即为动态代理实现的

动态代理的核心

	Java动态代理机制中,基于jdk的动态代理需要知道两个重要的类和接口,Proxy(类)和

	InvocationHandler(接口),通过两者实现动态代理。

	值得一提的是,InvocationHandler在spring框架中被广泛使用,这意味着理解了InvocationHandler

	将为spring源码的学习打下坚实的基础。

使用步骤

	1.创建一个接口

	2.实现接口

	3.创建实现了接口的类的代理类

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第18张图片
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第19张图片

简单讲解
	-----------------------------------------------------------
	proxyInvocationHandler 	-->调用处理程序

	Proxy					-->代理实例

	InvocationHandler		-->调用处理程序要实现的一个接口
	-----------------------------------------------------------
	1.InvocationHandler接口是Proxy代理实例的调用处理程序实现的一个接口
	
	2.每一个proxy代理实例都有一个关联的调用处理程序

	3.在代理实例调用方法时,方法调用,且被编码分派到调用处理的程序的invoke方法
	-----------------------------------------------------------
	每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联

	到了实现该接口的动态代理类调用处理程序中,当开发者通过动态代理对象调用一个方法时候,这个方法

	的调用就会被转发到实现InvovationHandler接口类的Invoke方法来调用,看如下invoke方法:

	public Object invoke(Object proxy,Method method ,Object[] args) throws Throwable{
	}

	1.proxy		代理类代理的真实代理对象com.sun.proxy.$Proxy()

	2.method	我们所要调用某个对象真实的方法的Method对象

	3.args		指代理对象方法传递的参数
	


		解析proxy

			Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是开发者最常用的是

			newProxyInstance方法

			public static Object newProxyInstance(ClassLoader loader, 
                                            	  Class<?>[] interfaces, 
                                                  InvocationHandler h)

			这个方法的作用就是创建一个代理类对象,它接收三个参数

				1.loader

					一个classloader对象,定义了由哪个classloader对象对生成的代理类
			
					进行加载

				2.Interfaces

					一个interface对象数组,表示开发者将要给代理对象提供一组什么样的接口

					,如果提供了一个接口对象数组,那么也就是声明了代理类实现了这些接口,

					代理类就可以调用接口中声明的所有方法

				3.h

					一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会

					关联哪一个InvocationHandler对象上,并最终由其调用
					

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第20张图片


建造者模式

含义

	建造者属于创建型模式,它提供了一种创建对象的最佳方式。

	建造者模式能够将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

	换句话说,就是调用相同的创建对象的方法(建造过程)可以创建出不同的对象。
举例

	我要创建一部手机,我需要先有手机的几个核心部件,例如:屏幕、电池、听筒、话筒、机身等。
	public class MobilePhone {
	
	    //手机屏幕
	    private String screen;
	    //电池
	    private String battery;
	    //话筒
	    private String microphone;
	    //听筒
	    private String phoneReceiver;
	    //机身
	    private String phoneBody;
	
	    public String getScreen() {
	        return screen;
	    }
	
	    public void setScreen(String screen) {
	        this.screen = screen;
	    }
	
	    public String getBattery() {
	        return battery;
	    }
	
	    public void setBattery(String battery) {
	        this.battery = battery;
	    }
	
	    public String getMicrophone() {
	        return microphone;
	    }
	
	    public void setMicrophone(String microphone) {
	        this.microphone = microphone;
	    }
	
	    public String getPhoneReceiver() {
	        return phoneReceiver;
	    }
	
	    public void setPhoneReceiver(String phoneReceiver) {
	        this.phoneReceiver = phoneReceiver;
	    }
	
	    public String getPhoneBody() {
	        return phoneBody;
	    }
	
	    public void setPhoneBody(String phoneBody) {
	        this.phoneBody = phoneBody;
	    }
	}
	每一部手机都是上方这个类的对象,在创建一部手机的时候都要保证这几个核心部件的创建。

	所以创建手机是需要一个标准规范的,因为这几个核心部件都可以是不同的型号,

	不同的型号的部件制造出来的手机也是不同的,这样就有了下面建造规范的接口。
	public interface IBuildPhone {
	
	    /**
	     * 建造手机屏幕
	     */
	    void buildScreen();
	
	    /**
	     * 建造手机电池
	     */
	    void buildBattery();
	
	    /**
	     * 建造手机听筒
	     */
	    void buildMicrophone();
	
	    /**
	     * 建造手机话筒
	     */
	    void buildPhoneReceiver();
	
	    /**
	     * 建造手机机身
	     */
	    void buildPhoneBody();
	}
	有了规范了,就可以创建手机了,先创建一个iphoneX。
	public class IPhoneX implements IBuildPhone {
	
	    private MobilePhone mobilePhone;
	
	    public IPhoneX(){
	        mobilePhone =  new MobilePhone();
	    }
	
	    /**
	     * 建造手机屏幕
	     */
	    @Override
	    public void buildScreen() {
	        mobilePhone.setScreen("OLED显示屏");
	    }
	
	    /**
	     * 建造手机电池
	     */
	    @Override
	    public void buildBattery() {
	        mobilePhone.setBattery("2700mAh电池容量");
	    }
	
	    /**
	     * 建造手机听筒
	     */
	    @Override
	    public void buildMicrophone() {
	        mobilePhone.setMicrophone("听筒");
	    }
	
	    /**
	     * 建造手机话筒
	     */
	    @Override
	    public void buildPhoneReceiver() {
	        mobilePhone.setPhoneReceiver("话筒");
	    }
	
	    /**
	     * 建造手机机身
	     */
	    @Override
	    public void buildPhoneBody() {
	        mobilePhone.setPhoneBody("iphoneX机身");
	    }
	
	    /**
	     * 创建手机
	     * @return
	     */
	    public MobilePhone build(){
	        return mobilePhone;
	    }
	}
	创建手机的工具写好了,下面就可以使用了。
	public class Director {
	
	    /**
	     * 建造一部手机
	     * @param buildPhone
	     * @return
	     */
	    public MobilePhone createMobilePhone(IBuildPhone buildPhone){
	
	        buildPhone.buildBattery();
	        buildPhone.buildMicrophone();
	        buildPhone.buildScreen();
	        buildPhone.buildPhoneReceiver();
	        buildPhone.buildPhoneBody();
	
	        return buildPhone.createMobilePhone();
	    }
	
	    @Test
	    public void thatTest(){
	        System.out.println(JSON.toJSONString(createMobilePhone(new IPhoneX())));
	    }
	}
	上方关键的方法在createMobilePhone()方法,这个方法接收一个IBuildPhone接口的对象,

	所以只要符合这个创建手机规范的对象都可以创建一部手机。

	createMobilePhone()方法可以接收new IPhoneX()这样一个对象,也可以接收new IPhone8()new FindX()等等。

	具体使用方法在thatTest()方法中。这个方法的运行结果是:

	{"battery":"2700mAh电池容量","microphone":"听筒","phoneBody":"iphoneX机身","phoneReceiver":"话筒","screen":"OLED显示屏"}
上面这个例子的实现过程就使用了我们今天要说的建造者模式,我们来分析一下建造者模式的结构。

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第21张图片

在建造者模式的结构图中包含如下4个角色

	Builder(抽象建造者):
	
					   它(IBuildPhone)为创建一个产品的各个部件指定了标准,规定了要创建复杂对象需要创建哪些部分,

					   并不直接创建对象的具体部分。

	Product(产品角色):

					  它(MobilePhone)是被建造的复杂对象,包含多个组成部分,

					  具体建造者创建该产品的内部表示并定义它的装配过程。

	Director(指挥者):

					 指挥者(Director),它复杂安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,

					 可以在Director的方法中调用建造者对象的部件构造与装配方法,完成建造复杂对象的任务。

					 客户端一般只需与Director进行交互。

	ConcreteBuilder(具体建造者):
	
	                它实现了Builder接口(IPhoneX),实现各个部分的具体构造和装配方法,定义并明确它所创建的复杂对象,

	                也可以提供一个方法返回创建好的复杂产品对象。
建造者模式灵活使用

	好了,建造者模式到这里就算是介绍完了,然后说一说我们平时在项目中是怎么使用建造者模式的。

	先说一下场景,我们一般在开发的过程中都是需要分层的,MVC这个不一般人都不陌生吧,Model-View-Controller。

	(我这里只是举例子不一定真的项目中就这样用)

	那我们的数据在每一层的传输过程中如果需要增加或删除些额外的功能怎么实现呢?

	例子,如下面一个实体类
	public class Person {
	    
	    private Long id;
	    
	    private String name;
	    
	    private int age;
	    
	    private String address;
	
	    public Long getId() {
	        return id;
	    }
	
	    public void setId(Long id) {
	        this.id = id;
	    }
	
	    public String getName() {
	        return name;
	    }
	
	    public void setName(String name) {
	        this.name = name;
	    }
	
	    public int getAge() {
	        return age;
	    }
	
	    public void setAge(int age) {
	        this.age = age;
	    }
	
	    public String getAddress() {
	        return address;
	    }
	
	    public void setAddress(String address) {
	        this.address = address;
	    }
	}
如果说这个类是一个orm框架需要的实体类,它最好的场景是只被后端的数据操作使用,

但是controller中有一个add方法,这个方法是新增一个人员,add方法接收的参数是一个人员对象,

但是这个对象和上面这个Person得属性有些差别,

例如这个对象里面有请求ip,以及这个对象中没有id这个字段(id在数据库中自增,所以前端不允许传过来id )。

这个时候就不能使用Person类的对象作为add的方法了,需要再创建一个类专门来给Controller使用。
	/**
	 * Controller使用的参数类
	 */
	public class PersonVO {
	
	    private String name;
	
	    private int age;
	
	    private String address;
	
	    //ip地址
	    private String requestIP;
	
	    public String getName() {
	        return name;
	    }
	
	    public void setName(String name) {
	        this.name = name;
	    }
	
	    public int getAge() {
	        return age;
	    }
	
	    public void setAge(int age) {
	        this.age = age;
	    }
	
	    public String getAddress() {
	        return address;
	    }
	
	    public void setAddress(String address) {
	        this.address = address;
	    }
	
	    public String getRequestIP() {
	        return requestIP;
	    }
	
	    public void setRequestIP(String requestIP) {
	        this.requestIP = requestIP;
	    }
	}
参数对象可以创建了, 但是PersonVO的对象是需要转成Person的对象的,这样才能插入到数据库中(数据库的insert方法的参数是Person对象)。这种转换操作其实也简单如下代码:
	public Person convert2Person(PersonVO personVO){
	
	        Person person = new Person();
	
	        person.setName(personVO.getName());
	        person.setAge(personVO.getAge());
	        person.setAddress(personVO.getAddress());
	        
	        return person;
	    }
但是我们通常是不这么做的,因为如果要转换的这个对象的字段很多那需要写很多次对象调setter方法来进行赋值。

一种方式是直接写一个将所有属性当做参数的构造方法,直接一个一个把属性值传入就可以了,

这种方式最简单暴力。

还有一种方式就是需要包装一下这种方式,把Person改造一下。
	public class Person {
	
	
	    private Long id;
	
	    private String name;
	
	    private int age;
	
	    private String address;
	
	    public Long getId() {
	        return id;
	    }
	
	    public void setId(Long id) {
	        this.id = id;
	    }
	
	    public String getName() {
	        return name;
	    }
	
	    public void setName(String name) {
	        this.name = name;
	    }
	
	    public int getAge() {
	        return age;
	    }
	
	    public void setAge(int age) {
	        this.age = age;
	    }
	
	    public String getAddress() {
	        return address;
	    }
	
	    public void setAddress(String address) {
	        this.address = address;
	    }
	
	     Person(){}
	     
	     Person(String name,int age,String address){
	        this.name = name;
	        this.age = age;
	        this.address = address;
	    }
	
	    public static Person builder(){
	        return new Person();
	    }
	
	    public Person name(String name){
	        this.name = name;
	        return this;
	    }
	
	    public Person age(int age) {
	        this.age = age;
	        return this;
	    }
	
	    public Person address(String address) {
	        this.address = address;
	        return this;
	    }
	
	    public Person build(){
	        return new Person(name,age,address);
	    }
	    
	}
后面新增了两个构造函数,以及一个builder()方法和一个build()方法,还有几个赋值方法,需要注意的是赋值方法和setter方法的区别,

这样的赋值方法是在赋值后将当前对象返回,用来实现链式调用。

这样在对象转换的时候就可以这样用了:
	public Person convert2Person(PersonVO personVO){
	
	        return Person.builder()
	                .name(personVO.getName())
	                .age(personVO.getAge())
	          .address(personVO.getAddress())
	                .build();
	    }
这种方式其实也是一种建造者模式的应用,这种方式在构建对象的过程实现起来更灵活,例如如果这个对象就只有前两个参数有值,

address是没有内容的,那可以直接这样写:
	public Person convert2Person(PersonVO personVO){
	
	        return Person.builder()
	                .name(personVO.getName())
	                .age(personVO.getAge())
	                .build();
	    }
在填充了两个属性后就直接调用build()方法区创建对象。

其实为了实现这种创建对象的方式,每次除了写getter/setter方法后还需要写这么多其他的代码,这样是有点麻烦的,所以在日常的开发

过程中,我们是没必要写额外的代码来实现这种方式,可以用工具(lombok)来实现。
	@Data
	@Builder
	@AllArgsConstructor
	@NoArgsConstructor
	public class Person {
	
	
	    private Long id;
	
	    private String name;
	
	    private int age;
	
	    private String address;
	
	}
	@Data 这个注解代表实现了所有非final属性的getter/setter方法,以及重写了toString方法和hashCode方法。
	
	@AllArgsConstructor 这个注解代表实现了一个包含全部属性为参数的构造方法(Person(Long id,String name,int age, String address))。
	
	@NoArgsConstructor 这个注解代表实现了一个没有任何参数的构造方法(Person())。
	
	@Builder 这个注解代表实现了上面介绍的那种灵活的创建对象的建造者模式(使用这个注解时需要依赖上面3个注解,原因看这种方式的实现过程就能明白了)。

简单举例
【1】买汽车

  1. 工厂(建造者模式) :负责制造汽车(组装过程细节在工厂内)
  2. 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装(车轮、车门、发动机、方向盘等等))

【2】造房子

  1. 地基、钢筋、铺电线、粉刷。如果要盖一座房子,首先要找一个建筑公司或工程承包商(指挥者)。承包商指挥工人(具体建造者)火来造房子(产品),最后验收

方法一:
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第22张图片工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第23张图片
思路图:

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第24张图片
进一步解释:

上面的示例是Builder模式的常规用法,导演类Director 在Builder模式中具有很重要的作用,它用于指导具体建造者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合

通过静态内部类方式实现零件无需装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品。
比如:麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户,比第一种方式少了指挥者,主要是因为第二种方式吧指挥者交给用户来操作,产品的创建更加简单灵活。

方法二:

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第25张图片工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第26张图片

优点:
产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
具体的建造者类之间是相互独立的,这有利于系统扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则”。
缺点;
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大

应用场景:

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备 共性;
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
  • 适合于一个具有较多的零件(属性)的产品)(对象)的创建过程

建造者和抽象工厂的比较:

  • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
  • 在抽象工厂模式汇总,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车

单例模式

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
单例模式可以说只要是一个合格的开发者都会写,但是如果要深究,小小的单例模式可以牵扯到很多
东西,比如多线程是否安全,是否懒加载,性能等等。还有开发者知道单例模式的多少种写法。如何
防止反射破坏单例模式

  1. 常见的五种单例模式实现方式:
  • 饿汉式 (线程安全,调用效率高,不能延时加载)
  • 懒汉式 (线程安全,调用效率不高,可以延时加载)
  • DCL懒汉式 (由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
  • 饿汉式改进:静态内部类式 (线程安全,调用效率高,可以延时加载)
  • 枚举单例 (线程安全,调用效率高,不能延时加载)

饿汉式

工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第27张图片[注]
饿汉式是最简单的单例模式的写法,保证了线程的安全,在很长时间里,我都是饿汉模式来完成单例的,因为够简单,后来才知道饿汉式会有一点问题,如下

public class Hungry {
    private byte[] data1 = new byte[1024];
    private byte[] data2 = new byte[1024];
    private byte[] data3 = new byte[1024];
    private byte[] data4 = new byte[1024];
    
    private Hungry() {
    }

    private final static Hungry hungry = new Hungry();

    public static Hungry getInstance() {
        return hungry;
    }
}

[注]
在Hungrey 类中,定义了四个byte数组,当代码一运行,这四个数组就被初始化,并且放入内存了,如果长时间没有用到getInstance方法,不需要Hungrey类的对象,这不是一种浪费吗?开发者希望的是只有用到了getInstance方法,才会去初始化单例类,才会加载单例类中的数据,所以就有了第二种单例模式

懒汉式
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第28张图片
DCL懒汉式
工厂、抽象工厂、代理模式、建造者、单例模式(图解)_第29张图片

优点

	由于单例模式只生成一个实例,减少了系统性能开销
	
	单例模式可以在系统设置全局的访问点,优化共享资源访问

	单例模式可以说只要是一个合格的开发都会写,但是如果要深究,小小的单例模式可以牵扯很多东西

你可能感兴趣的:(工厂、抽象工厂、代理模式、建造者、单例模式(图解))