面向面试编程:设计模式——创建型模式

203 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情

面试官:说说你了解的设计模式

Factory Method 工厂方法模式

定义

通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。

作用

将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。

Abstract Factory 抽象工厂模式

定义

提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类。

优缺点

优点

  • 具体产品在应用层的代码隔离,无需关系创建的细节。
  • 将一个系列的产品统一到一起创建。

缺点

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难。
  • 增加了系统的抽象性和理解难度。

使用场景

  • 客户端(应用层)不依赖与产品类示例如何被创建、实现等细节。
  • 强调一系列相关的产品对象(数据同一产品族)一起使用创建对象需要大量的重复代码。
  • 提供一个产品类的库,所有的产品以同样的接口出现,使得客户端不依赖于具体实现。

Builder 建造者模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

作用

在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

  • 用户只需要给出指定复杂对象的类型和内容。
  • 建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)。

解决的问题

  • 方便用户创建复杂的对象(不需要知道实现过程)。
  • 代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)。

优缺点

优点

  • 易于解耦:将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
  • 易于精确控制对象的创建:将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
  • 易于拓展:增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则”。

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

应用场景

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性。
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

Singleton 单例模式

定义

实现1个类只有1个实例化对象 & 提供一个全局访问点。

作用

保证1个类只有1个对象,降低对象之间的耦合度。

优缺点

优点

  • 提供了对唯一实例的受控访问。
  • 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
  • 可以根据实际情况需要,在单例模式的基础上扩展做出双例模式,多例模式。

缺点

  • 单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单一职责原则”。
  • 如果实例化的对象长时间不被利用,会被系统认为是垃圾而被回收,这将导致对象状态的丢失。

实现方式

懒汉式

  • 第一次调用啊getInstance()方法时,再去new实例。
  • 此时可能会产生线程安全问题。
  • 使用双重检查锁的写法可以解决线程安全问题。
  • 声明需要加上volatile关键字。

饿汉式

  • 初始化的时候直接new实例。
  • 唯一缺点,可能用不上,但是已经占用内存了。

静态内部类

  • 在单例对象内部写一个静态内部类,内部类来new实例。
  • 类加载的时候并不会加载内部类,只有在使用的时候才加载。
  • 在第一次调用getInstance()方法时,由内部类来创建实例。
  • 由JVM保证线程安全(JVM只会加载一次Class对象)。

枚举单例

  • 高端写法,最完美的单例。
  • 枚举类中只有INSTANCE值。
  • 不会被反序列化,因为枚举类没有构造方法,就算拿到class文件也无法构造对象。
  • 反序列化返回的也是这个INSTANCE值。

Prototype 原型模式

定义

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

作用

主要用于对象的复制,Prototype类需要具备以下两个条件。

实现Cloneable接口

  • 在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。
  • 在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。

重写Object类中的clone方法

  • Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝。
  • 但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。

优点

  • 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
  • 简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

应用场景

在需要重复地创建相似对象时可以考虑使用原型模式。

比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多

注意事项

使用原型模式复制对象不会调用类的构造方法

  • 对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。
  • 访问权限都对原型模式无效,clone方法直接无视构造方法的权限,单例模式与原型模式是冲突的,在使用时要特别注意。

深拷贝与浅拷贝

  • Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。
  • 如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
  • 会发生深拷贝的有java中的8种基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。