目录
创建型模式特点
- 关注的问题:创建对象,将对象的创建和使用分离,这样的目的可以降低代码的耦合度
创建型模式分类
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模型
1、单例模式(Singleton Pattern)
单例介绍
定义:
保证一个类只有一个实例,并提供一个访问他的全局访问点
单例类图:
单例中包含的角色:
单例类:包含一个实例且能自行创建这个实例类
单例模式(Singleton Pattern)特点:
1、单例类唯一存在一个实例。
2、单类必须自己创建自己的唯一实例。
3、单例类必须给所有其他实例提供此实例。
代码演示
单例的实现有8种
饿汉式(静态常量)
饿汉式(静态代码块)
懒汉式(线程不安全)
懒汉式(加锁)
懒汉式(优化加锁)
懒汉式(双重检查)
静态内部类
枚举
①饿汉式(静态常量)
/**
* 饿汉式(静态变量)
* 线程安全的,在类记载到内存中后,由JVM保证线程安全
* 简单,推荐实现
*/
public class Singleton1 {
//构造函数私有化(防止new)
private Singleton1() {}
//本类内部创建对象,静态变量
private final static Singleton1 single = new Singleton1();
//提供一个公有的静态方法,返回实例对象,提供给外部使用
public static Singleton1 getInstance() {
return single;
}
}
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:写法简单
在类加载时已经完成类的实例化,保证线程安全
缺点:在类加载时完成了实例化(没有实现懒加载),如果没有使用这个实例,会造成内存的浪费
②饿汉式(静态代码块)
/**
* 饿汉式(静态代码块)
*/
public class Singleton2 {
//构造函数私有化
private Singleton2(){}
//类内部创建对象实例
private static Singleton2 single;
//在静态代码块中,创建单例对象
static {
single = new Singleton2();
}
//提供公有的访问点
public static Singleton2 getInstance(){
return single;
}
}
优缺点和饿汉式静态变量一样。
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:写法简单
在类加载时已经完成类的实例化,保证线程安全
缺点:在类加载时完成了实例化(没有实现懒加载),如果没有使用这个实例,会造成内存的浪费
③懒汉式(线程不安全)
/**
* 懒汉式(线程不安全)
*/
public class Singleton3 {
private static Singleton3 single;
private Singleton3 (){}
//提供一个静态的公有的访问,当调用时,才创建实例,即懒汉式
public static Singleton3 getInstance() {
if (single == null) {
single = new Singleton3();
}
return single;
}
}
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:容易实现
实现了懒加载的效果,但是只能在单线程使用
缺点:在多线程下,存在线程不安全
④懒汉式(线程安全,同步方法)
/**
* 懒汉式
*/
public class Singleton4 {
private static Singleton4 single;
private Singleton4 (){}
//提供一个静态的公有的访问,当调用时,才创建实例,即懒汉式
public static synchronized Singleton4 getInstance() {
if (single == null) {
single = new Singleton4();
}
return single;
}
}
线程安全的关键点:synchronized
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:容易实现
通过线程安全的关键字,解决线程安全问题
实现懒加载
缺点:效率低下,每个线程在获取实例的时候,执行getInstance都需要进行同步,方法的同步效率太低
⑤懒汉式(线程安全,同步代码块)
/**
* 懒汉式
*/
public class Singleton5 {
private static Singleton5 single;
private Singleton5() {
}
//提供一个静态的公有的访问,当调用时,才创建实例,即懒汉式
public static Singleton5 getInstance() {
if (single == null) {
synchronized (Singleton5.class) {
single = new Singleton5();
}
}
return single;
}
}
对加锁做了一定优化(锁的是代码块),总体优缺点和上面一致。
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:容易实现
实现懒加载
通过线程安全的关键字,解决线程安全问题
缺点:效率低下,每个线程在获取实例的时候,执行getInstance都需要进行同步,方法的同步效率太低,但是锁住的紧紧是那两代码,如果恰好友两个线程同时进入到了if判断语句中,那么其中一个会执行,另一个等待这个执行完毕之后去执行,那不就创建了两个实例了吗,这样就不能保证只有一个对象被创建。因此引入下面的双重检查
⑥双重检查
/**
* 双重检验
*/
public class Singleton6 {
private static Singleton6 single;
private Singleton6() {
}
//提供一个静态的公有的访问,当调用时,才创建实例,即懒汉式
public static Singleton6 getInstance() {
if (single == null) {
synchronized (Singleton6.class) {
if (single == null) {
single = new Singleton6();
}
}
}
return single;
}
}
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:实现懒加载
线程安全 ,双重检查进行了两次的if (single == null)检查,保证线程安全性,效率高
缺点:较难实现
推荐使用
⑦静态内部类
/**
* 静态内部类
* 静态内部属性 ,在类加载时进行初始化,保证线程安全
* 采用静态内部类实现延迟加载
*/
public class Singleton7 {
private Singleton7() {
}
//提供一个静态内部类,该类中有一个静态属性
private static class SingleHoler{
private static final Singleton7 instance = new Singleton7();
}
//提供一个静态的公有的访问,直接返回SingleHoler.instance
public static Singleton7 getInstance() {
return SingleHoler.instance;
}
}
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
线程安全:本质是通过类加载来保证线程安全
实现懒加载:只有在实际使用的时候,才会触发类的初始化,也是懒加载的一种形式
效率高:没有使用锁机制
推荐使用
⑧枚举方式
/**
* 枚举方式实现单例
*/
public enum Singleton8 {
INSTANCE;//属性
//提供一个静态的公有的访问,直接返回SingleHoler.instance
public static Singleton8 getInstance() {
return INSTANCE;
}
}
优缺点:(评判方面:是否实现懒加载,是否线程安全,实现难度)
优点:容易实现
线程安全
缺点:没有实现懒加载
推荐使用
单例再探究
优点:
单例模式保证在内存中只存在一个实例,减少了内存的开销
避免了对资源的多种占用
单例设置全局访问点,可以优化和共享资源的访问
缺点:
单例模式一般没有接口,扩展困难,如果要进行扩展,就需要修改代码,违背了开闭原则
适用场景:
需要频繁创建的一些类,适用单例可以降低系统的内存压力,减少GC
某些对象实例占用资源比较多,或者实例耗时较长且经常使用的对象,采用单例形式
Java应用
1、spring中的单例
在spring中,Bean可以被定义两种模式:Singleton(单例)和Prototype(多例),Spring中模式采用的单例
2、工厂模式
按照业务场景划分:工厂模式有3种不同的实现:分别是简单工厂、工厂方法模式和抽象工厂模式
简单工厂模式
简单工厂介绍
简单工厂有一个具体的工厂类,可以生产不同的产品,属于创建性设计模式。
注意:简单工厂模式 不属于23种设计模式之列
简单工厂类图UML:
简单工厂角色说明:
简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑,工厂类的创建产品类的方法可以被外界直接访问,创建所需的产品对象
抽象产品(Product):是简单工厂创建所有对象的父类,负责描述所有实例公有的公共接口
具体产品(ConcreteProduct):是简单工厂创建的具体对象
代码实现
简单工厂实现
/**
* 抽象产品,提供所有具体产品的共有的方法
*/
public interface Product {
public void show();
}
/**
* 具体产品1
*/
public class ConcreteProduct1 implements Product {
@Override
public void show() {
System.out.println("具体产品1展示...");
}
}
/**
* 具体产品2
*/
public class ConcreteProduct2 implements Product {
@Override
public void show() {
System.out.println("具体产品2展示。。。");
}
}
/**
* 简单工厂
*/
public class SimpleFactory {
//提供一个方法,基于不同的参数获取不同的实例对象
public static Product getConcreteProduct(int productType) {
switch (productType) {
case Const.Product1:
return new ConcreteProduct1();
case Const.Product2:
return new ConcreteProduct2();
case Const.Product3:
return new ConcreteProduct3();
}
return null;
}
final class Const {
static final int Product1 = 1;//表示是ConcreteProduct1产品
static final int Product2 = 2;//表示是ConcreteProduct2产品
static final int Product3 = 3;//表示是ConcreteProduct2产品
}
//调用方式:
//获取产品1
Product product = SimpleFactory.getConcreteProduct(Const.Product1);
product.show();
//获取产品2
Product product1 = SimpleFactory.getConcreteProduct(Const.Product2);
product1.show();
使用步骤
1、创建抽象产品类和定义具体产品的公共接口
2、创建具体产品类,定义生产的具体产品
3、创建工厂类,通过创建静态方法根据传入不同的参数从而创建不同的具体产品的实例
4、外界通过调用工厂类的静态方法,传入不同的参数创建不同的产品实例
优缺点
优点:
1、代码逻辑比较简单,工厂类包含必要的创建对象的逻辑,可以决定创建具体的产品
2、调用方无需知道所创建具体产品的类名,只需要知道参数即可
缺点:
简单工厂违背开闭原则,工厂类负责产品的创建职责过重,一旦新增产品需要对工厂类内部逻辑新增判断
系统扩展困难,产品过多会造成逻辑过于复杂
使用场景
对于产品种类比较少的情况,可以考虑使用简单工厂,调用方只需要传入工厂类的参数,不需要关注如何创建的逻辑
工厂方法模式
工厂方法模式介绍
定义:工厂方法模式是对简单工厂模式的进一步的抽象化,好处是可以使系统不修改原来代码的情况下引进新的产品,即满足开闭原则
定义一个用于创建对象接口,让子类决定实例化哪一个类,使一个类的实例化延迟到子类中
工厂方法模式类图UML:
工厂方法模式的主要角色:
抽象工厂(AbstractFactory):提供了创建产品的接口,调用方通过它访问具体工厂的工厂方法来创建产品
具体工厂(ConcreteFactory):实现了抽象工厂定义的方法,完成具体产品的创建
抽象产品(Product):定义了产品的规范,描述产品的主要特征和性能
具体产品(ConcreteProduct):实现了抽象产品的定义的方法,有具体工厂来创建产品,具体工厂和具体产品一一对应
代码实现
/**
* 抽象工厂:提供具体工厂的共有方法
*/
public interface Product {
public void show();
}
public class ConcreteProduct1 implements Product {
@Override
public void show() {
System.out.println("具体产品1展示。。。");
}
}
public class ConcreateProduct2 implements Product {
@Override
public void show() {
System.out.println("具体产品展示...");
}
}
/**
* 抽象工厂
*/
public interface AbstractFactory {
public Product createProduct();
}
/**
* 具体工厂1,产生具体产品1
*/
public class ConcreteFactory1 implements AbstractFactory {
@Override
public Product createProduct() {
ConcreteProduct1 concreteProduct1 = new ConcreteProduct1();
System.out.println("具体工厂1创建具体产品1...");
return concreteProduct1;
}
public class ConcreteFactory2 implements AbstractFactory {
@Override
public Product createProduct() {
System.out.println("具体工厂2产生具体产品2");
return new ConcreateProduct2();
}
}
//调用:
//获取具体产品1
Product product = new ConcreteFactory1().createProduct();
product.show();
Product product1 = new ConcreteFactory2().createProduct();
product1.show();
使用步骤
1、创建抽象产品类,定义产品的公共方法
2、创建具体产品类(实现抽象产品接口),定义生成的具体产品
3、创建抽象工厂类,定义具体工厂的公共接口
4、创建具体工厂类,定义创建对应具体产品实例的方法
5、调用方调用具体的工厂类的方法,从而创建不同具体产品的实例
工厂模式再探究
解决问题:
解决了简单工厂类新增产品需要修改工厂类的方法逻辑问题,即不满足开闭原则。
将具体的产品创建退出到工厂类的子类(具体工厂)此时工厂类不在负责所有产品的创建,而是给出具体工厂必须实现的接口,这样工厂方法在添加新的产品的时候就不需要修改工厂类的逻辑而是添加了新的工厂子类,符合开闭原则
优缺点
优点:
灵活性强,对于新产品的创建,只需要多写一个对应的工厂类
用户只需要指导工厂的名称就可以获得所要的产品,不需要指导产品具体创建过程
缺点:
类个个数容易过多,增加了复杂度
每一个工厂只能生产一种产品,此弊端可以使用抽象工厂模式解决
应用场景
客户只关注创建产品的工厂名,不需要知道具体的产品名称
抽象工厂模式(Abstract Factory)
解决工厂方法模式的问题:一个具体工厂只能创建一类产品,而实际过程中一个工厂往往需要生成很多的产品,这种可以采用抽象工厂模式
抽象工厂模式解决
定义:
提供一个创建一系列相关或者互相依赖对象的接口,无序指定他们的具体的类,具体的工厂负责实现具体的产品的实例
使一个工厂可以额产生多个产品
抽象工厂类图UML:
抽象工厂模式的主要角色:
抽象工厂(AbstractFactory):提供了创建产品的接口,包含了多个创建产品的接口方法,可以创建多个不同等级的产品
具体工厂(ConcreteFactory):实现了抽象工厂定义的方法,完成具体产品的创建
抽象产品(Product):定义了产品的规范,描述产品的主要特征和性能
具体产品(ConcreteProduct):实现了抽象产品的定义的方法,有具体工厂来创建产品,具体工厂和具体产品一对多关系
代码实现
public interface AbstractFactory {
public Product1 createProduct1();
public Product2 createProduct2();
}
public class ConcreteFactory1 implements AbstractFactory {
@Override
public Product1 createProduct1() {
System.out.println("具体工厂1产生具体产品2.。。");
return new ConcreteProduct12();
}
@Override
public Product2 createProduct2() {
System.out.println("具体工厂1产生具体产品3.。。");
return new ConcreteProduct21();
}
}
使用步骤:
1、创建抽象产品类,定义产品的公共方法
2、创建具体产品类(实现抽象产品接口),定义生成的具体产品
3、创建抽象工厂类,定义具体工厂的公共接口,一个工厂类可以创建不同等级的产品
4、创建具体工厂类,定义创建对应具体产品实例的方法
5、调用方调用具体的工厂类的方法,从而创建不同具体产品的实例
抽象工厂方法再探究
优缺点
优点:
可以在工厂类内部对产品族中相关联的多级产品共同管理,而不必专门引入新的类来管理
抽象工厂增加了程序的可扩展性,当新增一个新的产品族时,不需要修改源代码,满足开闭原则
缺点:
当产品族新增一个产品类型时,所有的工厂类都需要进行修改,增加了系统的抽象性和理解难度
Java应用
在Spring框架中对简单工厂的使用
SPring中所有对象通过IOC容器进行管理,获取对象通过getBean
使用示例如下:
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext(path);
//在IOC容器获取需要的对象实例
Student student = (Student) applicationContext.getBean("student");
当前的ClassPathXmlApplicationContext是ApplicationContext容器的实现,而ApplicationContext是BeanFactory接口的子接口,BeanFactory接口是简单工厂模式的使用,通过其提供的getBean方法可以获取更多的对象。