0%

Java设计模式-创建型模式

一、单例模式

1.1 概述

单例模式就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法,即一次创建,终生使用

它有四种实现方式:饿汉式(静态常量/静态代码块)、懒汉式(线程不安全/安全)、静态内部类、枚举

饿汉式意思是提前实现对象的实例化

懒汉式的意思是需要的时候再进行对象的实例化,即Lazy Loading(懒加载)

1.1 饿汉式

1.1.1 静态变量

优点:

  • 实现简单
  • 在类装载时就完成了实例化,避免了线程同步问题

缺点:

  • 在类装载的时候就完成了实例化,没有达到Lazy Loading(懒加载)的效果,容易造成内存的浪费

结论:如果实例经常被调用,则可以使用

1
2
3
4
5
6
7
public class HungrySingleton{
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return instance;
}
}

1.1.2 静态代码块

优缺点和静态变量方式一致,就是将对象的初始化放在了静态代码块中

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HungrySingleton{
private static final HungrySingleton instance;

// 在静态代码块中,初始化单例对象
static{
instance=new HungrySingleton();
}

private HungrySingleton(){}
public static HungrySingleton getInstance(){
return instance;
}
}

1.2 懒汉式

1.2.1 线程不安全

这种方式容易造成线程不安全,在多线程的情况下,如线程a、b、c,a线程通过了判断体后进行了线程的切换,切换到了b线程,那么b线程也能通过判断体,这样就不符合单例模式的要求了,等于对instance进行了两次实例化。所以多线程环境下不能使用这种方式。

结论:不推荐使用

1
2
3
4
5
6
7
8
9
public class LazySingleton{
private static LazySingleton instance; //保证instance在所有线程中同步
private LazySingleton(){} // private避免类在外部被实例化
public static LazySingleton getInstance(){ //getInstance方法前加同步
if(instance==null)
instance=new LazySingleton();
return instance;
}
}

1.2.1 双重校验

解决了线程不同步的问题,在多个线程执行的时候,会有多个线程同时进入第一个判断体中,而第一个判断体有一个同步代码块。同步代码块是需要线程完全执行完里面的操作才进行线程的切换,那么如果前一个线程实现了对象的实例化,那么后面的线程进入同步代码块进行判断的时候,instance就不为空了,保证了线程安全。

声明的变量前的volatile关键字有两个作用:

  • 保证变量修改的可见性,如a线程对变量进行修改后,其他线程也能看见修改后的值
  • 禁止指令重排

结论:推荐使用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LazySingleton{
private static volatile LazySingleton instance=null; //保证instance在所有线程中同步
private LazySingleton(){} // private避免类在外部被实例化
public static LazySingleton getInstance(){
if(instance==null){
synchronized(LazySingleton.class){
if(instance==null)
instance=new LazySingleton();
}
}
return instance;
}
}

1.3 静态内部类

静态内部类方式在类被装载时不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化

类的静态属性只会在第一次加载类的时候初始化,

优点:

  • 避免了线程不安全
  • 利用静态内部类特点实现延迟加载,效率高

结论:推荐使用

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton{
private Singleton(){}

// 静态内部类SingletonInstance
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();

}
public static synchronized Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}

1.4 枚举

优点:

  • 实现简单
  • 避免了多线程同步问题

结论:推荐使用(Effective Java作者推荐)

1
2
3
4
5
6
enum Singleton{
INSTANCE,
public void method(){
// 方法
}
}

二、工厂模式

2.1 简单工厂模式

简单工厂设计模式类体如下:

简单来说就是:工厂类Factory可以createProduct()方法传入的标签选择对应的产品类对象的进行创建,这些具体产品类同属于一个抽象的公共产品类

简单工厂的设计在于简单,只有一个工厂类和一个抽象产品类

如果存在多个工厂,多种产品类型,这时就需要用到工厂模式

2.1.1 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 工厂类
public class SimpleFactory {
public static Product createProduct(String tag){
if ("A".equals(tag))
return new ProductA();
else
return new ProductB();
}

public static void main(String[] args) {
Product a = SimpleFactory.createProduct("B");
a.use();
}
}

// 抽象产品类
abstract class Product{
public void use(){
System.out.print("抽象产品类");
};
}

// 具体产品类A
class ProductA extends Product{
@Override
public void use() {
super.use();
System.out.println("--->"+"具体产品类A");
}
}

// 具体产品类B
class ProductB extends Product{
@Override
public void use() {
super.use();
System.out.println("--->"+"具体产品类B");
}
}

2.2 工厂模式

