Java设计模式(23种)

视频
马士兵更加精简一点

随看随记

七大设计原则

  • 编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序(软件),具有更好
  1. 代码重用性(即:相同功能的代码,不用多次编写
  2. 可读性(即:编程规范性,便于其他程序员的阅读和理解
  3. 可扩展性(即:当需要增加新的功能时,非常的方便,称为可维护
  4. 可靠性(即:当我们增加新的功能后,对原来的功能没有影响
  5. 使程序呈现高内聚,低耦合的特性

单一职责原则

  • 我认为是使用功能的原子性
  • 每个类只用一个职责,类似于数据库的第一范式,类仅有原子的作用。
  • 当一个类有多个职责是,将这个类的功能进行拆分,或者对类中的方法进行分类修改。
  • 主要还是为了解耦。
  1. 降低类的复杂度,一个类只负责一项职责。
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,我们应当遵守单一职责原则, 只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

接口隔离原则

  • 我认为是实现功能的原子性
  • 我认为是要实现接口的原子性,一个接口仅仅实现自己需要的原则的一部分。通过一个类实现多个接口,来实现一个类的多种功能。
  • 接口的功能不需要很大,否则会导致功能冗余。
  • 一个类依赖(调用)了一个接口,这个接口应该是仅仅具有这个类的职责,而不应该有多余的职责。
  • 一个类对另一个类的依赖应该建立在最小接口之上。
  • A依赖B实现的接口,C依赖D实现的接口。A仅仅需要123方法,C需要145方法,如果按下面的这个图实现的话,则会导致B实现了4,5无用方法,D实现了2、3无用方法。Java设计模式(23种)_第1张图片
  • 应该修改成这样,接口根据需要拆分成三个接口,B实现1、2接口,D实现1、3接口。这样A调用B时,不会有多余的4、5方法。C调用D时不会有多余的2、3方法。
  • Java设计模式(23种)_第2张图片

依赖倒转原则

  • 依赖指的是方法的输入参数。
  • 我认为是实现了输入与方法实现的解耦
  • 通过接口实现的方式,来实现的。
  • 依赖倒转原则(Dependence Inversion Principle)是指:
    1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象
    2. 抽象不应该依赖细节,细节应该依赖抽象,细节指的是实际的对象,抽象指的是接口等规范。
    3. 依赖倒转(倒置)的中心思想是面向接口编程
    4. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中, 抽象指的是接口或抽象类,细节就是具体的实现类
    5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

依赖倒转的反例

  • Java设计模式(23种)_第3张图片
  • 仅仅能接收Email,不能接收weixin等,需要有一个接口来解耦对特定类的依赖。这里的IReceiver表示所有的能够接收信息的对象要实现的接口。
  • Java设计模式(23种)_第4张图片

依赖倒转的的实现方式有三种

  • 在下面三种方式种,IOpenAndClose都是对象相应的需要实现的功能,对于方法执行对象都要接收一个统一的接口ITV,处理的重点在于如何将这个实现了接口(ITV)的对象传入我们要执行或调用的方法。
  • 接口传递:通过方法的输入接收接口对象。
  • Java设计模式(23种)_第5张图片
  • Java设计模式(23种)_第6张图片
  • 构造方法传递:
  • 通过构造函数传进来的接口对象的方法。
  • Java设计模式(23种)_第7张图片
  • 在这里插入图片描述
  • setter方式传递(SpringIOC):
  • 通过setter()的方式接收接口对象。
    Java设计模式(23种)_第8张图片
    在这里插入图片描述

里氏替换原则

  • 父子方法功能要相同
  • 子类和父类在方法的功能上完全相同时,才能使用里氏替换原则。强调的是父类与子类相同方法之间的透明性,是一种面向对象继承的一种原则。
  • 在子类种尽量不要重写父类已实现的方法。
  • 通常的做法是将这两个类都继承一个更大的基类baseClass,将原来的继承关系区掉,使用依赖,聚合,组合等方法来解决问题。

里氏替换原则反例

  • 因为B继承了A,同时也继承了A方法,所以我们会理所当然的认为,fun1是实现的同一个功能,这样在我们使用的时候会误把B的fun1,也当作A的fun1方法,导致了问题。
  • Java设计模式(23种)_第9张图片

解决方案

  • 将B进行升级,不再成为A的子类,而是和A平级,这样就不会有人认为A和B的fun1方法是一个方法。如果我们依然想使用A类的方法,就可以使用聚合的方式,通过调用a.fun1()来实现,或者使用依赖fun2(A a)的方式来解耦。
  • Java设计模式(23种)_第10张图片

开闭原则

  • p16 ,自己看吧, 不太好描述。
  • 对提供方扩展开放,对使用方修改关闭,即当要实现功能拓展的时候,需要尽量不要让使用方感受到修改的变化。
  • 尽量通过拓展的方式而不是修改的方式。
  • 视频中的解决方式是抽象出了一个公共抽象方法,来解决了这个问题。

迪米特法则

  • p19 ,自己看吧, 不太好描述。
  • 降低类与类之间的耦合性,我感觉就是自己的类的事情自己做,不要放到其他的类中去做。目的是提高内聚,减少耦合。
    1. 一个对 象应该对其他对象保持最少的了解
    2. 类与类关系越密切,耦合度越大
    3. 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息
    4. 迪米特法则还有个更简单的定义:只与直接的朋友通信
    5. 直接的朋友: 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。

合成复用原则

  • 尽量使用合成/聚合的方式,而不是使用继承的方式。
  • 当A与B无关时,B仅仅想使用A的方法,这样就会导致,当对A进行扩展时,B不想要扩展,但是也会被动扩展。这不是我们想要的,因此使用依赖、组合、聚合的方式来进行解决。
  • Java设计模式(23种)_第11张图片
  • 依赖(方法传输参数)
  • Java设计模式(23种)_第12张图片
  • 聚合(通过setter注入属性的方式)
  • Java设计模式(23种)_第13张图片
  • 组合(通过构造函数注入的方式)
  • Java设计模式(23种)_第14张图片

七大原则的核心思想

  • 解耦:尽量减少不同类之间的侵入关系,尽量做到一个类修改不影响其他类。
  • 复用:将共同有的方法拆解出来,不需要变化的代码混在一起。
  • 抽象:要面向接口编程,对能够实现接口规范的都可以,而不要面向实现编程,这样就会导致仅仅针对某一个实例,而不是针对一批实例,导致服复用性差。(控制反转的主要思想)

UML类图

  • 依赖:方法调用

  • Java设计模式(23种)_第15张图片

  • 继承:extend

  • 实现:implement

  • 组合:属性和对象创建是不可分离的,构造方法注入属性(组装乐高)

  • 聚合:属性和对象创建是可以分离的,set注入属性(一把筷子)

Java设计模式(23种)_第16张图片

设计模式分类

  • 创建型模式:单例、抽象工厂、原型、建造者、工厂模式。
  • 结构型模式:适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式、代理模式。
  • 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式。

创建型模式

  • 以产生什么样的对象为重点

单例模式(SpringIOC Singleton)

  • 很多对象仅仅需要一个就可以,并不需要频繁的创建销毁,这样可以节省内存和释放JVM资源
  • 例如我们仅仅需要一个factory对象就可以了
  • 如果private static Singeton instance 在懒汉式中不可以使用final,因为final后面必须紧跟着初始化。
  • 单例模式创建对象的步骤:
  1. 私有化构造函数
  2. 类内部创建对象
  3. 提供一个共有静态方法,对外返回实例对象。

饿汉式 - 静态变量

  • 静态的作用是在类加载的时候直接进行处理。
  • 静态方法使用的变量必须是静态变量。
class Singeton{
	private Singeton(){
		//......
	}
	private final static Singeton instance = new Singeton();
	public static Singeton getInstance(){
		return instance;
	}
}

饿汉式 - 静态代码块

class Singeton{
	private Singeton(){
		//......
	}
	private static Singeton instance;
	static{
		instance = new Singeton();
	}
	public static Singeton getInstance(){
		return instance;
	}
}

懒汉式 - 线程不安全

class Singeton{
	private Singeton(){
		//......
	}
	private static Singeton instance;

	public static Singeton getInstance(){
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

懒汉式 - 同步类

class Singeton{
	private Singeton(){
		//......
	}
	private static Singeton instance;

	public static synchronized Singeton getInstance(){
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

懒汉式 - 同步代码块 DCL(Double Check Lock)

class Singeton{
	private Singeton(){
		//......
	}
	private static volatile Singeton instance;

	public static Singeton getInstance(){
		if(instance == null) {
			synchronized(Singeton.class){
				if(instance == null) {
					instance = new Singleton();
				}
				return instance;
			}
		}
	}
}

懒汉式 - 静态内部类

  • 当外部类被装载的时候,内部类不会被装载。因此是懒汉式。
  • 因为也是通过类装载的方式解决的,因此是线程安全的。
class Singeton{
	private Singeton(){
		//......
	}
	private static class SingletonInstance {
		private static final Singeton INSTANCE = new Singeton();
	}

	public static Singeton getInstance(){
		return SingletonInstance.INSTANCE;
	}
}

懒汉式 - 枚举

  • 枚举默认不会被序列化,构造方法默认为私有且无法通过反射的方式变成公有。
  • 枚举类中的每一个枚举项都是一个常量对象,
enum Singeton{
	INSTANCE;
	// 当没有写构造函数时,默认是下面的私有构造方法,去构造静态变量。
	//private Singleton(){}
	
	public void fun(){
		//.......
	}
}

反射

  • 反射能够破坏单例的原因时可以拿到私有的构造方法,通过修改构造方法权限的方式,破坏单例。
  • 饿汉式可以通过在构造函数中判断是否为空来,阻止反射的破坏。
private Singleton(){
		if(singleton != null)	
			throw new RuntimeException("不允许反射访问");
	}
  • 但是DCL不可以通过标识位的方式,因为有可能通过反射构造方法而不会通过getInstance()方法。
private Singleton(){
	boolean flag = false;
	private Singleton(){
		if(singleton != null)	
			throw new RuntimeException("不允许反射访问");
		flag = true;
	}

序列化

  • 在反序列化的情况下,会自动创建一个新的对象,而不会使用原来的对象。
  • 可以通过增加ReadResolve方法来避免创建新的对象。
	public Object readResolve() throws ObjectStreamException {
		return singleton;
	}

枚举类不会被影响

  • 枚举类是不会被反射。
  • 枚举类在序列化时不会序列化枚举类对象,仅仅会序列化枚举类的对象的名字(INSTANCE这个字符串)。

工厂模式(BeanFactory)

  • 目的在于不需要知晓具体构造情况。
  • 我认为原型模式和工厂模式都是为了获取大批量对象,原型模式是在有已知对象的情况下,通过clone()方式从而不需要知道对象的实际情况。
  • 工厂模式是在将工厂参数设计好以后,不需要知道对象是如何被创建的。
  • 目的在于将对象的获取与对象的使用进行分离。
  • 使调用者不需要关注对象是如何创建的。
  • 任何可以得到对象的方法或者类都可以成为工厂。
  • 目的在于获得一批对象,同时不用去关注对象创建的细节。
  • 单例也是一种工厂。

简单工厂

  • Java设计模式(23种)_第17张图片
  • 一个能够得到创建对象的方式,就是简单工厂。

静态工厂

  • 在这个类中通过静态的方法来创建对象的方法叫做静态工厂。

工厂方法

  • 在产品的维度上扩展方便,只要实现了Factory的所有的工厂,都有了create()方法,因此子实现的所有的方法都会能创建对象,因此Factory就称为工厂方法模式。
  • Java设计模式(23种)_第18张图片
  • 在产品维度上扩展方便,例如可以扩展Car,Plane,Broom,Tank等。
  • 只要实现了Factory就可以了。

抽象工厂

  • 但是当Factory中不仅仅有一个方法的时候,这些方法所组成的一系列创造的对象称为产品族。
  • 例如下面的图中一个抽象工厂中有武器,食物和交通工具三种。
  • Java设计模式(23种)_第19张图片
  • 当继承的时候,继承的对象就需要实现所有的方法,例如ModernFactory就会创建AK47、面包、汽车。MagicFactory就会创建魔法杖、红蘑菇、扫帚。
  • 但是ModernFactory中不能创造魔法棒,MagicFactory不能创造AK47。
  • 即不同簇之间创造的对象是不同的,具有隔离性的。
  • 因此想要扩展一个产品族时,就需要抽象工厂了。
  • 但是如果仅仅扩展一个同类型的工厂,仅仅需要工厂方法就可以了。
  • 在QQ的换肤操作类似,仅仅切换另一个实现工厂就可以实现一键换肤。
  • 产品族,相当于这一族产品的所有的动作都是一系列的固定的,不同的产品族之间是不会交叉的。
  • 抽象工厂(BeanFactory)隔离不同的产品族,因为每个bean对象都要求走bean的生命周期。
  • 工厂方法(FactoryBean)只能生产一个产品,我们想要的bean的工厂。
  • Java设计模式(23种)_第20张图片
  • 当使用魔法世界的东西的时候,直接new一个magicFactory就可以了。

原型模式(克隆羊)(bean prototype)

  • 强调在不知晓对象细节的情况下,依旧得到对象的同类,与new一个对象的本质区别。
  • 我感觉这个视频没说到点子上,原型模式的目的在于在不知晓对象具体构造的情况下,能够创造出对象一样的个体
  • String类型不需要深拷贝,以为Java有常量池,直接到缓冲池中拿对应的字符串就行了,但是如果String被封装到一个对象(包括new一个String对象)中就需要进行深拷贝了,因为拷贝前后的对象是指向同一个属性对象的。
  • 在这里插入图片描述
  • p1的Location 和p2的location不是一个location,但是两个location指向的对象是一个对象。
  • 因为在调用clone()的情况时,并不需要像new一样输出各种构造参数,而是仅仅调用clone()方法就可以了。
  • clone()不是重点,clone()仅仅是JDK提供的方便我们利用原型模式的一种方法,和动态代理是一样的。
  • Java设计模式(23种)_第21张图片
  • 在使用clone()时,必须要实现cloneable接口才可以。如果使用浅拷贝则直接使用JDK自带的就可以,如果使用深拷贝则需要进一步处理。

浅拷贝和深拷贝

  • JDK中默认的clone()方法是浅拷贝,仅仅会拷贝引用地址,而不会拷贝对象属性。如果需要对象的对象属性也需要拷贝,则需要进行处理。
  • Java设计模式(23种)_第22张图片

clone方式

  • 把对象属性的对象也进行clone()处理,这样就可以实现对象属性的拷贝。
  • 这里的deepCloneableTarget是一个对象属性,因此在进行克隆的时候,把对象也进行克隆就可以了。
  • 但是这种方法在对象属性特别多的情况下,就会导致另外clone()的对象特别多,因此并不推荐这种写法。
  • Java设计模式(23种)_第23张图片

序列化方式

  • 通过序列化的方式进行创建对象。要实现Serializable接口。
public CloneTest deepClone() throws IOException{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        // 向内存的缓冲区写入二进制字节数组
        oos.writeObject(this);
        
		// 从指定的缓冲区拿到字节数组
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);

        CloneTest cloneTest = null;
        try {
        // 将字节数组转化成对象
            cloneTest = (CloneTest) ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally{
            ois.close();
            bis.close();
            oos.close();
            bos.close();
        }
        return  cloneTest;
    }
  • 其原理还是IO流序列化的readResolve()方法,会默认自动创造一个新的对象。

建造者模式(盖房子)(Mybatis sqlSessionBulider)

Java设计模式(23种)_第24张图片

  • 将建造过程进行解耦,接口或抽象类仅仅提供建造流程,流程的具体实施,由各自的实际建造者决定。
  • 建造者注重的是建造对象的特异性,工厂模式注重的是如何获得一批产品。
  • ==建造者的目的是通过我们的既定流程,使用具体的建造者得到一个具体的对象。外观模式是通过我们的既定方法,使用具体的对象得到一个实际的方法实现来调用。==外观模式不强调得到一个对象,而是强调既定方法独自实现。
  • 建造者模式强调的是,当建造一个对象有很多不同的过程要素,可以通过设置不同的参数,来成为不同的实际构造者(concreteBulider),通过不同的构造者来建造对象。
  • Java设计模式(23种)_第25张图片
  • Java设计模式(23种)_第26张图片
  • Product (产品角色,你要得到的类对象) : 一个具体的产品对象。
  • Builder (抽象建造者,你创建一个对象必须要有的流程,不会有具体的实现) :创建一个Product对象的各个部件指定的接口。
  • ConcreteBuilder (具体建造者,每个流程都有具体的实施) :实现接口, 构建和装配各个部件。
  • Director (指挥者,调用build方法,建造一个producet方法) :构建"一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

结构型模式

  • 让类与类之间可以协同工作

适配器模式(电压问题)(SpringMVC HandlerAdapter)

  • 强调的是两个类之间,如果不能调用,则需要增加一个缓冲层来辅助进行调用。
  • 重点在于适配器两端的解耦。
  • Java设计模式(23种)_第27张图片
  • Java设计模式(23种)_第28张图片

类适配器

  • Java设计模式(23种)_第29张图片
  • 通过继承类来实现对220v的适配
  • 由于是继承关系,因此增加了Adapter和Voltage220V之间的耦合性,因此在对象适配器中,就是通过聚合的方式来减少Voltage220V耦合性。
  • 其输出都是5v,重点在于实现了输出为5V的接口

对象适配器

  • Java设计模式(23种)_第30张图片

  • Java设计模式(23种)_第31张图片

  • 通过聚合的方式对220V进行适配。

接口适配器

  • 接口适配器更像定义一个规范,对于适配器的输出有这默认的n种,如果想要适配的输出在这n种,就直接重写就可以了,否则需要另加新的适配方式。
  • 对于接口适配器中抽象类内所有的方法都是缺省的,具体的实现看具体我们需要什么样的适配方法。
  • Java设计模式(23种)_第32张图片
  • Java设计模式(23种)_第33张图片

桥接模式(双维度扩展 JDBC)

  • Java设计模式(23种)_第34张图片

  • 桥接模式的目的是防止类爆炸的。

  • ![在这里插入图片描述](https://img-blog.csdnimg.cn/31fc0a78df9e4451a764971b8eeb92be.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAcGFsZWF0dGE=,size_20,color_FFFFFF,t_70,g_se,x_16)
    
  • 在这里插入图片描述

  • 这样聚合以后,就时收到了Warm + Gift 。 如果想要更换,则就是cool + book等,这样通过抽象和实现的分别扩展,来减少类爆炸。

  • 使用桥接有m个实现n个抽象,则 可以有n*m的具体实现,如果仅仅用继承则 要有n * m 个类。

  • Java设计模式(23种)_第35张图片

  • 具体做法是将抽象类和实现类从继承关系转化为聚合关系。

  • 分成了两部分,一部分是抽象的部分,一部分是具体的实现。通过将抽象和实现分离开,这样可以在对抽象扩展是不会影响实现类,实现类增加实现时,不会影响抽象类,然后实现双维度扩展。

装饰者模式(星巴克咖啡)(Mybatis CachingHandler)

  • 用聚合代替继承
  • 在原有的对象的基础上增加一点装饰。
  • Java设计模式(23种)_第36张图片
  • 会在类的内部封装一个decorator,这个decorator就是我们想要装饰的对象,在这个类中,增加一个方法,通过对这个decorator对象进行操作,从而实现对decorator的装饰。
  • decorator可以传入所有的实现GameObject类的对象,并对其进行装饰。
  • Mybatis中通过decorator,对baseHandler的三个实现类,simpleHandler、reuseHandler、batchHandler进行了封装,并对其进行了二级缓存的装饰。

组合模式Composite(树状结构模式)

  • Java设计模式(23种)_第37张图片
  • Java设计模式(23种)_第38张图片
  • 就是一棵树
  • 对于branch节点,可能会有很多子节点,通过递归的方式去处理这种情况。HashMap中的拉链法。

享元模式(共享元数据)(连接池的思想)

  • 在池中的对象进行复用,不要每次需要一个对象就去创建,用完销毁。在用完之后,回收到池子中,等到下一次进行复用。
  • Java设计模式(23种)_第39张图片

门面模式(外观模式)(Mybatis的BaseHandler)

  • 对外,类似于处理一个黑匣子。
  • 各个方框之间的关系错综复杂,如果要实现一个行动,则需要去寻找很多个方框对象来进行协调。
  • 因此抽象出来一个外观(矩形),作为接口,我们直接调用这个接口的方法,而黑匣子中的具体实现,我们不去关注。
  • Java设计模式(23种)_第40张图片
  • Java设计模式(23种)_第41张图片

调停者模式(中介者模式)(消息中间件)

  • 对内
  • 当内部的对象过于复杂的情况下,使用一个中介,来协调各个对象之间的调用。类似于交换机的实现。
  • Java设计模式(23种)_第42张图片

行为型模式

  • 限制类的行为,使其符合既定的规范

策略模式(Stratery)(Comparator)(ThreadPoolExecutor)

  • 封装的是做一件事情时,不同的执行方式
  • 通常对于策略模式来说,通常会写几个默认的执行策略,然后直接调用这些策略就好。(例如线程池中的拒绝策略)
  • comparator是策略模式,comparable不是策略模式。
  • Java设计模式(23种)_第43张图片
  • 对于只有实现了Comparable的类才能进行比较,通过重写其中的compareTo()方法,来实现比较的方式。主要的目的是为了提供两个类比较的方式。
  • 但是当比较的方式增多时,对于不同的对象来说,不同的属性来说,比较的策略往往是不同的。因此就需要Comparator来表示需要比较的策略。
  • 每个实现的comparator就是一个策略。

责任链模式()

  • 实现对递进状态事件处理的解耦。
  • 博客
  • 可以通过将我们需要的事件插入到责任链中,来进行处理。插入责任,链,则表示在之前的都处理过,在确定是否处理本次的事件。
  • 通过设置我们的事件的插入的位置,来判断我们在什么时候需要处理该事件。( 每个实现类继承了Handler)
  • 不同处理事件的方式是不同的,每一处理事件都会有自己的处理链,把所有的链连接在一起,一起去处理就可以。(HandlerChain也继承了Handler)
  • 一个处理链的内部都是自己的一系列处理逻辑。
  • 有的是需要处理的进行处理,不需要处理的就不进行处理。
  • Java设计模式(23种)_第44张图片
  • Java设计模式(23种)_第45张图片

命令模式

  • 当很多个模块都具有类似功能但具体实现不同的方法时,可以将其封装起来,其子对象调用其中相应的已经实现的方法就可以了。
  • 例如在word中,文本、图像等都具有复制、插入、删除、撤回的功能,因此把这四个功能封装起来,放到接口Command中,当我们调用的时候,就可以直接调用Command的对应子类的方法就可以了。
  • Java设计模式(23种)_第46张图片
  • 命令+组合 -> 宏命令,一个命令套另一个命令。
  • 命令+责任链 -> 多次撤销,每次操作时都会记录到Chain中,当需要撤回时,只要对责任链进行执行就行。
  • 命令+备忘录 ->
    Java设计模式(23种)_第47张图片

模板方法(JDBCTemplate)

  • Java设计模式(23种)_第48张图片
  • 模板方法中会规定一个方法的流程,具体的子类会实现这个流程中具体的方法,来实现这个是实现类的这个特异化功能。
  • 这个流程就是这个模板方法的模板
  • Java设计模式(23种)_第49张图片

迭代器模式(List)

  • hasNext()有下一个吗 + next()输出下一个
  • Java设计模式(23种)_第50张图片
  • 遍历用的
  • ArrayList扩容每次扩大到1.5倍

访问者模式(编译器)

  • 访问结构固定的
  • 每个Visitor实现自己的accept()方法,让自己去处理实现的accept()的方式
  • Java设计模式(23种)_第51张图片

观察者模式(Obsever)(Spring Event)

  • 在很多系统中,Observer模式往往和责任链共同负责对于事件的处理,
    其中的某一个Observer是否将事件进一步传递。
  • 观察者主要有三个对象,事件源(婴儿),观察者(父母和狗),事件(哭)。当事件源(婴儿)触发事件(哭)时,观察者(父母)做出相应的动作。而且在事件源中需要注册观察者有哪些,观察者所进行的一系列动作一般由责任链模式来执行。
  • 一般和责任链一起用,当一个事件发生时,通常会有一系列的观察者做出相应的动作,从而和责任链模式一起走流程
  • Java设计模式(23种)_第52张图片

观察者

  • 对于观察者来说,主要是实现了针对事件发生的方法。
  • 根据事件传进来的参数,来自定义事件的处理方式。
  • Java设计模式(23种)_第53张图片

事件源

  • 对于事件源来说,要维护一个观察者列表,所有继承了observer都会可以放到检观察者列表中。
  • Java设计模式(23种)_第54张图片
  • 这个add()方法可以对外封装出去,来增加观察者,来维护观察者列表。

事件

  • 对于事件来说,事件本身也有很多的状态,一般都会有事件源,getSource(),因为对于观察者来说,有事件源才好处理事件。
  • 一般会抽象处一个事件本身,在上面的例子中,行了触发哭的时候会有很多状态,时间,或者哭的位置。
  • 事件源对象如何传递给观察者,就在事件中增加一个事件源。一般Event都会有getSource方法来获取事件源。
  • 在这里插入图片描述
  • 事件是可以成体系的。
    Java设计模式(23种)_第55张图片

代理模式(SpringAOP)

  • 聚合代替继承,通过Tank的对象来代替继承Tank,从而能够把tank对象封装起来进行使用。
  • Java设计模式(23种)_第56张图片
  • Proxy通过对聚合的对象使用Movable接口类,而不是具体对象,则会对所有的Movable对象都可以实现。
  • Java设计模式(23种)_第57张图片
  • 动态代理类相当于生成了最上面的TankTimeProxy对象
  • Java设计模式(23种)_第58张图片

JDK动态代理源码

  • Java设计模式(23种)_第59张图片
  • this 生成的$Proxy对象,m3 表示被代理的方法,args表示参数
  • Java设计模式(23种)_第60张图片
  • 整个流程包括两部分:1和2表示创建代理类,3表示执行代理方法:
  1. 调用生成代理类
  2. 代理类接收InnovationHandler,来方便后面调用它。
  3. 通过调用InnovationHandler的invoke方法,先去执行before的方法,然后执行原生的方法,后去执行after的方法,来进行代理。

CGLIB

  • 注意在CGLIB中应该调用的是methodProxy的invokeSuper方法,这样才表示调用了目标类的对应方法。
  • Java设计模式(23种)_第61张图片

备忘录模式(快照)

  • 事务回滚

状态模式(State)

  • 根据状态决定行为
  • Java设计模式(23种)_第62张图片

解释器模式


  • Java设计模式(23种)_第63张图片

你可能感兴趣的:(java设计模式)