创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。
创建型模式分为以下几种。
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
以上 5 种创建型模式,除了工厂方法模式属于类创建型模式,其他的全部属于对象创建型模式,我们将在之后的教程中详细地介绍它们的特点、结构与应用
一、单例模式
1. 单例模式的优缺点
单例模式的优点:
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
单例模式的缺点:
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
2. 单例模式的应用场景
对于 java来说,单例模式可以保证在一个 JVM 中只存在单一实例。单例模式的应用场景主要有以下几个方面。
- 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
- 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
- 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
- 频繁访问数据库或文件的对象。
- 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
3. 单例模式的结构与实现
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
单例模式有两种实现形式:
- 饿汉单例
public class HungerSingleton {
private static final HungerSingleton HUNGER_SINGLETON = new HungerSingleton();
public HungerSingleton getInstance() {
return HUNGER_SINGLETON;
}
private HungerSingleton() {
}
public static void main(String[] args) {
HungerSingleton hungerSingleton = new HungerSingleton();
HungerSingleton instance = hungerSingleton.getInstance();
System.out.println(instance);
}
}
- 懒汉单例
public class LazySingleton {
private static volatile LazySingleton lazySingleton = null;
public synchronized LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
private LazySingleton() {
}
public static void main(String[] args) {
LazySingleton lazySingleton = new LazySingleton();
LazySingleton instance = lazySingleton.getInstance();
System.out.println(instance);
}
}
二、原型模式
1. 原型模式的定义与特点
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多
原型模式的优点:
- java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
原型模式的缺点:
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
2. 原型模式的结构与实现
浅克隆实现
public class ShallowClone implements Cloneable {
String name;
int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
@Override
public String toString() {
return "ShallowClone{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
ShallowClone shallowClone = new ShallowClone();
shallowClone.setName("zhangSan");
shallowClone.setAge(18);
ShallowClone clone = (ShallowClone) shallowClone.clone();
System.out.println(clone.toString());
}
深克隆实现
public class DepthClone implements Cloneable {
MyObject myObject;
int i;
public MyObject getMyObject() {
return myObject;
}
public void setMyObject(MyObject myObject) {
this.myObject = myObject;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
public Object clone2() {
DepthClone clone = null;
try {
clone = (DepthClone) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
clone.myObject = (MyObject) clone.myObject.myClone();
return clone;
}
@Override
public String toString() {
return "DepthClone{" +
"myObject=" + myObject +
", i=" + i +
'}';
}
public static void main(String[] args) {
//测试浅克隆
DepthClone depthClone = new DepthClone();
depthClone.setI(12);
depthClone.setMyObject(new MyObject(11));
DepthClone tempObject = (DepthClone) depthClone.clone();
System.out.println(tempObject.toString());
tempObject.myObject.i = 54;
System.out.println(depthClone.toString());
System.out.println(tempObject.toString());
//测试深克隆
DepthClone depthClone2 = new DepthClone();
depthClone2.setI(13);
depthClone2.setMyObject(new MyObject(11));
DepthClone tempObject2 = (DepthClone) depthClone.clone();
System.out.println(tempObject2.toString());
tempObject2.myObject.i = 54;
System.out.println(depthClone2.toString());
System.out.println(tempObject2.toString());
}
三、简单工厂模式
1. 简单工厂模式定义与特点
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。
简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。
2. 简单工厂模式的优缺点
优点:
- 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
- 客户端无需知道所创建具体产品的类名,只需知道参数即可。
- 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
缺点:
- 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
- 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
- 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
3. 简单工厂模式的结构与应用实现
应用场景:对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
简单工厂模式的主要角色如下:
- 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
- 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
- 具体产品(ConcreteProduct):是简单工厂模式的创建目标。
public class SimpleFactory {
interface Shape {
void description();
}
static class Circle implements Shape {
@Override
public void description() {
System.out.println("我是圆形");
}
}
static class Rectangle implements Shape {
@Override
public void description() {
System.out.println("我是长方形");
}
}
public static Shape createComputer(String type) {
Shape shape = null;
switch (type) {
case "circle":
shape = new Circle();
break;
case "rectangle":
shape = new Rectangle();
break;
}
return shape;
}
public static void main(String[] args) {
//测试简单工厂类
SimpleFactory.createComputer("circle").description();
}
四、工厂方法模式
1. 工厂方法模式的优缺点
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
- 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
- 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
- 类的个数容易过多,增加复杂度
- 增加了系统的抽象性和理解难度
- 抽象产品只能生产一种产品,此弊端可使用抽象工厂方法解决
2. 工厂方法模式的结构与应用实现
应用场景:
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌
模式的结构
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
public class FactoryMethod {
interface Shape {
void draw();
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("我是圆形");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("我是长方形");
}
}
interface ShapeFactory {
Shape newProduct();
}
class CircleFactory implements ShapeFactory {
@Override
public Shape newProduct() {
return new Circle();
}
}
class RectangleFactory implements ShapeFactory {
@Override
public Shape newProduct() {
return new Rectangle();
}
}
public static void main(String[] args) {
FactoryMethod.ShapeFactory factoryMethod = new FactoryMethod().new CircleFactory();
FactoryMethod.Shape product = factoryMethod.newProduct();
product.draw();
}
五、抽象工厂模式
1. 抽象工厂模式定义与特点
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件。
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
2. 抽象工厂模式的优缺点
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
3. 抽象工厂模式的结构与实现
抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
public class AbstractFactory {
interface Shape {
void draw();
}
abstract class Circle implements Shape {
}
class RedCircle extends Circle {
@Override
public void draw() {
System.out.println("我是红色的圆形");
}
}
class BlueCircle extends Circle {
@Override
public void draw() {
System.out.println("我是蓝色的圆形");
}
}
abstract class Rectangle implements Shape {
}
class RedRectangle extends Rectangle {
@Override
public void draw() {
System.out.println("我是红色的长方形");
}
}
class BlueRectangle extends Rectangle {
@Override
public void draw() {
System.out.println("我是蓝色的长方形");
}
}
interface ShapeFactory {
Shape getCircle();
Rectangle getRectangle();
}
class RedShapeFactory implements ShapeFactory {
@Override
public Shape getCircle() {
return new RedCircle();
}
@Override
public Rectangle getRectangle() {
return new RedRectangle();
}
}
class BlueShapeFactory implements ShapeFactory {
@Override
public Shape getCircle() {
return new BlueCircle();
}
@Override
public Rectangle getRectangle() {
return new BlueRectangle();
}
}
public static void main(String[] args) {
//测试抽象工厂
AbstractFactory abstractFactory = new AbstractFactory();
ShapeFactory redShapeFactory = abstractFactory.new RedShapeFactory();
redShapeFactory.getCircle().draw();
redShapeFactory.getRectangle().draw();
BlueShapeFactory blueShapeFactory = abstractFactory.new BlueShapeFactory();
blueShapeFactory.getCircle().draw();
blueShapeFactory.getRectangle().draw();
}