在简单工厂的基础上对工厂类进行抽象,现在可以构建多个具体工厂类了,不同的工厂用于生产不同类的产品

在具体工厂的扩展并不会修改源代码,只需要新建一个工厂类,比如FactoryC用于生产其他产品对象

但如果要新增一个产品类型,并让其中一个工厂进行生产时,这时则需要修改具体工厂类的源代码,不符合开闭原则

2.2.1 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 抽象工厂类
public abstract class AbstractFactory {
abstract Product createProduct(String type);

public Factory(){
Product product = null;

do{
String type = getType();
// 产品对象的创建推迟到具体工厂类中
product = createProduct(type);

if (product == null){
break;
}
product.use();
}while (true);

}

//写一个方法 可以获取客户希望订购的pizza
private String getType() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请在下面输入你要购买产品类型:");
String pizza = reader.readLine();
return pizza;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}

// 抽象产品类
public abstract class Product {
public abstract void use();
}

// 具体工厂类A
class FactoryA extends Factory{
@Override
Product createProduct(String type) {
Product product = null;
if("A1".equals(type))
return new ProductA1();
else if("A2".equals(type))
return new ProductA2();
return product;
}
}

// 具体工厂类B
class FactoryB extends Factory{
@Override
Product createProduct(String type) {
Product product = null;
if("B1".equals(type))
return new ProductB1();
else if("B2".equals(type))
return new ProductB2();
return product;
}
}

// A工厂生产产品A1
class ProductA1 extends Product{
@Override
public void use() {
System.out.println("使用产品A1");
}
}

// A工厂生产产品A2
class ProductA2 extends Product{
@Override
public void use() {
System.out.println("使用产品A2");
}
}

// B工厂生产产品B1
class ProductB1 extends Product{
@Override
public void use() {
System.out.println("使用产品B1");
}
}

// B工厂生产产品B1
class ProductB2 extends Product{
@Override
public void use() {
System.out.println("使用产品B2");
}
}

// 访问类
public class Client {
public static void main(String[] args) {
// 使用A工厂进行产品生产
Factory factoryA = new FactoryA();
}

}

2.3 抽象工厂模式

抽象工厂模式与工厂模式的区别在于将抽象工厂类改成了接口,接口的抽象层次更高,因此称为抽象工厂模式

2.4 应用

三、原型模式

原型模式提供对象复制的一种方式,即对象的克隆,克隆又分为深克隆和浅克隆

浅克隆:复制某一对象时,只复制其本身值类型的成员变量,不复制引用变量

深克隆:复制某一对象时,不仅复制其本身值类型的成员变量,还复制引用变量

原型模式结构:

  • 抽象原型类
  • 具体原型类
  • 访问类

3.1 实现

可以按照原型模式的结构来创建对应的类,但是Java中的Object类提供了一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供clone()方法来实现对象的浅克隆

此时Object类相当于抽象原型类,所有实现了Cloneable接口的类相当于具体原型类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具体原型类(浅克隆)
public class ConcretePrototype implements Cloneable {
private String name; // 值类型
private Product product; // 应用类型,深克隆需要调用product.clone()进行引用类型的复制

public Prototype clone() {
Object object = null;
try {
object = super.clone(); // 浅克隆
} catch (CloneNotSupportedException exception) {
System.err.println("Not support Cloneable");
}
return (Prototype)object;
}
}

3.2 应用

四、建造者模式

建造者模式结构:

  • 抽象建造者:规范产品对象的各个组成部分的建造
  • 具体建造者:具体化对象的各个组成部分的创建
  • 复杂产品类:需要被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程
  • 指挥者类:指挥建造者创建产品的各个部分,并返回产品对象

4.1 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 复杂产品类
public class Product {
private String partA; // 定义部件,部件可以是任意类型,包括值类型和引用类型
private String partB;
private String partC;
// 属性的Getter和Setter方法省略
}

// 抽象建造者类
public abstract class Builder {
// 创建产品对象
protected Product product = new Product();

public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();

// 返回产品对象
public Product getResult() {
return product;
}
}

// 具体建造者1
public class ConcreteBuilder1 extends Builder {
public void buildPartA() {
product.setPartA("A1");
}
public void buildPartB() {
product.setPartA("B1");
}
public void buildPartC() {
product.setPartA("C1");
}
}

// 指挥者类
public class Director {
private Builder builder;

public Director(Builder builder) {
this.builder = builder;
}
public void setBuilder(Builder builder) {
this.builder = builder;
}
// 产品的构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}

4.2 应用