目录
1、创建型模式特点和分类
关注的问题:创建对象,将对象的创建和使用分离,这样的目的可以降低代码的藕合度的问题
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模型
2、单例模式(Singleton Pattern)
2.1、单例介绍
定义:
单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个访问他的全局访问点
2.2、单例类图:
单例中包含的角色:
单例类:包含一个实例且能自行创建这个实例类
2.3、单例模式特点:
2.3.1、单例类只有一个实例对象
2.3.2、单例对象必须由单例类自行创建
2.3.3、单例类堆外提供一个可访问该单例的全局访问点
2.4、代码演示
单例的实现有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;
}
}
对加锁做了一定优化,总体优缺点和上面一致
⑥双重检查
/**
* 双重检验
*/
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;
}
}
推荐使用
2.5、单例再探究
优点:
单例模式保证在内存中只存在一个实例,减少了内存的开销
避免了对资源的多种占用
单例设置全局访问点,可以优化和共享资源的访问
缺点:
单例模式一般没有接口,扩展困难,如果要进行扩展,就需要修改代码,违背了开闭原则
适用场景:
需要频繁创建的一些类,适用单例可以降低系统的内存压力,减少GC
某些对象实例占用资源比较多,或者实例耗时较长且经常使用的对象,采用单例形式
2.6、Java应用
2.6.1、spring中的单例
在spring中,Bean可以被定义两种模式:Singleton(单例)和Prototype(多例),Spring中模式采用的单例
按照业务场景划分:工厂模式有3种不同的实现:分别是简单工厂、工厂方法模式和抽象工厂模式
3、简单工厂模式
3.1、简单工厂介绍
简单工厂有一个具体的工厂类,可以生产不同的产品,属于创建性设计模式,
注意:简单工厂模式 不属于23种设计模式之列
3.2、简单工厂类图UML
简单工厂角色说明:
简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑,工厂类的创建产品类的方法可以被外界直接访问,创建所需的产品对象
抽象产品(Product):是简单工厂创建所有对象的父类,负责描述所有实例公有的公共接口
具体产品(ConcreteProduct):是简单工厂创建的具体对象
3.3、代码实现
简单工厂实现
/**
* 抽象产品,提供所有具体产品的共有的方法
*/
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();
3.4、使用步骤:
3.4.1、创建抽象产品类和定义具体产品的公共接口
3.4.2、创建具体产品类,定义生产的具体产品
3.4.3、创建工厂类,通过创建静态方法根据传入不同的参数从而创建不同的具体产品的实例
3.4.4、外界通过调用工厂类的静态方法,传入不同的参数创建不同的产品实例
优缺点
优点:
1、代码逻辑比较简单,工厂类包含必要的创建对象的逻辑,可以决定创建具体的产品
2、调用方无需指导所创建具体产品的类名,只需要知道参数即可
缺点:
简单工厂违背开闭原则,工厂类负责产品的创建职责过重,一旦新增产品需要对工厂类内部逻辑新增判断
系统扩展困难,产品过多会造成逻辑过于复杂
使用场景:
对于产品种类比较少的情况,可以考虑使用简单工厂,调用方只需要传入工厂类的参数,不需要关注如何创建的逻辑
4、工厂方法模式
4.1、工厂方法模式介绍
定义:工厂方法模式是对简单工厂模式的进一步的抽象画,好处是可以使系统不修改原来代码的情况下引进新的产品,即满足开闭原则
定义一个用于创建对象接口,让子类决定实例化哪一个类,使一个类的实例化延迟到子类中
4.2、工厂方法模式类图UML
工厂方法模式的主要角色:
抽象工厂(AbstractFactory):提供了创建产品的接口,调用方通过它访问具体工厂的工厂方法来创建产品
具体工厂(ConcreteFactory):实现了抽象工厂定义的方法,完成具体产品的创建
抽象产品(Product):定义了产品的规范,描述产品的主要特征和性能
具体产品(ConcreteProduct):实现了抽象产品的定义的方法,有具体工厂来创建产品,具体工厂和具体产品一一对应
4.3、代码实现
/**
* 抽象工厂:提供具体工厂的共有方法
*/
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、调用方调用具体的工厂类的方法,从而创建不同具体产品的实例
4.4、工厂模式再探究
解决问题:
解决了简单工厂类新增产品需要修改工厂类的方法逻辑问题,即为别开闭原则。
将具体的产品创建退出到工厂类的子类(具体工厂)此时工厂类不在负责所有产品的创建,而是给出具体工厂必须实现的接口,这样工厂方法在添加新的产品的时候就不需要修改工厂类的逻辑而是添加了新的工厂子类,符合开闭原则。
优点:
灵活性强,对于新产品的创建,只需要多写一个对应的工厂类
用户只需要指导工厂的名称就可以获得所要的产品,不需要指导产品具体创建过程
缺点:
类个个数容易过多,增加了复杂度
每一个工厂只能生产一种产品,次弊端可以使用抽象工厂模式解决
应用场景:
客户只关注创建产品的工厂名,不需要知道具体的产品名称
5、抽象工厂模式(Abstract Factory)
解决工厂方法模式的问题:一个具体工厂只能创建一类产品,而实际过程中一个工厂往往需要生成很多的产品,这种可以采用抽象工厂模式
5.1、抽象工厂模式解决
定义:
提供一个创建一系列相关或者互相依赖对象的接口,无序指定他们的具体的类,具体的工厂负责实现具体的产品的实例
使一个工厂可以额产生多个产品
5.2、抽象工厂类图UML
抽象工厂模式的主要角色:
抽象工厂(AbstractFactory):提供了创建产品的接口,包含了多个创建产品的接口方法,可以创建多个不同等级的产品
具体工厂(ConcreteFactory):实现了抽象工厂定义的方法,完成具体产品的创建
抽象产品(Product):定义了产品的规范,描述产品的主要特征和性能
具体产品(ConcreteProduct):实现了抽象产品的定义的方法,有具体工厂来创建产品,具体工厂和具体产品一对多关系
5.3、代码实现
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、调用方调用具体的工厂类的方法,从而创建不同具体产品的实例
5.4、抽象工厂方法再探究
解决问题
每个工厂只能创建一类产品,即是工厂方法的缺点
优点:
可以在工厂类内部对产品族中相关联的多级产品共同管理,而不必专门引入新的类来管理
抽象工厂增加了程序的可扩展性,当新增一个新的产品族时,不需要修改源代码,满足开闭原则
缺点:
当产品族新增一个产品类型时,所有的工厂类都需要进行修改,增加了系统的抽象性和理解难度
5.5、Java应用
在Spring框架中对简单工厂的使用
SPring中所有对象通过IOC容器进行管理,获取对象通过getBean
使用示例如下:
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext(path);
//在IOC容器获取需要的对象实例
Student student = (Student) applicationContext.getBean("student");
当前的ClassPathXmlApplicationContext是ApplicationContext容器的实现,而ApplicationContext是BeanFactory接口的子接口,BeanFactory接口是简单工厂模式的使用,通过其提供的getBean方法可以获取更多的对象
适用示例:
王者荣耀中提供了很多的英雄,英雄分为射手、辅助、打野、坦克等职业,不同的职业对应的技能,属性不同。
声明坦克、法师、刺客等多个工厂子类,每个工厂可以生产相应技能的应用
6、构建者模式(Builder)
在软件开发过程中有时需要创建一个复杂的对象,该复杂对象通常是有多个子部件按一定的顺序组合而成。
6.1、构建者介绍
介绍:
指将一个复杂的对象的构造和表示分离,使同样的构造过程可以创建不同的表示,
将一个复杂的对象分解为多个简单的对象,然后一步步构建而成
6.2、类图UML:
主要角色如下:
产品角色:它包含多个组成部件的复杂的对象,由具体具体构建者来创建各个部件
抽象建设者:它是一个包含产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法
具体建设者:实现builder接口,完成复杂对象的各个部件的具体创建方法
指挥者:它调用建造者对象的部件构造与装配方法完成复杂对象的创建
6.3、代码实现
/**
* 复杂的产品
*/
public class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
public void show() {
//展示产品特征
final StringBuilder sb = new StringBuilder("{");
sb.append("\"partA\":\"")
.append(partA).append('\"');
sb.append(",\"partB\":\"")
.append(partB).append('\"');
sb.append(",\"partC\":\"")
.append(partC).append('\"');
sb.append('}');
System.out.println( sb.toString());
}
}
/**
* 抽象建设者
*/
public abstract class Builder {
protected Product product = new Product();
public abstract void builderPartA();
public abstract void builderPartB();
public abstract void builderPartC();
//返回产品对象
public Product getResult(){
return product;
}
}
public class ConcreteBuilder extends Builder {
@Override
public void builderPartA() {
product.setPartA("建造部件A");
}
@Override
public void builderPartB() {
product.setPartB("建造部件B");
}
@Override
public void builderPartC() {
product.setPartC("建造部件C");
}
}
/**
* 指挥者
*/
public class Directer {
private Builder builder;
public Directer(Builder builder) {
this.builder = builder;
}
//产品的构建和组装方法
public Product construct() {
builder.builderPartA();
builder.builderPartB();
builder.builderPartC();
return builder.getResult();
}
public static void main(String[] args) {
//创建具体建设者实例
Builder builder = new ConcreteBuilder();
Directer directer = new Directer(builder);
Product construct = directer.construct();
construct.show();
}
}
6.4、构造者模式再研究
优点:
封装性好,建构和表示分离
扩展性好,各个具体的建造者相互独立,有利于系统解耦
缺点:
产品的组成部分必须相同,限制了使用范围
应用场景:
当需要创建的复杂的产品时,可以抽取出共性的创建过程,然后交由具体实现类来实现创建流程,使得同样的创建行为可以生产部不同的产品,分离了创建和表示,创建产品灵活性大大增强
6.5、Java中的应用
6.5.1、nettey中创建启动辅助类
网络编程中-网络通信框架:netty
/**
* Netty中提供了需求的不同的EventLoopGroup的实现用来处理不同的传输
* 在这里使用2个NioEventLoopGroup
* 一个叫做boss ,用来接收客户端的连接
* 一个叫做worker,用来处理客户端的连接
* boss一旦接收到连接,就会将连接信息交给worker来处理
*/
/**
* NioEventLoopGroup事件循环组中,每一个事件循环组中都是创建一个线程池,线程池中每一个线程
* 都会绑定一个selector实例。
* 使用是一般boss事件循环组可以指定一个线程来处理
*/
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup(5);
try {
/**
* 使用启动辅助类
* 服务端的启动辅助类ServerBootStrap
*/
ServerBootstrap bootstrap = new ServerBootstrap();
// bootstrap.bind().channel();
bootstrap
.group(boss,worker)
// attr 向内部传递特定的业务数据
// .attr(AttributeKey.valueOf())
//根据特定的需求来配置ChannelOption的选项
//指定连接的超时时间
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 300)
// sendbuf
.option(ChannelOption.SO_SNDBUF, 1024)
//指定事件循环组
//指定boss事件循环组的channel类型
.channel(NioServerSocketChannel.class)
//指定worker事件循环组中的Channel信息,
//ChildXXX开头的配置信息都是给子事件循环组的配置
// .childAttr()
.childOption(ChannelOption.SO_SNDBUF, 1024)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
//添加ChannelHandler到ChannelPipeLine容器中
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
//获取ChannelPipeline的容器
ChannelPipeline pipeline = ch.pipeline();
//处理字符串数据,将字符串的编码解码作为ChannelHandler放入容器中
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
//添加自定义的实现
pipeline.addLast(new ServerHandler());
}
});
//同步阻塞启动服务端 bind操作来启动端口,sync()产生阻塞直至当前的bind操作完成时才结束
ChannelFuture sync = bootstrap.bind(8888).sync();
System.out.println("服务端启动了");
//同步阻塞等待服务端关闭
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
bootstrap.bind().channel()这个就是builder模式的体现
这样设置的目的在于可以自由选择设置相关的参数而不强求绑定,可以根据使用场景进行自由的设置参数
6.5.2、mybatis源码中使用构建者
在mybatis中创建SQLSessionFactory的实例是通过SQLSessionFactoryBuilder的对象调用build方法进行构建,这个采用构建者模式
XMLConfigBuilder负责创建复杂对象Configuration,这个是构建者模式的简化的构建构成。