1、代理模式
- 代理对象拥有目标对象相同的方法【因为参数二指定了对象的接口,代理对象会实现接口的所有方法】
- 用户调用代理对象的什么方法,都是在调用处理器的invoke方法。【被拦截】
- 使用JDK动态代理必须要有接口【参数二需要接口】
所有通过动态代理实现的方法全部通过invoke()
调用
使用静态代理时:
- 如果目标对象的接口有很多方法的话,那我们还是得一一实现,这样就会比较麻烦
使用动态代理时:
- 代理对象的生成,是利用JDKAPI,动态地在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型),并且会默认实现接口的全部方法。
要实现动态代理必须要有接口的,动态代理是基于接口来代理的(实现接口的所有方法),如果没有接口的话我们可以考虑cglib代理。
cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能!
2、装饰模式
一般来说,实现对象增强有三种方式:
-
继 承
-
- 继承父类,子类扩展
-
装饰器模式
-
- 使用“包装”的方式来增强对象
-
代理模式
继承
最简单的方式就是继承父类,子类扩展来达到目的。虽然简单,但是这种方式的缺陷非常大:
- 一、如果父类是带有数据、信息、属性的话,那么子类无法增强。
- 二、子类实现了之后需求无法变更,增强的内容是固定的。
装饰器模式
- 第一步:我们有一个Phone接口,该接口定义了Phone的功能
- 第二步:我们有一个最简单的实现类iPhoneX
- 第三步:写一个装饰器抽象类PhoneDecorate,以组合(构造函数传递)的方式接收我们最简单的实现类iPhoneX。其实装饰器抽象类的作用就是代理(核心的功能还是由最简单的实现类iPhoneX来做,只不过在扩展的时候可以添加一些没有的功能而已)。
- 第四步:想要扩展什么功能,就继承PhoneDecorate装饰器抽象类,将想要增强的对象(最简单的实现类iPhoneX或者已经被增强过的对象)传进去,完成我们的扩展!
3、单例模式
单例模式定义很简单:一个类中能创建一个实例,所以称之为单例!
学过Java Web的同学可能就知道:
- Servlet是单例的
- Struts2是多例的
- SpringMVC是单例的
编写单例模式的代码其实很简单,就分了三步:
- 将构造函数私有化
- 在类的内部创建实例
- 提供获取唯一实例的方法
饿汉式
public class Java3y {
// 1.将构造函数私有化,不可以通过new的方式来创建对象
private Java3y(){}
// 2.在类的内部创建自行实例
private static Java3y java3y = new Java3y();
// 3.提供获取唯一实例的方法
public static Student getJava3y() {
return java3y;
}
}
- 一上来就创建对象了,如果该实例从始至终都没被使用过,则会造成内存浪费。
简单懒汉式
既然说一上来就创建对象,如果没有用过会造成内存浪费:
- 那么我们就设计用到的时候再创建对象!
public class Java3y {
// 1.将构造函数私有化,不可以通过new的方式来创建对象
private Java3y(){}
// 2.1先不创建对象,等用到的时候再创建
private static Java3y java3y = null;
// 2.1调用到这个方法了,证明是要被用到的了
public static Java3y getJava3y() {
// 3. 如果这个对象引用为null,我们就创建并返回出去
if (java3y == null) {
java3y = new Java3y();
}
return java3y;
}
}
双重检测机制(DCL)懒汉式
public class Java3y {
private Java3y() {
}
private static volatile Java3y java3y = null;
public static Java3y getJava3y() {
if (java3y == null) {
// 将锁的范围缩小,提高性能
synchronized (Java3y.class) {
// 再判断一次是否为null
if (java3y == null) {
java3y = new Java3y();
}
}
}
return java3y;
}
}
静态内部类懒汉式
- 当任何一个线程第一次调用
getInstance()
时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。(被调用时才进行初始化!) - 初始化静态数据时,Java提供了的线程安全性保证。(所以不需要任何的同步)
public class Java3y {
private Java3y() {
}
// 使用内部类的方式来实现懒加载
private static class LazyHolder {
// 创建单例对象
private static final Java3y INSTANCE = new Java3y();
}
// 获取对象
public static final Java3y getInstance() {
return LazyHolder.INSTANCE;
}
}
总的来说单例模式写法有5种:
- 饿汉式
- 简单懒汉式(在方法加锁)
- DCL双重检测加锁(进阶懒汉式)
- 静态内部类实现懒汉式(最推荐写法)
- 枚举方式(最安全、简洁写法)
4、工厂模式
厂模式分成三种:
- 简单/静态工厂模式
- 工厂方法模式
- 抽象工厂模式
5、策略模式
定义一组算法,将每个算法都封装起来,并且使他们之间可以互换
优点:
-
算法可以自由切换
-
- 改一下策略很方便
-
扩展性良好
-
- 增加一个策略,就多增加一个类就好了。
缺点:
-
策略类的数量增多
-
- 每一个策略都是一个类,复用的可能性很小、类数量增多
-
所有的策略类都需要对外暴露
-
- 上层模块必须知道有哪些策略,然后才能决定使用哪一个策略
应用
学习ThreadPoolExecutor(线程池)就肯定要知道它的构造方法每个参数的意义:
RejectedExecutionHandler handler
其中我们可以找到RejectedExecutionHandler,这个参数代表的是拒绝策略(有四种具体的实现:直接抛出异常、使用调用者的线程来处理、直接丢掉这个任务、丢掉最老的任务)
6、门面模式
【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
无论是空调、冰箱、电视、电脑、风扇等等,只要是电器都受这个电闸控制。只要这个电闸将关闭,所有的电器都会受到牵连(一同关闭)。
电源总开关(电闸)即为该系统的外观模式设计。
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
按照我们的例子,子系统就相当于电脑、冰箱、电视。统一的对象就相当于我们的电闸。我们通过电闸来对所有电器进行关闭(使得不用逐个逐个找电脑、冰箱、电视来关闭)
使用了门面模式,使客户端调用变得更加简单!
7、模板方法模式
要点:
- 把公共的代码抽取出来,如果该功能是不确定的,那我们将其修饰成抽象方法。
- 将几个固定步骤的功能封装到一个方法中,对外暴露这个方法,就可以非常方便调用了。
// 抽象模板类
public abstract class WriteArticle {
// 基本方法
protected abstract void introduction();
// 基本方法
protected abstract void theLast();
// 基本方法
protected abstract void actualContent();
// 模板方法
public final void writeAnCompleteArticle() {
introduction();
actualContent();
theLast();
}
}
// 具体模板类
public class Java3yWriteArticle extends WriteArticle {
// 实现基本方法
@Override
public void introduction() {
System.out.println("只有充钱才能变强");
}
// 实现基本方法
@Override
public void theLast() {
System.out.println("关注我的公众号:Java3y");
}
// 实现基本方法
@Override
protected void actualContent() {
System.out.println("大家好,我是3y,今天来给大家分享我写的模板方法模式");
}
}
-
基本方法:在子类实现,并且在模板方法中被调用
-
模板方法:定义了一个框架,实现对基本方法的调用,完成固定的逻辑。
-
定义一个操作中的算法框架,而将一些步骤延迟到子类中。
-
- WriteArticle中有一个writeAnCompleteArticle()方法,该方法定义了发文章的所有步骤,但是这些步骤大多是抽象的,得由子类来实现。
-
使子类可以不改变一个算法的结构即可重定义该算法的某些步骤
-
- 外界是通过调用writeAnCompleteArticle()方法来写文章的,子类如果改变具体的实现就会间接改变了算法的细节。
8、责任链模式
public class FilterChain {
List<Filter> filters = new ArrayList<>();
public FilterChain() {
filters.add(new FilterEgg());
filters.add(new FilterAoBing());
filters.add(new FilterBaiCai());
filters.add(new FilterJiTou());
}
public void processData(String data) {
for (Filter filter : filters) {
filter.doFilter(data);
}
}
}
public class Handler {
public void handlerRequest(Request request) {
// 得到请求的数据
String data = request.getData();
FilterChain filterChain = new FilterChain();
// 处理数据
filterChain.processData(data);
}
}
- 将处理的各个流程抽象为各个类(本来Handler里边有多个
if
和方法
) - 将多个类用
Chain
链起来,暴露一个方法给Handler使用 - done
9、建造者模式
那怎么实现建造者模式呢?其实也非常简单:
- 在domain类上创建一个静态内部类 Builder,Builder拥有domain所有的属性
- 在domain类上创建一个
private
的构造函数,参数为Builder类型,里边将Builder的属性赋值给domain的属性 - 在Builder内部类创建domain属性的赋值方法,返回值是Builder
- Builder内部类创建一个
build
方法,返回domain实例
public class MessageTask {
private String taskId;
private String content;
private String messageId;
private String taskName;
private MessageTask(Builder builder) {
this.taskId = builder.taskId;
this.content = builder.content;
this.messageId = builder.messageId;
this.taskName = builder.taskName;
}
public static class Builder{
private String taskId;
private String content;
private String messageId;
private String taskName;
public Builder setTaskId(String taskId) {
this.taskId = taskId;
return this;
}
public Builder setContent(String content) {
this.content = content;
return this;
}
public Builder setMessageId(String messageId) {
this.messageId = messageId;
return this;
}
public Builder setTaskName(String taskName) {
this.taskName = taskName;
return this;
}
// 创建build方法,返回实例
public MessageTask build() {
return new MessageTask(this);
}
}
}
使用方式:先创建Builder对象,然后用Builder去赋值,最后再调用build()
返回真正的实例:
MessageTask.Builder builder = new MessageTask.Builder();
MessageTask task = builder.setContent("关注 Java3y 吧 >>")
.setTaskId("3y")
.setTaskName("一起来玩")
.setMessageId(String.valueOf(ThreadLocalRandom.current().nextLong()))
.build();
我们如果使用了Lombok
后,在类上加上一个注解@Builder
就可以使用建造者模式的代码了
@Builder
@Data
public class MessageTask {
private String taskId;
private String content;
private String messageId;
private String taskName;
}