b1cat`s

创建型模式

Word count: 1.5kReading time: 6 min
2020/02/23

创建型模式

创建型模式关注点是如何创建对象,其核心思想是分离对象的创建和使用。

创建型模式包括:

  • 工厂方法:Factory Method
  • 抽象工厂:Abstract Factory
  • 建造者:Builder
  • 原型:Prototype
  • 单例:Singleton

工厂方法:

工厂方法:定义一个用于创建对象的接口,让子类决定去实例化哪一个类。即Factory Method, 是一种对象创建型模式。

目的是使得创建对象和使用对象相分离,并且客户端总是引用抽象工厂和抽象产品。

以实现一个解析字符串到NumberFactory为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//FactoryMethod.java
//工厂接口,输出产品Number
package org.b1cat.degin;

public interface FactoryMethod {
Number parse(String s);

//客户端通过静态类调用工厂子类
static NumberFactoryMethod getFactory() {
return nfm;
}

static NumberFactoryMethod nfm = new NumberFactoryMethod();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//NumberFactoryMethod
//工厂实现类,目的返回一个BigDecImal的产品
package org.b1cat.degin;

import java.math.BigDecimal;

public class NumberFactoryMethod implements FactoryMethod{

@Override
public Number parse(String s) {
// TODO Auto-generated method stub
return new BigDecimal(s);
}

}
1
2
3
4
5
6
7
8
9
10
11
//客户端使用
//客户端完全忽略真正的工厂`NumberFactoryMethod`和实际的产品`BigDecimal`
package org.b1cat.degin;

public class use {
public static void main(String[] args) throws Exception{

FactoryMethod fac = FactoryMethod.getFactory();
Number res = fac.parse("123.345");
}
}

以上可以简化为静态工厂方法

1
2
3
4
5
public class NumberFactory{
public static Number parse(String a){
return new BigDecimal(s);
}
}

常见静态工厂方法

1
2
3
4
Integer n = Integer.valueOf(100);
//通过静态方法创建Integer
//这种方法无需知道valueOf内部创建的新实例还是缓存
//直接通过new获取会加大内存消耗

抽象工厂

抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式是一个比较复杂的创建型模式。

它和工厂方法不一样,它解决的问题比较复杂,不但工厂是抽象的,产品是抽象的,还有多个不同产品需要创建。因此这个抽象工厂会对应多个实际工厂,每个实际工厂对象创建多个实际产品。

image-20200223155139891

生成器

生成器模式:是使用多个小型工厂最终创建出一个完整对象。

Builder模式是为了创建一个复杂的对象,需要多个步骤完成创建,或者需要多个零件组装的场景,且创建过程中可以灵活调用不同的步骤或组件。

比如说将Markdown转为html,这个过程比较复杂,需要拆解为一步一步的解析

  • 如果以#开头,使用HeadingBuilder转换;
  • 如果以>开头,使用QuoteBuilder转换;
  • 如果以---开头,使用HrBuilder转换;
  • 其余使用ParagraphBuilder转换。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HtmlBuilder {
private HeadingBuilder headingBuilder = new HeadingBuilder();
private HrBuilder hrBuilder = new HrBuilder();
private ParagraphBuilder paragraphBuilder = new ParagraphBuilder();
private QuoteBuilder quoteBuilder = new QuoteBuilder();

public String toHtml(String markdown) {
StringBuilder buffer = new StringBuilder();
markdown.lines().forEach(line -> {
if (line.startsWith("#")) {
buffer.append(headingBuilder.buildHeading(line)).append('\n');
} else if (line.startsWith(">")) {
buffer.append(quoteBuilder.buildQuote(line)).append('\n');
} else if (line.startsWith("---")) {
buffer.append(hrBuilder.buildHr(line)).append('\n');
} else {
buffer.append(paragraphBuilder.buildParagraph(line)).append('\n');
}
});
return buffer.toString();
}
}

其中HeadingBuilder实现为

1
2
3
4
5
6
7
8
9
public class HeadingBuilder {
public String buildHeading(String line) {
int n = 0;
while (line.charAt(0) == '#') {
n++;
line = line.substring(1);
}
return String.format("<h%d>%s</h%d>", n, line.strip(), n);
}

原型

原型模式,即Prototype,是指创建新对象的时候,根据现在又的一个原型来创建。

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

对于普通类,我们如何实现原型拷贝?Java的Object提供了一个clone()方法,它的意图就是复制一个新的对象出来,我们需要实现一个Cloneable接口来标识一个对象是“可复制”的:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Student implements Cloneable{
private int id;
private String name;
private int score;

public Object clone(){ //复制对象并返回
Student std = new Student();
std.id = this.id;
std.name = this.name;
std.score = this.score;
return std;
}
}

这样使用时因为clone()方法签名定义在Object中,返回类型也是Object,所以要强制转化类型,比较麻烦。

实际上,使用原型模式更好的方式是定义一个copy()方法,返回明确的类型:

1
2
3
4
5
6
7
8
9
10
11
12
public class Student{
private String name;
private int score;

public Student copy{
Student std = new Student();
std.id = this.id;
std.name = this.name;
std.score = this.score;
return std;
}
}

原型模式应用不是很广泛,因为很多实例会持有类似文件、Socket这样的资源,而这些资源是无法复制给另一个对象共享的,只有存储简单类型的“值”对象可以复制。

单例模式

单例模式(Singleton)是为了保证在一个进程中,某个类有且仅有一个实例。

保证一个类只有一个实例,并提供一个访问它的全局访问点。

  • 只有private构造方法,确保外部无法实例化。
  • 通过private static变量持有唯一实例,保证全局唯一。
  • 通过public static 返回此唯一实例,使外部调用方法能获取到。
1
2
3
4
5
6
7
8
9
public class Singleton{
private Singleton(){}

public static final Singleton INSTANCE = new Singleton();

public static Singleton getInstance() {
return INSTANCE;
}
}

另一种实现Singleton是利用Java的enum,因为Java保证枚举类的每个枚举都是单例,

所以我们只需要编写一个只有一个枚举的类即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum World{
//唯一枚举
INSTANCE;

private String name = "world";

public String getName(){
return this.name;
}

public void setName(String name){
this.name = name;
}
}

调用只需要:

String name = World.INSTANCE.getName();

使用枚举实现Singleton还避免了第一种方式实现Singleton的一个潜在问题:即序列化和反序列化会绕过普通类的private构造方法从而创建出多个实例,而枚举类就没有这个问题。

Singleton模式既可以严格实现,也可以以约定的方式把普通类视作单例。

CATALOG
  1. 1. 创建型模式
    1. 1.0.1. 工厂方法:
    2. 1.0.2. 抽象工厂
    3. 1.0.3. 生成器
    4. 1.0.4. 原型
    5. 1.0.5. 单例模式