创建型模式
创建型模式关注点是如何创建对象,其核心思想是分离对象的创建和使用。
创建型模式包括:
- 工厂方法:Factory Method
- 抽象工厂:Abstract Factory
- 建造者:Builder
- 原型:Prototype
- 单例:Singleton
工厂方法:
工厂方法:定义一个用于创建对象的接口,让子类决定去实例化哪一个类。即Factory Method
, 是一种对象创建型模式。
目的是使得创建对象和使用对象相分离,并且客户端总是引用抽象工厂和抽象产品。
以实现一个解析字符串到Number
的Factory
为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
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
|
package org.b1cat.degin;
import java.math.BigDecimal;
public class NumberFactoryMethod implements FactoryMethod{
@Override public Number parse(String s) { return new BigDecimal(s); }
}
|
1 2 3 4 5 6 7 8 9 10 11
|
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);
|
抽象工厂
抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式是一个比较复杂的创建型模式。
它和工厂方法不一样,它解决的问题比较复杂,不但工厂是抽象的,产品是抽象的,还有多个不同产品需要创建。因此这个抽象工厂会对应多个实际工厂,每个实际工厂对象创建多个实际产品。
生成器
生成器模式:是使用多个小型工厂最终创建出一个完整对象。
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模式既可以严格实现,也可以以约定的方式把普通类视作单例。