要想更全面理解设计模式,建议先查看Spring的设计模式快速入门干货,前半部分是设计模式的分类和综述,与Spring无关。
对象的创建会消耗掉系统的很多资源,所以对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:
说明:严格来说,简单工厂模式不是GoF总结出来的23种设计模式之一。 GoF在《设计模式》一书中将工厂模式分为两类:工厂方法模式与抽象工厂模式。将简单工厂模式看为工厂方法模式的一种特例,两者归为一类。
我们的目标是客户提出某个要求,我们的工厂负责new出来这个产品。 简单工厂模式的做法:
这样的缺点是新增产品后工厂的静态类的代码会逐渐繁杂,不便于维护。所以诞生了工厂模式。 工厂模式的做法:
这样的缺点也很明显,客户需要new不同种类的产品,就先需要new对应的工厂。所以诞生了抽象工厂模式。 抽象工厂模式的做法:
工厂模式或者抽象工厂模式中,产品类或者工厂类实现几个接口,完全取决于业务的需要,并没有强制要求。
网上大部分博客在介绍工厂模式的时候都做了大篇幅的文字介绍和代码演示,实际上三个模式的区别并没有好好介绍清楚。所以本文一步到位的讲解三种模式区别。
建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性, 建造者模式通常包括下面几个角色:
一段代码就可以理解这个模式了:
public class Director {
public Product constructProduct(ConcreteBuilder concreteBuilder){
concreteBuilder.buildBasic();
concreteBuilder.buildWalls();
concreteBuilder.roofed();
return concreteBuilder.buildProduct();
}
}
原型(Prototype)模式是一种对象创建型模式,他采取复制原型对象的方法来创建对象的实例。使用原型模式创建的实例,具有与原型一样的数据。
public class Prototype implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}finally {
return null;
}
}
public static void main ( String[] args){
Prototype pro = new Prototype();
Prototype pro1 = (Prototype)pro.clone();
}
}
此处使用的是浅拷贝,关于深浅拷贝,大家可以另行查找相关资料。
在定义类的静态私有变量同时进行实例化。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
好处:线程安全;获取实例速度快 缺点:类加载即初始化实例,内存浪费
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
优点:线程安全,进行双重检查,保证只在实例未初始化前进行同步,效率高 缺点:实例非空判断,耗费一定资源
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式利用了classloder的机制来保证初始化instance时只有一个线程,但singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。如果实例化instance很消耗资源,我们想让他延迟加载,此外,我们不希望在Singleton类加载时就实例化,因为不能确保Singleton类还可能在其他的地方被主动使用从而被加载。这个时候,这种方式相比饿汉式就显得更合理。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
优点:写法简单,天然线程安全,可防止反射生成实例,是Effective Java作者Josh Bloch提倡的方式。
不过笔者用的最多的还是饿汉式,看不少源码也常用饿汉式,哈哈哈