文章目录
- 创建型模式
- 单例
- 饿汉式
- 懒汉式
- 存在的问题
- 工厂方法
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
创建型模式
关注点是如何创建对象,核心思想是要把对象创建和使用相分离,这样两者能相对独立地变换
包括:
1、工厂方法:Factory Method
2、抽象工厂:Abstarct Factory
3、建造者:Builder
4、原型:Prototype
5、单例:Singleton
单例
最简单的设计模式之一,目的是为了保证在一个进程中,某个类有且仅有一个实例。
结构
单例模式主要有以下角色:
- 单例类。只能创建一个实例的类。
- 访问类。使用单例类。
分类
- 饿汉式:类加载就会导致单实例对象被创建
- 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
饿汉式
1、静态变量方式
/*
饿汉式
静态变量创建类的对象
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 成员位置创建该类的对象
private static Singleton instance = new Singleton();
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return instance;
}
}
instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存浪费。
2、静态代码块方式
package DesignPattern.Single.demo2;
/*
饿汉式
静态代码块中创建类的对象
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 成员位置创建该类的对象
private static Singleton instance;
static {
instance = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return instance;
}
}
也存在内存浪费问题
3、枚举方式
枚举类实现单例模式是激励推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
public enum Singleton{
INSTANCE;
}
懒汉式
1、线程不安全
/*
懒汉式
线程不安全
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 声明Singleton类型的变量instance
private static Singleton instance;
// 对外提供访问方式
public static Singleton getInstance(){
// 判断instance是否为null,如果为null,说明还没有创建Singleton的对象
if(instance == null){
//如果有两个线程,线程1等待,线程2获取到CPU的执行权,也会进入到该判断
instance=new Singleton();
}
return instance;
}
}
2、线程安全
/*
懒汉式
线程安全
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 声明Singleton类型的变量instance
private static Singleton instance;
// 对外提供访问方式
public static synchronized Singleton getInstance(){
// 判断instance是否为null,如果为null,说明还没有创建Singleton的对象
if(instance == null){
instance=new Singleton();
}
return instance;
}
}
3、双重检查锁
/*
懒汉式
双重检查锁
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 声明Singleton类型的变量instance
private static Singleton instance;
// 对外提供访问方式
public static Singleton getInstance(){
// 第一次判断,如果instance的值不为null,不需要抢占锁,直接返回对象
if(instance == null){
synchronized (Singleton.class){
// 第二次判断
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
双重检查锁模式解决了单例、性能、线程安全问题,但在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
要解决双重检查锁模式带来空指针异常的问题,只需要使用volatile关键字,volatile关键字可以保证可见性和有序性
/*
懒汉式
双重检查方式
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 声明Singleton类型的变量instance
private static volatile Singleton instance;
// 对外提供访问方式
public static Singleton getInstance(){
// 第一次判断,如果instance的值不为null,不需要抢占锁,直接返回对象
if(instance == null){
synchronized (Singleton.class){
// 第二次判断
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
4、静态内部类方式
JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证只被实例化一次,并且严格保证实例化顺序
/*
懒汉式
静态内部类方式
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 定义一个静态内部类
private static class SingletonHolder{
// 在内部类中声明并初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
存在的问题
单例有可能被破坏,序列化和反射可以在单例中创建多个对象,枚举除外。
工厂方法
简单工厂模式
不是23种设计模式,比较像一种编程习惯
结构
1、抽象产品
2、具体产品
3、具体工厂
原来:
CoffeeStore–匹配咖啡种类
Coffee
AmericanCoffee
LatteCoffee
简单工厂:
CoffeeStore–调用工厂类
SimpleCoffeeFactory–匹配咖啡种类
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
}
}
Coffee
AmericanCoffee
LatteCoffee
优点:封装了创建对象的过程,可以通过参数直接获取对象,把对象的创建和业务逻辑层分开,避免修改客户代码,实现新产品直接修改工厂类,不需要在原代码种修改
缺点:增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”
扩展
静态工厂
把工厂类中创建对象的功能定义为静态的
public class SimpleCoffeeFactory {
public static Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffe;
}
}
好处是可以直接通过类名调用
工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。
目的是使创建对象和使用对象分离,并且客户端总是引用抽象工厂和抽象产品。
结构
1、抽象工厂
2、具体工厂
3、抽象产品
4、具体产品
CoffeeFactory<>
CoffeeStore
Coffee
AmericanCoffee
LatteCoffee
AmericanCoffeeFactory
LatteCoffeeFactory
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无需知道产品的具体创建过程
- 系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无需对原工厂进行任何修改
缺点:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度
抽象工厂模式
抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构
是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生成多个等级的产品
结构
1、抽象工厂
2、具体工厂
3、抽象产品
4、具体产品
如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。