目录
一、设计思想
二、设计原则
三、设计模式
1.创建型
(1)单例模式(Singleton)
(2)抽象工厂模式(AbstractFactory)
(3)工厂方法模式(FactoryMethod)
(4)构建器模式(Builder)
(5)原型模式(Prototype)
2.结构型
(6)门面模式(Facade)
(7)装饰模式(Decorator)
(8)组合模式(Composite)
(9)享元模式(Flyweight)
(10)代理模式(Proxy)
(11)转换器(包装器)模式(Adapter/Wrapper)
(12)桥接模式(Bridge)
3、行为型
(13)策略模式(Strategy)
(14)中介者模式(Mediator)
(15)责任链模式(Chain Of Responsibility)
(16)观察者模式(Observer)
(17)迭代器模式(Iterator)
(18)访问者模式(Visitor)
(19)命令模式(Command)
(20)备忘录模式(Memento)
(21)模板方法模式(TemplateMethod)
(22)状态模式(State)
(23)解释器模式(Intepreter)
本文为个人学习笔记,一切内容仅供参考。
一、设计思想
1. 可维护性
修改功能,需要改动的地方越少越好。
2. 可复用性
代码以后可以被重复使用。
3. 可扩展性
添加功能无需修改原来的代码。
4. 灵活性
代码的接口可以灵活调用。
二、设计原则
1. 单一职责原则
一个类别太大,别太累,应只负责单一的职责。
高内聚,低耦合:
高内聚:属于我的责任,都放在我这儿。
低耦合:不属于我的责任,我尽量不参与。
2. 开闭原则
对扩展开放,对修改关闭。
应在尽量不修改原来代码的情况下进行扩展。
抽象化,多态是关键。
3. 里氏替换原则
子类应该能够完全替代父类。
在所有使用父类的地方,必须能够透明的使用其子类对象。
4. 依赖倒置原则
应该尽量依赖抽象,而不是依赖具体。
面向抽象(接口)编程。
5. 接口分离原则
每一个接口应该承担独立的角色,不干自己不该干的事。
应避免子类实现不需要的方法。
需要对客户提供接口的时候,只需要暴露最小的接口。
6. 迪米特原则
降低耦合。
尽量不要和“陌生人”说话,对于一个对象,其“非陌生人”包括:
本身(this)。
以参数形式传入到当前对象方法中的对象。
当前对象的成员对象(及其所在集合中的其他元素)。
当前对象所创建的对象。
三、设计模式
1.创建型
(1)单例模式(Singleton)
什么是单例模式?
单例模式是指对于一个 Java 的类来说,在内存中只会存在一个该类的实例。
该类的构造方法为 private,外界无法通过构造方法创建实例。
该类需要提供一个公开的 get 方法来供外界获取这个唯一的实例。
为什么要使用单例模式?
某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
省去了 new 操作符,降低了系统内存的使用频率,减轻 GC 压力。
系统中某些类,如 spring 里的 controller,控制着处理流程,如果该类可以创建多个的话,系统完全乱了。
避免了对资源的重复占用。
什么时候使用单例模式?
配置文件
单例模式的分类:
饿汉式
懒汉式
1. 饿汉式
饿汉式是指当类加载到内存之后,就立即实例化一个本类的对象(JVM 保证线程安全,推荐使用)。
其用法可以概括为:
创建实例:并初始化对象。
构造私有:让外界无法通过构造方法创建实例。
提供接口:让外界可以直接通过类名调用该方法,并返回之前创建的实例。
代码:
public class Singleton {
// 创建实例
private static final Singleton instance = new Singleton();
// 私有构造方法
private Singleton() {}
// 提供get方法
public static Singleton getInstance() {
return instance;
}
}
2. 懒汉式
懒汉式是指当需要用到该实例时,才去初始化该对象。
其用法可以概括为:
创建实例:不初始化对象。
构造私有:让外界无法通过构造方法创建实例。
提供接口:当外界调用 getInstance() 时,需要先判断本类对象是否已经创建(该实例是否为 null),若还没有创建,则初始化该对象并返回;若已经创建,则直接返回该对象。
懒汉式是一种线程不安全的模式。
懒汉式有一个很明显的缺点就是在多线程环境下,可能会不安全。
比如,当某一个线程调用了 getInstance() 方法,但是还没有完全完成对该对象的初始化时(该实例依然为 null),此时如果又有另一个线程调用了 getInstance() 方法,那么在经过判断后,第二个线程也会去创建一个对象,这就造成了多个不同对象的创建,破坏了单例的规则。
那么应该如何解决这个问题呢?
1. 对 getInstance() 方法加锁
可以通过给 getInstance() 方法加上 synchronized 关键字来保证线程的安全。
优点:保证了安全。
缺点:该方法只能同时被一个线程调用,严重降低了系统的处理速度。
2. Double Check Lock (DCL)
在 getInstance() 方法中,先判断实例是否存在,如果不存在才进入接下来的同步代码块。同步代码块:用 synchronized 锁住对象的创建,在代码块中,再次判断该实例是否存在,如果仍然不存在才真的创建对象并返回。
优点:将 synchronized 关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在 instance 为 null,并创建对象的时候才需要加锁,性能有一定的提升(锁的粒度降低了)。
缺点:仍然可能会出错。在 Java 指令中创建对象和赋值操作是分开进行的,也就是说 new Singleton(); 语句是分两步执行的。但是 JVM 并不保证这两个操作的先后顺序,也就是说有可能 JVM 会为新的 Singleton 实例分配空间,然后直接赋值给 instance 成员,然后再去初始化这个 Singleton 实例,这样就可能出错了。
问题举例:
A、B 线程同时进入了第一个 if 判断。
A首先进入 synchronized 块,由于 instance 为 null,所以它执行 instance = new Singleton();
由于 JVM 内部的优化机制,JVM 先画出了一些分配给 Singleton 实例的空白内存,并赋值给 instance 成员(注意此时 JVM 没有开始初始化这个实例),然后 A 离开了 synchronized 块。
B 进入 synchronized 块,由于 instance 此时不是 null,因此它马上离开了 synchronized 块并将结果(还未初始化的对象)返回给调用该方法的程序。
此时 B 线程打算使用 Singleton 实例,却发现它没有被初始化,于是错误发生了。
优化(最终版)
给 instance 加上 volatile 关键字。被 volatile 修饰的变量,不会被线程本地缓存,所有线程对该对象的读写都会第一时间同步到主内存,从而保证多个线程间该对象的准确性。
代码:
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
// DCL(第一次判断)
if (instance == null) {
synchronized (Singleton.class) {
// DCL(第二次判断)
if (instance == null) {
// 创建对象
instance = new Singleton();
}
}
}
return instance;
}
}
volatile 的作用:
可见性(保证内存可见)
有序性(防止指令重排)
3. 在静态内部类中创建对象
定义一个静态内部类,在该内部类中创建实例并完成对象的初始化。原理:在加载外部类时,不会加载内部类,因此可以实现懒加载(JVM保证单例:每个类只加载1次)。可以通过在 getInstance() 方法中通过内部类名来获取该对象。
代码:
public class Singleton {
private Singleton() {}
// 静态内部类
private static class SingletonFactory {
// 当内部类被调用时才创建对象
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonFactory.instance;
}
}
4. 枚举
定义一个枚举类,其中包含一个本类的对象。优点:不仅可以解决线程同步问题,还可以防止反序列化(枚举类没有构造方法,反射时无法通过其 class 文件构造其对象)。
为什么不用静态方法而用单例模式?
两者其实都能实现我们加载的最终目的,但是他们一个是基于对象,一个是面向对象的,就像我们不面向对象也能解决问题一样,面向对象的代码提供一个更好的编程思想。
如果一个方法和他所在类的实例对象无关,那么它就应该是静态的,反之他就应该是非静态的。如果我们确实应该使用非静态的方法,但是在创建类时又确实只需要维护一份实例时,就需要用单例模式了。
(2)抽象工厂模式(AbstractFactory)
什么是抽象工厂模式?
抽象工厂里可以生产抽象产品,具体工厂去继承抽象工厂,具体产品去继承抽象产品。
特点:
每个工厂可以生产一套产品。
可以通过定义不同的工厂,灵活地扩展同一套产品的不同版本。
举例:
具体工厂:现代工厂、古代工厂。
抽象产品:武器、交通工具。
现代工厂重写其生产武器的方法为生产枪,重写其生产交通工具的方法为生产车。
古代工厂重写其生产武器的方法为生产剑,重写其生产交通工具的方法为生产马。
使用现代工厂调用其生产武器、交通工具的方法,生产的就是枪、车。
使用古代工厂 调用其生产武器、交通工具的方法,生产的就是剑、马。
代码:
// 抽象工厂
abstract class AbstractFactory {
public abstract Weapon createWeapon();
public abstract Vehicle createVehicle();
}
// 抽象产品
abstract class Vehicle {
public abstract void printName();
}
abstract class Weapon {
public abstract void printName();
}
// 现代工厂生产现代的产品
class ModernFactory extends AbstractFactory {
@Override
public Weapon createWeapon() {
return new Gun();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
}
// 具体产品
class Gun extends Weapon {
@Override
public void printName() {
System.out.println("gun");
}
}
class Car extends Vehicle {
@Override
public void printName() {
System.out.println("car");
}
}
// 古代工厂生产古代的产品
class AncientFactory extends AbstractFactory {
@Override
public Weapon createWeapon() {
return new Sword();
}
@Override
public Vehicle createVehicle() {
return new Horse();
}
}
// 具体产品
class Sword extends Weapon {
@Override
public void printName() {
System.out.println("sword");
}
}
class Horse extends Vehicle {
@Override
public void printName() {
System.out.println("horse");
}
}
public class Main {
public static void main(String[] args) {
AbstractFactory f = new ModernFactory();
Weapon w = f.createWeapon();
Vehicle v = f.createVehicle();
w.printName(); // gun
v.printName(); // car
// 切换另一个工厂
f = new AncientFactory();
// 生产的是另一系列的产品
w = f.createWeapon();
v = f.createVehicle();
w.printName(); // sword
v.printName(); // horse
}
}
(3)工厂方法模式(FactoryMethod)
什么是工厂方法模式?
针对不同类型的产品创建不同的工厂,所有的产品只需要去实现同一个接口,就可以在获取该对象时,通过不同的工厂获得不同的具体产品。
特点:
每个工厂只生产一类产品。
可以灵活地扩展产品种类(定义新的工厂)。
代码:
interface Vehicle {
public void getName();
}
class Car implements Vehicle {
@Override
public void getName() {
System.out.println("this is a car");
}
}
class Plane implements Vehicle {
@Override
public void getName() {
System.out.println("this is a plane");
}
}
class CarFactory {
public Vehicle create(){
return new Car();
}
}
class PlaneFactory {
public Vehicle create(){
return new Plane();
}
}
public class Main {
public static void main(String[] ags) {
Vehicle v = new CarFactory().create();
v.getName(); // this is a car
v = new PlaneFactory().create();
v.getName(); // this is a plane
}
}
(4)构建器模式(Builder)
什么是构建器模式?
用于分离复杂对象的构建和表示,构建时可以灵活地指定只构建哪部分的信息。
举例:
有一个 Person 类,其成员变量为每个人的个人信息:id、name、age、weight、score、location。
定义一个 Person 的静态内部类:PersonBuilder,在其中可以构建多种不同组合方式的信息。
代码:
class Person {
// 个人信息
private int id;
private String name;
private int age;
private double weight;
private int score;
private Location loc;
// 构建器(静态内部类)
public static class PersonBuilder {
Person p = new Person();
public PersonBuilder basicInfo(int id, String name, int age) {
p.id = id;
p.name = name;
p.age = age;
return this;
}
public PersonBuilder weight(double weight) {
p.weight = weight;
return this;
}
public PersonBuilder score(int score) {
p.score = score;
return this;
}
public PersonBuilder loc(String city, String street) {
p.loc = new Location(city, street);
return this;
}
public Person build() {
return p;
}
}
}
class Location {
private String city;
private String street;
public Location(String city, String street) {
this.city = city;
this.street = street;
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person.PersonBuilder()
.basicInfo(1, "申杉杉", 24)
//.weight(60.5)
//.score(99)
.loc("北京", "上园村3号")
.build();
}
}
(5)原型模式(Prototype)
什么是原型模式?
以某一个对象为原型,克隆出很多相同的对象(Java 自带该模式)。
什么时候使用原型模式?
用于一个对象的属性已经确定,需要产生很多相同的对象的时候。
原型模式的分类:
浅克隆
深克隆
使用方法:
需要被克隆的对象的类实现标记接口 Cloneable(标记接口:无方法,只是用于标记)。
在需要被克隆的对象的类中重写 clone() 方法。
1. 浅克隆
代码:
class Person implements Cloneable {
int age = 10;
int score = 100;
Location loc = new Location("北京", "北京交通大学");
// 重写 clone() 方法
@Override protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Location {
String city;
String school;
public Location(String city, String school) {
this.city = city;
this.school = school;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person();
Person p2 = (Person) p1.clone();
System.out.println(p2.loc.city); // 北京
p1.loc.city = "上海";
System.out.println(p2.loc.city); // 上海
}
}
问题:
loc 是一个指向 Location 对象的引用,p1 与 p2 的 loc 指向同一块内存。修改 p1 的 loc 以后,p2 的 loc 也会跟着变化。
两个复制的对象之间,他们的 loc 指向同一个对象,互相之间会有影响。
如何解决这个问题?
2. 深克隆
解决方法:
让 Location 也去实现标记接口 Cloneable,并重写 clone() 方法。
重写 Person 的 clone() 方法,不仅复制 Person 对象,还复制 Location 对象(即将 loc 引用指向的对象也复制了一份),这样就可以使 p1 和 p2 的 loc 互不影响。
代码:
class Location implements Cloneable {
String city;
String school;
public Location(String city, String school) {
this.city = city;
this.school = school;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable {
int age = 10;
int score = 100;
Location loc = new Location("北京", "北京交通大学");
@Override
protected Object clone() throws CloneNotSupportedException {
Person p = (Person) super.clone();
// 单独把Location对象再拷贝一份
p.loc = (Location) loc.clone();
return p;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person();
Person p2 = (Person) p1.clone();
System.out.println(p2.loc.city); // 北京
p1.loc.city = "上海";
System.out.println(p2.loc.city); // 北京,不会再因为p1而被修改
}
}
String 类型的变量(字符串对象的引用)需要进行深克隆吗?
不需要
2.结构型
(6)门面模式(Facade)
什么是门面模式?
MVC(实现 model 和 view 的分离与控制)。
(7)装饰模式(Decorator)
原理类似于代理模式,可以通过静态代理转化实现。
在实际开发中很少使用。
(8)组合模式(Composite)
什么是组合模式?
是一种用于组织树状结构数据的模式。
什么时候使用组合模式?
文件目录系统
代码:
abstract class Node {}
// 叶节点
class LeafNode extends Node {
String content;
public LeafNode(String content) {
this.content = content;
}
}
// 枝节点
class BranchNode extends Node {
String name;
List nodes = new ArrayList<>();
public BranchNode(String name) {
this.name = name;
}
public void add(Node n) {
nodes.add(n);
}
}
public class Main {
public static void main(String[] args) {
// 创建节点
BranchNode root = new BranchNode("根目录");
BranchNode chapter1 = new BranchNode("目录1");
BranchNode chapter2 = new BranchNode("目录2");
Node n11 = new LeafNode("文件11");
Node n12 = new LeafNode("文件12");
BranchNode chapter21 = new BranchNode("目录21");
Node n211 = new LeafNode("文件211");
Node n212 = new LeafNode("文件212");
// 添加节点
root.add(chapter1);
root.add(chapter2);
chapter1.add(n11);
chapter1.add(n12);
chapter2.add(chapter21);
chapter21.add(n211);
chapter21.add(n212);
}
}
上面的例子最后组合出来的树结构如图所示:
(9)享元模式(Flyweight)
什么是享元模式?
指重复利用对象。
什么时候使用享元模式?
Java 中的字符串常量池
面试中常问,但在实际开发中很少使用。
代码:
public class Main {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
// “abc”存储在字符串常量池中,s1、s2是同一个对象
String s3 = new String("abc");
String s4 = new String("abc");
// new是在堆空间创建对象,地址值不同
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
// String对象可以通过intern()方法,拿到常量池中对应的字符串常量对象
System.out.println(s1 == s3.intern()); // true
System.out.println(s3.intern() == s4.intern()); // true
}
}
(10)代理模式(Proxy)
什么是代理模式?
代理只负责实现某些特定的功能,而具体的其它事情交由被代理类自己实现。
分类:
静态代理
动态代理
1. 静态代理
使用方法:
代理、被代理对象都去实现某一个接口。
代理中包含这个接口的引用。
在代理重写的方法中去实现各种不同的操作。
代码:
interface Movable {
public abstract void move();
}
// 被代理对象
class Car implements Movable {
@Override
public void move() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 代理类
// 日志代理,该类只负责记录move()方法的运行日志
class CarLogProxy implements Movable {
// 代理中包含这个接口的引用
Movable m;
public CarLogProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("开始");
// 执行方法
m.move();
System.out.println("结束");
}
}
// 时间代理,该类只负责记录move()方法的运行时间
class CarTimeProxy implements Movable {
// 代理中包含这个接口的引用
Movable m;
public CarTimeProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
// 记录起始时间
long start = System.currentTimeMillis();
// 执行方法
m.move();
// 记录结束时间
long end = System.currentTimeMillis();
System.out.println("time = " + (end - start) / 1000 + "s");
}
}
public class Main {
public static void main(String[] args) {
// 可以以任意顺序灵活地实现各类代理的嵌套调用
new CarLogProxy(new CarTimeProxy(new Car())).move();
// 开车
// time = 3s
// 停车
}
}
静态代理可以灵活地扩展出各种代理类,且可以通过嵌套调用实现任意顺序的代理(有点类似于装饰模式)。
2. 动态代理
与静态代理有什么区别?
分离“代理行为”与“被代理对象”。
Jdk 反射生成动态代理
什么是反射?
通过二进制字节码文件(.class)分析类的属性和方法。
代码:
interface Movable {
void move();
}
class Car implements Movable {
@Override
public void move() {
System.out.println("开车!");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 日志代理
class LogHandler implements InvocationHandler {
Movable m;
public LogHandler(Movable m){
this.m = m;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("method " + method.getName() + " start...");
// 这里传入的是谁的引用,就调用谁的方法
// 在本例中,这里的method就是move方法
Object o = method.invoke(m, args);
System.out.println("method " + method.getName() + " end!");
// 这里返回的是一个空值
return o;
}
}
public class Main {
public static void main(String[] args) {
// 反射生成动态代理
Movable m = (Movable)
Proxy.newProxyInstance(Car.class.getClassLoader()
, new Class[]{Movable.class}
, new LogHandler(new Car()));
// 执行方法,实际调用的是LogHandler中的invoke()
m.move();
// method move start...
// 开车!
// method move end!
}
}
newProxyInstance(...):
用于动态生成代理(相当于之前的 CarLogProxy)。
ClassLoader:使用被代理对象的 ClassLoader。
Class>[]:(被)代理对象应该实现的所有接口,存进一个接口数组中。
InvocationHandler:被代理对象的方法被调用后,代理应该做的处理。
invoke():
proxy:动态生成的代理的对象(即:m)。
method:被调用的方法。
args:被调用方法的参数。
总结:
在 invoke() 中可以通过对 method 进行判断来实现对不同方法做不同的处理。
在 invoke() 中可以通过传入不同的对象(要实现Movable接口)实现对不同对象的代理。
(11)转换器(包装器)模式(Adapter/Wrapper)
什么是转换器模式?
接口转换器(适配器)。
什么时候使用转换器模式?
java.io
jdbc-odbc bridge(不是桥接模式)
ASM transformer
一个误区:常见的 Adapter 类反而不是 Adapter,如:WindowAdapter,KeyAdapter 等。
(12)桥接模式(Bridge)
什么是桥接模式?
分离抽象与具体,从双维度扩展,用聚合方式(桥)连接抽象与具体。
举例:
假设有一个生产交通工具的工厂,可以生产红色、黄色、蓝色 ... 的汽车、飞机、轮船 ... ,如果单独为每一种交通工具都建一个类,会造成类的爆炸。
解决:抽象的 Vehicle 类和具体的 VehicleImpl 类实现各自的继承体系,但在每一个抽象的类中聚合一个具体的实现类。
这里的“抽象”与“具体”指的是语文概念上的,而不是 Java 语法中的。比如,各种颜色为抽象的一组概念,但是抽象体系中的每一个颜色的类不是抽象类;具体体系中的父类 VehicleImpl 是一个抽象类。
在实际开发中很少使用。
代码:
// 抽象体系
abstract class Vehicle {
public VehicleImpl impl;
public abstract String getColor();
}
class RedVehicle extends Vehicle {
public VehicleImpl impl;
public RedVehicle(VehicleImpl impl) {
this.impl = impl;
}
@Override
public String getColor() {
return "红色";
}
}
// YellowVehicle、BlueVehicle ...
// 具体体系
abstract class VehicleImpl {
public abstract String getName();
}
class Car extends VehicleImpl {
@Override
public String getName() {
return "车";
}
}
// Plane、Boat ...
public class Main {
public static void main(String[] args) {
Vehicle v = new RedVehicle(new Car());
System.out.println(v.getColor() + "的" + ((RedVehicle) v).impl.getName()); // 红色的车
// 可以灵活地实现各种组合
v = new BlueVehicle(new Plane());
v = new YellowVehicle(new Car());
}
}
3、行为型
(13)策略模式(Strategy)
什么是策略模式?
做同一件事的时候,采用不同的策略方式。
不修改做这件事的代码,而是只用添加(扩展)不同的策略(开闭原则)。
举例:通过自己定义不同的比较器来实现按不同规则进行排序。
使用方法:
定义策略接口。
具体策略实现策略接口,并重写方法实现不同的策略。
创建具体的策略对象去调用方法。
代码:
interface LearnStrategy {
public void study();
}
class MathLearnStrategy implements LearnStrategy{
@Override
public void study() {
System.out.println("刷题");
}
}
class EnglishLearnStrategy implements LearnStrategy{
@Override
public void study() {
System.out.println("朗读");
}
}
public class Student {
public void sduty(LearnStrategy ls) {
ls.study();
}
public static void main(String[] args) {
// 针对不同的科目,采用不同的策略学习
new Student().sduty(new EnglishLearnStrategy()); // 朗读
new Student().sduty(new MathLearnStrategy()); // 刷题
}
}
(14)中介者模式(Mediator)
什么时候使用中介者模式?
消息中间件
(15)责任链模式(Chain Of Responsibility)
代码:
// 假设要设计一组用于处理消息的过滤器
interface Filter {
public void doFilter();
}
class FirstFilter implements Filter {
@Override
public void doFilter() {
// 处理消息中的非法字符
}
}
class SecondFilter implements Filter {
@Override
public void doFilter() {
// 将消息中的字母都换为小写
}
}
// ...,可以定义各种规则的消息过滤器
// 过滤器链(责任链)
// 该链实现Filter接口后,可以将一整条链看作一种单独的Filter
class FilterChain implements Filter {
// 存放链上的各种Filter
List filters = new ArrayList<>();
// 添加新的Filter到链上
public FilterChain add(Filter f) {
filters.add(f);
return this;
}
// 遍历所有的Filter并应用
@Override
public void doFilter() {
for (Filter f : filters) {
f.doFilter();
}
}
}
public class Main {
public static void main(String[] args) {
FilterChain fc = new FilterChain();
// 链式编程,每加入一个过滤器,就返回一个新的链
// 可以灵活地以任意的组合和顺序添加Filter
fc.add(new FirstFilter()).add(new SecondFilter());
fc.doFilter();
FilterChain fc2 = new FilterChain();
// 可以将整条链当做一种过滤器来进行添加和应用
fc2.add(fc).add(new SecondFilter());
fc2.doFilter();
}
}
(16)观察者模式(Observer)
什么是观察者模式?
事件源会产生事件,特定的事件会触发对应的观察者做出相应的处理。
观察者只和事件打交道,和事件源解耦
一个观察者可以同时监听几个不同的事件源。
一系列的事件可以形成继承体系。
什么时候使用观察者模式?
前端界面对用户输入的监听
针对程序运行错误的监听
代码:
// 假设一个场景,当观察到婴儿睡醒了以后需要去哄他
interface Observer {
// 有许多时候,观察者需要根据事件的具体情况来进行处理,因此需要传入event
void actionOnWakeUp(WakeUpEvent event);
}
// Observers(观察者)
class Dad implements Observer {
@Override
public void actionOnWakeUp(WakeUpEvent event) {
System.out.println("摇婴儿床");
}
}
class Mom implements Observer {
@Override
public void actionOnWakeUp(WakeUpEvent event) {
System.out.println("喂奶");
}
}
class Dog implements Observer {
@Override
public void actionOnWakeUp(WakeUpEvent event) {
System.out.println("汪汪汪");
}
}
abstract class Event {
// 有许多时候,观察者在处理事件的时候,需要事件源对象
abstract T getSource();
}
// Event(事件)
class WakeUpEvent extends Event {
long timeStamp;
String loc;
Child source;
public WakeUpEvent(long timeStamp, String loc) {
this.timeStamp = timeStamp;
this.loc = loc;
}
@Override
Child getSource() {
return source;
}
}
// Source(事件源)
class Child {
private boolean cry = false;
private List obs = new ArrayList<>();
{
obs.add(new Dad());
obs.add(new Mom());
obs.add(new Dog());
}
public boolean isCry() {
return cry;
}
public void wakeUp() {
cry = true;
// 产生事件
WakeUpEvent event = new WakeUpEvent(System.currentTimeMillis()
,"bed");
// 将事件传递给观察者处理
for (Observer o : obs) {
o.actionOnWakeUp(event);
}
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.wakeUp();
// 摇婴儿床
// 喂奶
// 汪汪汪
}
}
(17)迭代器模式(Iterator)
什么时候使用迭代器模式?
用于容器和容器遍历。
(18)访问者模式(Visitor)
什么是访问者模式?
在结构不变的情况下动态改变对于内部元素的动作。
缺点:只适合在结构固定时使用。
在实际开发中很少使用。
(19)命令模式(Command)
什么是命令模式?
在每一条命令中都包含 do() 和 undo() 方法,可以实现命令的执行与撤销。
别名:Action / Transaction。
(20)备忘录模式(Memento)
什么时候使用备忘录模式?
记录快照
游戏存盘
(21)模板方法模式(TemplateMethod)
什么是模板方法模式?
在父类中调用了有可能会被子类重写的方法。
代码:
abstract class Person {
abstract void getName();
abstract void getAge();
abstract void getSex();
// 模板方法
public void getInfo() {
getName();
getAge();
getSex();
}
}
// 以后要改变getInfo的内容,只需要改变子类中方法重写的内容即可
class XiaoMing extends Person {
@Override
void getName() {
System.out.println("小明");
}
@Override
void getAge() {
System.out.println(20);
}
@Override
void getSex() {
System.out.println("男");
}
}
public class Main {
public static void main(String[] args) {
Person p = new XiaoMing();
p.getInfo();
}
}
(22)状态模式(State)
什么是状态模式?
根据状态决定行为。
特点:
优点:易于状态的扩展。
缺点:不易于行为的扩展,当行为较多时,不如直接用 switch - case。
(23)解释器模式(Intepreter)
什么时候使用解释器模式?
动态脚本解析
在实际开发中很少使用。



