设计模式图解记忆
单例模式
-
饿汉式
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() { // 多线程下加synchronized // 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; } }
-
静态内部类懒汉式
public class Java3y { private Java3y() { } // 使用内部类的方式来实现懒加载 private static class LazyHolder { // 创建单例对象 private static final Java3y INSTANCE = new Java3y(); } // 获取对象, 被调用时才进行初始化 // 初始化静态数据时,Java提供了的线程安全性保证。(所以不需要任何的同步) public static final Java3y getInstance() { return LazyHolder.INSTANCE; } }
代理模式
原有的对象需要额外的功能,想想动态代理这项技术!
要实现动态代理必须要有接口的,动态代理是基于接口来代理的(实现接口的所有方法),如果没有接口的话我们可以考虑cglib代理。
cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能!
所以说代理模式就是:当前对象不愿意干的,没法干的东西委托给别的对象来做,我只要做好本分的东西就好了!
-
静态代理
public interface Programmer { // 程序员每天都写代码 void coding(); } public class Java3y implements Programmer { @Override public void coding() { System.out.println("Java3y最新文章:......给女朋友讲解什么是代理模式......."); } } public class ProgrammerBigV implements Programmer { // 指定程序员大V要让谁发文章(先发文章、后点赞) private Java3y java3y ; public ProgrammerBigV(Java3y java3y) { this.java3y = java3y; } // 程序员大V点赞评论收藏转发 public void upvote() { System.out.println("程序员大V点赞评论收藏转发!"); } @Override public void coding() { // 让Java3y发文章 java3y.coding(); // 程序员大V点赞评论收藏转发! upvote(); } }
-
动态代理
public class Main { public static void main(String[] args1) { // Java3y请水军 Java3y java3y = new Java3y(); Programmer programmerWaterArmy = (Programmer) Proxy.newProxyInstance(java3y.getClass().getClassLoader(), java3y.getClass().getInterfaces(), (proxy, method, args) -> { // 如果是调用coding方法,那么水军就要点赞了 if (method.getName().equals("coding")) { method.invoke(java3y, args); System.out.println("我是水军,我来点赞了!"); } else { // 如果不是调用coding方法,那么调用原对象的方法 return method.invoke(java3y, args); } return null; }); // 每当Java3y写完文章,水军都会点赞 programmerWaterArmy.coding(); } }
包装模式(装饰器模式)
在IO用得最多的就是装饰模式了
其实无论是代理模式还是装饰模式。本质上我认为就是对原有对象增强的方式~
一般来说,实现对象增强有三种方式:
-
继 承
-
- 继承父类,子类扩展
-
装饰器模式
-
- 使用“包装”的方式来增强对象
-
代理模式
-
// 一个良好的设计是抽取成接口或者抽象类的
public interface Phone {
// 可以打电话
void call();
}
public class IphoneX implements Phone {
@Override
public void call() {
System.out.println("打电话给周围的人关注公众号Java3y");
}
}
// 装饰器,实现接口
public abstract class PhoneDecorate implements Phone {
// 以组合的方式来获取默认实现类
private Phone phone;
public PhoneDecorate(Phone phone) {
this.phone = phone;
}
@Override
public void call() {
phone.call();
}
}
// 这里继承的是MusicPhone装饰器类
public class GiveCurrentTimePhone extends PhoneDecorate {
public GiveCurrentTimePhone(Phone phone) {
super(phone);
}
// 自定义想要实现的功能:给出当前的时间
public void currentTime() {
System.out.println("当前的时间是:" + System.currentTimeMillis());
}
// 重写要增强的方法
@Override
public void call() {
super.call();
// 打完电话后通知一下当前时间
currentTime();
}
}
工厂模式
我们修改了具体的实现类,对客户端(调用方)而言是完全不用修改的。
如果我们使用new
的方式来创建对象的话,那么我们就说:new
出来的这个对象和当前客户端(调用方)耦合了!
- 也就是,当前客户端(调用方)依赖着这个
new
出来的对象!这就是解耦的好处!
-
工厂方法模式
-
静态工厂模式(最常用)
public class AnimalFactory { public static Dog createDog() { return new Dog(); } public static Cat createCat() { return new Cat(); } // 外界想要猫要狗,这里创建就好了 public static Animal createAnimal(String type) { if ("dog".equals(type)) { return new Dog(); } else if ("cat".equals(type)) { return new Cat(); } else { return null; } } }
-
抽象工厂模式
策略模式
定义一组算法,将每个算法都封装起来,并且使他们之间可以互换
Context叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
说白了,通过Person来调用更符合面向对象(屏蔽了直接对具体实现的访问)。
优点:
-
算法可以自由切换
-
- 改一下策略很方便
-
扩展性良好
-
- 增加一个策略,就多增加一个类就好了。
缺点:
-
策略类的数量增多
-
- 每一个策略都是一个类,复用的可能性很小、类数量增多
-
所有的策略类都需要对外暴露
-
- 上层模块必须知道有哪些策略,然后才能决定使用哪一个策略
应用:ThreadPoolExecutor线程池的拒绝策略RejectedExecutionHandler
门面(外观)模式
【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
《设计模式之禅》:
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
优点:
- 减少系统的相互依赖。使用门面模式,所有的依赖都是对门面对象的依赖,与子系统无关
- 提高了灵活性。不管子系统内部如何变化,只要不影响门面对象,任你自由活动。
缺点:
- 不符合开闭原则,对修改关闭,对扩展开放。比如我们上面的例子,如果有新电器要想要加入一次关闭的队伍中,只能在门面对象上修改
turnOffAll()
方法的代码。
模板方法模式
《设计模式之禅》:
定义一个操作中的算法框架,而将一些步骤延迟到子类中。使子类可以不改变一个算法的结构即可重定义该算法的某些步骤。
// 抽象模板类
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,今天来给大家分享我写的模板方法模式");
}
}
// 3y写文章
public static void main(String[] args) {
WriteArticle java3ywriteArticle = new Java3yWriteArticle();
java3ywriteArticle.writeAnCompleteArticle();
}
优点:
- 封装不变的部分,扩展可变的部分。把认为是不变的部分的算法封装到父类,可变部分的交由子类来实现!
- 提取公共部分的代码,行为由父类控制,子类实现!
缺点:
- 抽象类定义了部分抽象方法,这些抽象的方法由子类来实现,子类执行的结果影响了父类的结果(子类对父类产生了影响),会带来阅读代码的难度!
应用:acquire()
相当于模板方法,tryAcquire(arg)
相当于基本方法。
责任链模式
public interface Filter {
// 过滤
void doFilter(String data);
}
class FilterEgg implements Filter {
@Override
public void doFilter(String data) {
//doSomething
}
}
class FilterAoBing implements Filter {
@Override
public void doFilter(String data) {
//doSomething
}
}
class FilterBaiCai implements Filter {
@Override
public void doFilter(String data) {
//doSomething
}
}
class FilterJiTou implements Filter {
@Override
public void doFilter(String data) {
//doSomething
}
}
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
责任链模式主要有以下特点:
- 一个Handler接口,多个Handler处理类
- 多个Handler处理类串起来形成一条链
用责任链模式的好处就是分工明确,解耦,容易维护。
- 将多个条件判定分散到各个的处理类上,相对于
if else
耦合性相对较低。 - 增加一个具体的
Handler
处理类,不会影响到BaseHandler
的代码
责任链模式的缺点:
- 项目里边会有多个具体Handler类(因为每种处理都抽象为一个类,所以会有多个类)
- 不好调试,初看代码时不好阅读。(对外只是一个
doChain
方法,而里边由多个处理类来组成,还得看相应的调用顺序)
应用:JavaWeb的Filter