设计模式
# 设计模式
# 理念
# 作用
使代码具有更好的:
- 代码重用性:相同代码不用多次编写
- 可读性 :编程规范
- 可扩展性 :添加新功能方便
- 可靠性:添加新功能,对原有功能没有影响
- 高内聚,低耦合
# 七大原则
单一职责原则 一个类只负责一个职责
接口隔离原则 客户端不应该依赖它不需要的接口 一个类对另一个类保存最小接口(依赖)
依赖倒置原则 高层模块不应该依赖于底层模块,二者都应该依赖其抽象 抽象不应该依赖细节,细节应该依赖抽象 依赖倒转中心思路是面向接口编程
里氏替换原则 子类集成父类,尽量不修改父类方法
开闭原则 对于扩展是开放的,对于修改是关闭的
迪米特法则 最少知道原则:一个对象应该对其他对象保持最少了解(类间关系越密切,耦合度越大)。 定义:只与直接的朋友通信。即提供接口,隐藏逻辑 核心:降低类之间的耦合关系(减少不必要的依赖)
合成复用原则 尽量使用合成、聚合的方式而不是使用继承 1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。 2.针对接口编程,而不是针对实现编程。 3.为了交互对象之间的松耦合设计而努力 继承又称为白箱复用,相当于把所有的实现细节暴露给子类,组合/聚合又被称为黑箱复用,对类以外的对象是无法获取实现细节的。
设计原则的核心思想
- 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
- 针对接口编程,而不是针对实现编程
- 为了交互对象之间的松耦合设计而努力
# 单例模式
概念
- 采取一定的方法保证在整个的软件系统中,
- 对同一类型类只能存在一个对象对象实例,
- 并且该类只提供一个取得其对象实例的方法
使用场景
- 对于需要频繁创建的销毁的对象,使用单例模式提供系统性能
- 对于创建对象时耗时过多或耗费资源过多的对象(重量级对象),但又经常用到的对象,工具类对象,频繁访问数据库或文件的对象(数据源,session工厂等)
# 饿汉式(静态常量)
方式
- 构造器私有化
- 类的内部创建对象(静态常量)
- 向外暴露静态公共方法:getInstance
- 代码实现
class A{
private static final A a = new A(new Date().getTime() + "");
private final String string;
private A(String string) {
this.string = string;
}
public static A getInstance(){
return a;
}
public String getString() {
return string;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
优缺点
优点:
- 实现简单
- 避免多线程同步问题
缺点:
- 在类装载时即完成实例化,未达到Lazy Loading效果
- 若未使用过,会造成内存浪费
# 饿汉式(静态代码块)
方式
- 构造器私有化
- 类的内部创建对象(静态代码快)
- 向外暴露静态公共方法:getInstance
- 代码实现
//饿汉式(静态代码块)
class B {
private static final B b;
private final String string = "s";
private B() {
}
static {
b = new B();
}
public static B getInstance() {
return b;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
优缺点
优点:
- 实现简单
- 避免多线程同步问题
缺点:
- 在类装载时即完成实例化,未达到Lazy Loading效果
- 若未使用过,会造成内存浪费
# 懒汉式(线程不安全)
方式
构造器私有化
向外暴露静态公共方法:getInstance
在公共方法中通过判断静态变量是否为空决定是否创建
代码实现
//懒汉式(线程不安全) class C { private static C c; private String string; private C() {} public static C getInstance() { if (c == null) { c = new C(); } return c; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
优缺点
优点:
- 起到懒加载效果(lazy laoding)
缺点:
- 只能在单线程使用
- 实际开发中,不能使用这种方法
# 懒汉式(线程安全,同步方法)
方式
在懒汉式(线程不安全)
基础上个体暴露方法添加synchronized
标签
public static synchronized C getInstance() {
if (c == null) {
c = new C();
}
return c;
}
2
3
4
5
6
优缺点
优点:
- 起到懒加载效果(lazy loading)
- 解决线程不安全问题
缺点:
- 效率太低
- 实际开发中,不推荐使用这种方法
###懒汉式(线程安全,同步代码块)
class D {
private static D c;
private String string;
private D() {
}
public static D getInstance() {
if (c == null) {
synchronized (D.class) {
c = new D();
}
}
return c;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
无法解决线程安全,和效率问题
# 双重检查
推荐使用
方式
构造器私有化
向外暴露静态公共方法:getInstance
在公共方法中进行双重检查,第一重检查是否为空,第二重同步代码块中再次判断是否为空
代码实现
//双重检查 class E { private static E c; private String string; private E() { } public static E getInstance() { if (c == null) { synchronized (E.class) { if (c == null) { c = new E(); } } } return c; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 静态内部类
推荐使用
方式
- 构造器私有化
- 实现静态内部类 其中含有静态属性Singleton
- 向外暴露静态公共方法:getInstance,返回私有静态内中静态属性
- 代码实现
//静态内部类
class F {
public String string;
private F() {
}
private static class FInstance {
private static final F F = new F();
static {
F.string = "123";
}
}
public static F getInstance() {
return FInstance.F;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
优缺点
优点:
- 起到懒加载效果(lazy loading)
- 使用JVM类装载时线程安全机制保证线程安全
###枚举
推荐使用
枚举可以实现单例模式,不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象
enum G {
INSTANCE;
public void say() {
System.out.println("ok");
}
}
2
3
4
5
6
7
# 工厂模式
概念
专门负责将大量有共同接口的类实例化,工厂模式可以动态决定将哪一个类实例化,不必事先知道要实例化那一个类。
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到与主项目的依赖关系的解耦合,提高项目的维护性和扩展性
设计模式的依赖倒置原则
作用场景
重复,或批量创建同一类,同一类型的对象
# 简单工厂模式(静态工厂模式)
介绍
简单工厂模式属于创建型模式,是工厂模式的一种。
是由一个工厂对象决定创建出那以后总产品类的实例。
定义一个创建对象的类,由这个类来封装实例化对象的行为
实现
- 定义一个基类
abstract class Pizza {
protected String name;
public abstract void prepare();//原材料
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 实体类
class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println(name + "披萨!!");
}
}
2
3
4
5
6
7
public class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println(name + "披萨");
}
}
2
3
4
5
6
3.实现一个工厂
//简单工厂类
public class SimpleFactory {
private static Pizza pizza;
public static Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new CheesePizza();
pizza.setName("奶酪");
break;
case "greek":
pizza = new GreekPizza();
pizza.setName("奶酪");
break;
default:
break;
}
return pizza;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 调用工厂
public class SimpleFactoryTest {
public static void main(String[] args) {
SimpleFactory.createPizza("greek").prepare();
SimpleFactory.createPizza("greek").bake();
SimpleFactory.createPizza("cheese").prepare();
}
}
2
3
4
5
6
7
输出结果
奶酪披萨
奶酪baking
奶酪披萨!!
2
3
# 工厂方法模式
介绍
定义一个创建对象的抽象方法,由子类决定要实例化的类。
工厂方法模式将对象的实例化推迟到子类
实现
- 实体类添加一个判断字段
class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京奶酪披萨");
System.out.println(name + "!!");
}
}
2
3
4
5
6
7
8
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
setName("北京希腊披萨");
System.out.println(name + "!!");
}
}
2
3
4
5
6
7
class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦奶酪披萨");
System.out.println(name + "!!");
}
}
2
3
4
5
6
7
8
public class LDGreekPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦希腊披萨");
System.out.println(name + "!!");
}
}
2
3
4
5
6
7
- 拆分工厂
//工厂基类
abstract class OrderPizza {
protected Pizza pizza;
abstract Pizza createPizza(String type);
public static Pizza getPizza(String country, String type) {
switch (country) {
case "bj":
return new BJOrderPizza().createPizza(type);
case "ld":
return new LDOrderPizza().createPizza(type);
default:
return null;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//工厂实现类
public class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new BJCheesePizza();
break;
case "greek":
pizza = new BJGreekPizza();
break;
default:
break;
}
return pizza;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//工厂实现类
public class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new LDCheesePizza();
break;
case "greek":
pizza = new LDGreekPizza();
break;
default:
break;
}
return pizza;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
具体使用
public static void main(String[] args) { Pizza pizza = OrderPizza.getPizza("ld", "greek"); assert pizza != null; pizza.prepare(); }
1
2
3
4
5//输出结果 伦敦希腊披萨!!
1
2
# 抽象工厂模式
介绍
定义一个接口用于创建相关或由依赖关系的对象簇,而无需指明具体的类
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。是对简单工厂模式的进一步抽象,将工厂抽象成两层,AbstractFactory和具体实现的工厂子类
实现
- 确定一个抽象接口
//抽象层
public interface AbsFactory {
Pizza createPizza(String orderType);
}
2
3
4
- 子工厂继承并实现接口
public class BJFactory implements AbsFactory {
private Pizza pizza;
@Override
public Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new BJCheesePizza();
break;
case "greek":
pizza = new BJGreekPizza();
break;
default:
break;
}
return pizza;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 封装操作
public class OrderPizza {
private static AbsFactory absFactory;
public static void setAbsFactory(AbsFactory factory) {
absFactory = factory;
}
public static Pizza getPizza(String type) {
if (absFactory != null) {
return absFactory.createPizza(type);
} else
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 操作
public static void main(String[] args) {
OrderPizza.setAbsFactory(new LDFactory());
Pizza cheese = OrderPizza.getPizza("cheese");
assert cheese != null;
cheese.prepare();
}
2
3
4
5
6
输出结果
伦敦奶酪披萨!!
# 原型模式
概念
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,允许一个对象在创建另一个可定制的对象。可以由工厂方法由克隆方法来实现。
作用场景
实现逻辑
- 实体类继承
Cloneable
接口 - 重写
clone()
方法
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
public Sheep() {
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
public Sheep getClone() throws CloneNotSupportedException {
return (Sheep) clone();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
使用
Sheep sheep = new Sheep("tom", 12, "red");
Sheep clone = sheep.getClone();
System.out.println(sheep);
System.out.println(clone);
2
3
4
输出结果
Sheep{name='tom', age=12, color='red'}
Sheep{name='tom', age=12, color='red'}
2
# 浅拷贝
介绍
对于数据类型为基本类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值赋值一份给新的对象
对于数据类型为引用类型的成员变量,浅拷贝会进行引用传递,将该成员变量的内存地址复制一份给新的对象。
举例
public static void main(String[] args) throws CloneNotSupportedException {
List<String> list = new ArrayList<>();
Sheep sheep = new Sheep("tom", 12, "red");
sheep.setList(list);
Sheep clone = sheep.getClone();
System.out.println(sheep);
System.out.println(clone);
list.add("Red");
System.out.println(sheep);
System.out.println(clone);
}
2
3
4
5
6
7
8
9
10
11
输出结果
Sheep{name='tom', age=12, color='red', list=[]}
Sheep{name='tom', age=12, color='red', list=[]}
Sheep{name='tom', age=12, color='red', list=[Red]}
Sheep{name='tom', age=12, color='red', list=[Red]}
2
3
4
# 深拷贝
介绍
赋值对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每隔引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。
方式
重写clone方法实现
基本类型直接调用父类进行
复杂类型单独进行转换
通过对象序列化实现
通过序列化与反序列化进行对象克隆
//通过对象序列化实现 public Object deepClone() { //创建流对象 ByteArrayOutputStream bos; ObjectOutputStream oos; ByteArrayInputStream bis; ObjectInputStream ois; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this);//当前对象以对象流方式输出 //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); Object object = ois.readObject(); bos.close(); oos.close(); bis.close(); ois.close(); return object; } catch (Exception e) { e.printStackTrace(); return null; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 建造者模式
概念
Builder pattern,又叫生成器模式,是一种对象构建模式,它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造不同的属性的对象
允许用户只通过指定复杂对象的类型和内容构建他们,用户不需要知道内部的具体构建细节
作用场景
创建复杂对象
抽象工厂模式只关心由什么工厂生产
建造者模式主要目的通过组装生产新的产品
操作流程
- 确定产品原型
public class House {
private String basic;
private String walls;
private String roofs;
public String getBasic() {
return basic;
}
public void setBasic(String basic) {
this.basic = basic;
}
public String getWalls() {
return walls;
}
public void setWalls(String walls) {
this.walls = walls;
}
public String getRoofs() {
return roofs;
}
public void setRoofs(String roofs) {
this.roofs = roofs;
}
@Override
public String toString() {
return "House{" +
"basic='" + basic + '\'' +
", walls='" + walls + '\'' +
", roofs='" + roofs + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- 创建一个建造者基类
public abstract class HouseBuilder {
protected House house = new House();
public abstract HouseBuilder buildBasic(String value);
public abstract HouseBuilder buildWalls(String value);
public abstract HouseBuilder buildRoofs(String value);
public House build() {
return house;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
- 实现具体建造者
public class CommonHouse extends HouseBuilder{
@Override
public HouseBuilder buildBasic(String value) {
house.setBasic(value);
return this;
}
@Override
public HouseBuilder buildWalls(String value) {
house.setWalls(value);
return this;
}
@Override
public HouseBuilder buildRoofs(String value) {
house.setRoofs(value);
return this;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 使用链式操作直接构建
public static void main(String[] args) {
House build = new CommonHouse().buildBasic("Asda").build();
System.out.println(build);
House build1 = new CommonHouse().buildBasic("地基").buildWalls("四墙").buildRoofs("屋顶").build();
System.out.println(build1);
}
2
3
4
5
6
输入结果
House{basic='Asda', walls='null', roofs='null'}
House{basic='地基', walls='四墙', roofs='屋顶'}
2
# 适配器模式
概念
Adapter pattern 将某个类的接口转换成客户端期望的另一个接口表示, 主要目的是兼容性 让原本因接口不匹配不能一起工作的两个类可以协同工作
适配器模式属于结构型模式
别名:包装器(Wrapper)
作用场景
# 类适配器模式
介绍
Adapter类 通过继承src类,实现dst类接口,完成src --> dst 的适配
实例
被适配类
public class Voltage220V {
public int output220V() {
int src = 220;
System.out.println("电压" + src + "伏");
return src;
}
}
2
3
4
5
6
7
适配接口
public interface Voltage5V {
int output5V();
}
2
3
适配器
public class VoltageAdapter extends Voltage220V implements Voltage5V{
@Override
public int output5V() {
int i = output220V();
return i/44;
}
}
2
3
4
5
6
7
8
使用者
public class Phone {
public void charging(Voltage5V voltage5V){
if (voltage5V.output5V() == 5){
System.out.println("true");
}else {
System.out.println("false");
}
}
}
2
3
4
5
6
7
8
9
10
调用
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
2
3
4
输出接口
电压220伏
true
2
注意事项
- 类适配器需要继承src类 , 且dst类必须是接口,有一定局限性
- src类的方法在Adapter中都会暴露,增加使用成本
- 由于继承src,可以根据需求重写src类的方法,使得Adapter的灵活性增加
# 对象适配器模式
介绍
基本思路与类适配器相同
修改Adapter类 不继承src类,而是持有src类的实例,以解决兼容性问题。
即: 持有src类,实现dst类接口,完成src--> dst的适配
根据合成复用原则:系统中尽量使用关联关系来代替继承关系
实例
修改适配器
public class VoltageAdapter implements Voltage5V {
private final Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int dst = 0;
if (voltage220V != null) {
int src = voltage220V.output220V();
dst = src / 44;
}
return dst;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
调用
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
2
3
4
输出结果
电压220伏
true
2
# 接口适配器模式
介绍
Default Adatper Pattern (缺省适配器模式)
当不需要全部实现接口提供的方法时,可以设计一个抽象类实现接口,并且为该接口的每隔方法提供一个默认实现(空方法)。抽象类的子类就可有选择的覆盖父类的某些方法。
实例
若一个接口存在四个方法
public interface Voltage5V {
int m1();
void m2();
void m3();
void m4();
}
2
3
4
5
6
7
创建抽象类预实现
public abstract class AbsAdapter implements Voltage5V {
public int m1() {
return 0;
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用
public static void main(String[] args) {
Voltage5V absAdapter = new AbsAdapter() {
@Override
public int m1() {
return 2;
}
};
System.out.println(absAdapter.m1());
}
2
3
4
5
6
7
8
9
输出结果
2
注意事项
适用于一个接口不想使用其所有的方法的情况
# 桥接模式
概念
Bridge pattern :将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
结构型设计模式,基于类的最小设计原则,通过使用封装、聚合以及继承等行为让不同的类承担不同的职责
特点:把抽象与行为实现分离开,从而保持各部分的独立性以及应对他们的功能扩展
作用场景
多功能扩展时
实现逻辑
- 定义实现类接口
public interface Brand {
void open();
void close();
void call();
}
2
3
4
5
- 具体实现类实现接口
public class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("XiaoMi open...");
}
@Override
public void close() {
System.out.println("XiaoMi close...");
}
@Override
public void call() {
System.out.println("XiaoMi call...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HuaWei implements Brand{
@Override
public void open() {
System.out.println("HuaWei open...");
}
@Override
public void close() {
System.out.println("HuaWei close...");
}
@Override
public void call() {
System.out.println("HuaWei call...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 定义抽象层抽象类接口
public abstract class Phone {
private final Brand brand;
public Phone(Brand brand) {
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 具体抽象层实现类继承抽象类接口
public class FoldedPhone extends Phone {
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println("FoldedPhone");
}
public void close() {
super.close();
System.out.println("FoldedPhone");
}
public void call() {
super.call();
System.out.println("FoldedPhone");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UpRightPhone extends Phone{
public UpRightPhone(Brand brand) {
super(brand);
}
public void close() {
super.close();
System.out.println("UpRightPhone");
}
public void call() {
super.call();
System.out.println("UpRightPhone");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 调用
public class BridgeTest {
public static void main(String[] args) {
//使用FoldedPhone类型的XiaoMi
Phone phone = new FoldedPhone(new XiaoMi());
phone.open();
//使用UpRightPhone类型的XiaoMi
Phone phone2 = new UpRightPhone(new XiaoMi());
//可以直接使用XiaoMi的方法,也可进行包装截断
phone2.open();
}
}
2
3
4
5
6
7
8
9
10
11
输出结果
XiaoMi open...
FoldedPhone
XiaoMi open...
2
3
注意事项
- 实现抽象和实现部分的分离,有助于系统的分层设计,产生更好的结构化系统
- 对于系统高层,只需要知道抽象部分与实现部分的街廓,其他部分由具体业务完成
- 桥接模式代替多层继承,减少子类个数,降低系统的管理和维护
- 由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计
- 要求正确识别系统两个独立变化的维度,其使用范围具有一定局限性,。
# 装饰者模式
概念
动态的将新功能附加到对象上,在对象功能扩展方面,比继承更有弹性,装饰者模式体现了开闭原则(OCP)
通过递归方式 方便组合和维护
作用场景
多对象进行组合使用,减少类创建
实例
定义被装饰者抽象类
public abstract class Drink { private String description; private float price = 0f; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public abstract float cost(); @Override public String toString() { return "Drink{" + "description='" + description + '\'' + ", price=" + price + '}'; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30装饰者具体类缓冲层
public class Coffee extends Drink{ @Override public float cost() { return super.getPrice(); } }
1
2
3
4
5
6
7装饰者具体类
public class BlackCoffee extends Coffee{ public BlackCoffee() { setDescription("BlackCoffee"); setPrice(30f); } }
1
2
3
4
5
6public class ShortCoffee extends Coffee { public ShortCoffee() { setDescription("ShortCoffee"); setPrice(20f); } }
1
2
3
4
5
6装饰器继承被装饰者
public class Decorator extends Drink { private final Drink drink; public Decorator(Drink drink) { this.drink = drink; } @Override public float cost() { return super.getPrice() + drink.cost(); } @Override public String getDescription() { return super.getDescription() + ":" + super.getPrice() + " && " + drink.getDescription(); } @Override public String toString() { return super.toString() + " " + drink; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24装饰者继承装饰器
public class Milk extends Decorator{ public Milk(Drink drink) { super(drink); setDescription("Milk"); setPrice(2f); } }
1
2
3
4
5
6
7
8public class Chocolate extends Decorator{ public Chocolate(Drink drink) { super(drink); setDescription("Chocolate"); setPrice(10f); } }
1
2
3
4
5
6
7
8public class Soy extends Decorator{ public Soy(Drink drink) { super(drink); setDescription("Soy"); setPrice(5f); } }
1
2
3
4
5
6
7调用
public class DecoratorTest { public static void main(String[] args) { Drink blackCoffee = new ShortCoffee(); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); blackCoffee = new Chocolate(blackCoffee); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); blackCoffee = new Milk(blackCoffee); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); blackCoffee = new Soy(blackCoffee); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); System.out.println(blackCoffee); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
输出结果
ShortCoffee
20.0
------------
Chocolate:10.0 && ShortCoffee
30.0
------------
Milk:2.0 && Chocolate:10.0 && ShortCoffee
32.0
------------
Soy:5.0 && Milk:2.0 && Chocolate:10.0 && ShortCoffee
37.0
------------
Drink{description='Soy', price=5.0} Drink{description='Milk', price=2.0} Drink{description='Chocolate', price=10.0} Drink{description='ShortCoffee', price=20.0}
Process finished with exit code 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 组合模式
概念
Composite pattern :部分整体模式,创建对象组的树形结构,将对象组合成树状结构以表示整体-部分的层次关系
属于结构型模式,使得用户对单个对象和组合对象的访问局有一致性。即组合能让昂客户以一致的方式处理个别对象以及组合对象
作用场景
当要处理的对象可以生辰一个树形结构,而我们要对树的节点或叶子进行操作时,能够提供一致的方式而不需要考虑它是节点还是叶子
实例
创建一个基类
public abstract class OrganizationComponent { private String name; private String des; public OrganizationComponent(String name, String des) { this.name = name; this.des = des; } protected void add(OrganizationComponent component) { throw new UnsupportedOperationException(); } protected void remove(OrganizationComponent component) { throw new UnsupportedOperationException(); } protected abstract void print(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35创建一级类
public class University extends OrganizationComponent { //存放College private final List<OrganizationComponent> list = new ArrayList<>(); public University(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent component) { list.add(component); } @Override protected void remove(OrganizationComponent component) { list.remove(component); } @Override protected void print() { System.out.println("----------" + getName() + "------------"); for (OrganizationComponent organizationComponent : list) { organizationComponent.print(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26创建二级类
public class College extends OrganizationComponent { //存放department private final List<OrganizationComponent> list = new ArrayList<>(); public College(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent component) { list.add(component); } @Override protected void remove(OrganizationComponent component) { list.remove(component); } @Override protected void print() { System.out.println("----------" + getName() + "------------"); for (OrganizationComponent organizationComponent : list) { organizationComponent.print(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26创建叶子类
public class Department extends OrganizationComponent{ public Department(String name, String des) { super(name, des); } @Override public String getName() { return super.getName(); } @Override protected void print() { System.out.println(getName()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16调用
public class CompositeTest { public static void main(String[] args) { OrganizationComponent university = new University("清华", "大学"); OrganizationComponent college1 = new College("计算机学院", "计算机"); OrganizationComponent college2 = new College("软件学院", "软件"); college1.add(new Department("计算机科学与技术","computer")); Department department = new Department("大数据", "computer"); college1.add(department); college2.add(new Department("软件工程","软件工程")); university.add(college1); university.add(college2); university.print(); college1.remove(department); university.print(); university.remove(college1); university.print(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输出结果
----------清华------------
----------计算机学院------------
计算机科学与技术
大数据
----------软件学院------------
软件工程
----------清华------------
----------计算机学院------------
计算机科学与技术
----------软件学院------------
软件工程
----------清华------------
----------软件学院------------
软件工程
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意事项
- 简化客户端操作,客户端只需要面对一致的对象而不需要面对整体部分或节点叶子问题
- 具有较强扩展性,更改组合对象时,只需要调整内部的层次关系
- 方便创建复杂的层次结构,容易创建节点或叶子从而构建树形结构
- 需要遍历组织结构,或处理对象具有树形结构
- 要求较高的抽象性,要求节点和叶子的差异性小,若差异性过大则无法使用
# 外观模式
概念
Facade pattern 也叫过程模式: 为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,使这一子系统更加容易使用
屏蔽内部子系统的细节。
作用场景
封装多对象的操作,系统需要进行分层设计时
实例
定义方法对象
public class DVDPlayer { //使用单例模式 private static final DVDPlayer instance = new DVDPlayer(); private DVDPlayer() {} public static DVDPlayer getInstance(){ return instance; } public void open(){ System.out.println("DVDPlayer open..."); } public void off(){ System.out.println("DVDPlayer off..."); } public void play(){ System.out.println("DVDPlayer play..."); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class Popcorn { private static final Popcorn instance = new Popcorn(); private Popcorn() { } public static Popcorn getInstance() { return instance; } public void on(){ System.out.println("Popcorn on..."); } public void off(){ System.out.println("Popcorn off..."); } public void pop(){ System.out.println("Popcorn pop..."); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22封装操作
public class Facade { private final DVDPlayer dvdPlayer; private final Popcorn popcorn; public Facade() { dvdPlayer = DVDPlayer.getInstance(); popcorn = Popcorn.getInstance(); } public void open() { dvdPlayer.open(); popcorn.on(); } public void play() { dvdPlayer.play(); } public void pop() { popcorn.pop(); } public void close() { dvdPlayer.off(); popcorn.off(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29调用
public class FacadeTest { public static void main(String[] args) { Facade facade = new Facade(); facade.open(); facade.pop(); facade.play(); facade.close(); } }
1
2
3
4
5
6
7
8
9
输出结果
DVDPlayer open...
Popcorn on...
Popcorn pop...
DVDPlayer play...
DVDPlayer off...
Popcorn off...
2
3
4
5
6
注意事项
- 屏蔽子系统的细节,降低客户端对子系统使用的复杂性
- 当系统需要进行分层设计时,考虑使用Facade模式
- 维护大型系统时,开发Facade类提供遗留系统的较清晰简单的接口,提高复用性
- 不能过多或者使用不合理的使用外观模式,以系统有层次,便于维护为目的选择使用
# 享元模式
概念
Flyweight pattern 也叫蝇量模式 : 运用共享技术有效的支持大量细粒度 的对象
享元模式能够解决重复对象的内存浪费问题,当系统存在大量相似对象时,设置缓冲池并从中获取对象以避免创建新对象。达到降低系统内存,提高效率目的
享元模式提出要求:细粒度和共享对象
对象信息分为内部状态和外部状态
内部状态
对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
外部状态
对象得以依赖的一个标记,随环境改变而改变,不可共享的状态
作用场景
常用于系统底层开发,解决系统的性能问题,如数据库连接池
经典应用场景:池技术
实例
定义抽象类
public abstract class WebSite { abstract void use(User user); }
1
2
3具体实现类继承抽象类
public class ConcreteWebSite extends WebSite { //type即为享元模式的内部状态 共享内容 private String type; public ConcreteWebSite(String type) { this.type = type; } @Override void use(User user) { System.out.println(user.getName() + " use type: " + type); } }
1
2
3
4
5
6
7
8
9
10
11
12
13定义外部状态类
public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15创建工厂类 提供享元获取方式
public class WevSiteFactory { private final HashMap<String, ConcreteWebSite> pool = new HashMap<>(); public WebSite getWebSiteCategory(String type) { if (!pool.containsKey(type)) { pool.put(type, new ConcreteWebSite(type)); } return pool.get(type); } public int getWebSiteCount() { return pool.size(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14调用
public class FlyWeightTest { public static void main(String[] args) { WevSiteFactory wevSiteFactory = new WevSiteFactory(); WebSite webSite = wevSiteFactory.getWebSiteCategory("new"); WebSite webSite2 = wevSiteFactory.getWebSiteCategory("model"); User user = new User("test"); User user2 = new User("user"); webSite.use(user); webSite.use(user2); webSite2.use(user); webSite2.use(user2); System.out.println(wevSiteFactory.getWebSiteCount()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果
test use type: new
user use type: new
test use type: model
user use type: model
2
2
3
4
5
注意事项
- 系统中有大量对象,并且消耗大量内存,并且对象的状态大部分可以外部化时,可以考虑用享元模式
- 用唯一标识码判断,如果内存中有,则返回这个唯一表示码 的对象用HashMap/HashTable存储
- 享元模式极大减少对象的创建,降低程序内存的占用,
- 提高了系统的复杂度,需要分离内部状态和外部状态,外部状态具有固化特性**,不应随内部状态的改变而改变,这是使用享元模式需要注意的点**
- 使用时应注意划分内部状态和外部状态,并且需要一个工厂类加以控制
- 应用场景是需要缓冲池的场景如String常量池、数据库连接池
# 代理模式
概念
Proxy pattern :为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。
好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能
被代理对象可以是远程对象、创建开销大的对象或需要安全控制的对象
与装饰者模式区别:代理模式侧重于访问控制,装饰者模式侧重于功能动态附加分离,
作用场景
# 静态代理
实例
创建接口
public interface ITeachDao { void teach(); }
1
2
3创建被代理对象
public class TeachDao implements ITeachDao { @Override public void teach() { System.out.println("teaching..."); } }
1
2
3
4
5
6创建代理者,接收被代理对象
public class TeachDaoProxy implements ITeachDao { private final ITeachDao teachDao; public TeachDaoProxy(ITeachDao teachDao) { this.teachDao = teachDao; } @Override public void teach() { System.out.println("proxy on..."); teachDao.teach(); System.out.println("proxy off..."); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14调用
public class ProxyTest {
public static void main(String[] args) {
TeachDaoProxy teachDaoProxy = new TeachDaoProxy(new TeachDao());
teachDaoProxy.teach();
}
}
2
3
4
5
6
输出结果
proxy on...
teaching...
proxy off...
2
3
优缺点
优点:
在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
缺点:
需要代理对象与目标对象实现一样接口,一旦接口增加方法,目标对象与代理对象都需要维护
###动态代理
介绍
JDK代理、接口代理
代理对象不需要实现接口,但目标哦对象要实现接口,否则不能使用动态代理
代理的对象生成利用JDK的api,动态的在内存中构建代理对象
实例
定义对象接口
public interface ITeachDao { void teach(); }
1
2
3接口实现
public class TeachDao implements ITeachDao { @Override public void teach() { System.out.println("teaching..."); } }
1
2
3
4
5
6定义代理工厂
public class ProxyFactory { private Object target; public ProxyFactory(Object target) { this.target = target; } public Object getProxyInstance(){ /** * public static Object newProxyInstance(ClassLoader loader, * Class<?>[] interfaces, * InvocationHandler h) { * ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法固定 * Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型 * InvocationHandler h: 事件处理,执行目标对象的方法时,触发事情处理器方法,将当前执行的目标对象方法作为参数传入 */ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { /** * method方法对象 * args参数数组 **/ System.out.println(" jdk proxy on..."); Object invoke = method.invoke(target, args); System.out.println(" jdk proxy off..."); return invoke; }); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30调用
public static void main(String[] args) { ITeachDao teachDao = new TeachDao(); ITeachDao proxyInstance = (ITeachDao) new ProxyFactory(new TeachDao()).getProxyInstance(); proxyInstance.teach(); }
1
2
3
4
5
6
输出结果
jdk proxy on...
teaching...
jdk proxy off...
2
3
# Cglib代理
介绍
也叫子类代理,属于动态代理范畴,可以在内存中动态的创建对象,而不需要实现接口,需要引入cglib jar包
要求目标对象实现一个接口 ,若目标对象是一个单独对象,没有实现任何接口,可以使用目标对象子类实现代理
是在内存中构建一个子类对象从而实现对目标对象功能扩展,即Cglib代理归属到动态代理。
它是也强大的高性能代码生成器,可以在运行期扩展java类与实现java接口。广泛被许多AOP框架使用实现方法拦截
Cglib包底层通过使用字节码处理框架ASM来转换字节码并生成新的类
如何选择
- 目标对象需要实现接口,用JDK代理
- 目标对象不需要实现接口,用Cglib代理
实例
导入cglib 的jar包
定义目标对象
public class TeachDao{ public void teach() { System.out.println("teach..."); } }
1
2
3
4
5
6创建代理工厂并实现cglib包的MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor { //维护一个目标对象 private final Object target; public ProxyFactory(Object target) { this.target = target; } //返回一个代理对象 public Object ProxyInstance(){ //创建一个工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类对象并返回 return enhancer.create(); } //重写 调用目标对象的方法 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib proxy begin..."); Object invoke = method.invoke(target,objects); System.out.println("cglib proxy end..."); return invoke; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29调用
public class CglibTest { public static void main(String[] args) { TeachDao teachDao = (TeachDao) new ProxyFactory(new TeachDao()).ProxyInstance(); teachDao.teach(); } }
1
2
3
4
5
6
输出结果
cglib proxy begin...
teach...
cglib proxy end...
2
3
# 代理模式变种
防火墙代理
内网通过代理穿透防火墙,实现对公网的访问
缓存代理
请求图片文件等资源时,先到缓存代理取,若取不到再到公网或者数据库取,然后缓存
远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象调用,远程代理通过网络和真正的远程对象沟通信息
同步代理
主要使用在多线程编程,完成多线程间同步工作
# 模板模式
概念
Template Method pattern 模板方法模式,在一个抽象类中公开定义执行它的方法模板。子类可以按需重写方法实现,但调用将以抽象类中定义的方法进行
模板方法模式定义一个操作的算法的骨架,将步骤延迟到子类中,使子类可以不改变一个算法的结构的基础上,就可以重定义该算法的某些特定步骤
属于行为型模式
作用场景
将共同的步骤/方法抽离出来
当要完成某个过程,该过程要执行一系列步骤,这一系列步骤基本相同,但个别步骤在实现时可能不同,通常考虑模板方法
实例
抽离抽象类定义固定步骤
public abstract class SoyMilk { final void make() { select(); add(); } final void select() { System.out.println("选材:"); } abstract void add(); }
1
2
3
4
5
6
7
8
9
10
11
12定义具体类继承模板
public class RedBeanSoyMilk extends SoyMilk{ private final String str; public RedBeanSoyMilk(String str) { this.str = str; } @Override void add() { System.out.println(str); } }
1
2
3
4
5
6
7
8
9
10
11
12public class BlackBeanSoyMilk extends SoyMilk{ private final String str; public BlackBeanSoyMilk(String str) { this.str = str; } @Override void add() { System.out.println(str); } }
1
2
3
4
5
6
7
8
9
10
11
12调用
public class TemplateTest { public static void main(String[] args) { RedBeanSoyMilk redBeanSoyMilk = new RedBeanSoyMilk("红豆"); BlackBeanSoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk("黑豆"); redBeanSoyMilk.make(); blackBeanSoyMilk.make(); } }
1
2
3
4
5
6
7
8
输出结果
选材:
红豆
选材:
黑豆
2
3
4
# 钩子方法
介绍
在模板模式的父类中,定义一个方法。它默认不做任何事,子类可以根据情况选择是否覆盖,该方法称为钩子
根据钩子方法返回boolean 确定是否某步骤是否执行
实例
模板中添加钩子方法
public abstract class SoyMilk { final void make() { select(); //判断 if (customer()) add(); } final void select() { System.out.println("选材:"); } abstract void add(); //钩子,决定是否需要添加配料 boolean customer() { return true; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18- 子类继承并重写
public class RedBeanSoyMilk extends SoyMilk { private final String str; public RedBeanSoyMilk(String str) { this.str = str; } @Override void add() { System.out.println(str); } //覆盖 @Override boolean customer() { return false; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18调用
public class TemplateTest { public static void main(String[] args) { RedBeanSoyMilk redBeanSoyMilk = new RedBeanSoyMilk("红豆"); BlackBeanSoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk("黑豆"); redBeanSoyMilk.make(); blackBeanSoyMilk.make(); } }
1
2
3
4
5
6
7
8
输出结果
选材:
选材:
黑豆
2
3
模板方法注意事项
- 基本思想:算法只存在于一个地方,也就是覆盖,容易修改。当需要修改算法时只需要修改父类的模板方法或已经实现的某些步骤。子类就会继承这些方法
- 实现最大化代码复用:父类的模板方法和已实现的某些睦州会被子类继承而直接使用
- 既统一了算法,也提供了很大灵活性,确保算法的结构保持不变,同时由子类提供部分步骤的实现
- 每个不同的实现都需要一个子类完成,类个数增加使系统庞大
- 一般模板方法都需要加上
final
关键字 防止子类重写模板方法
- 一般模板方法都需要加上
# 命令模式
概念
command pattern: 将一个请求封装成一个对象,以便使用不同参数表示不同的请求(命令),同时命令模式支持可撤销的操作
命令模式使得请求发送者与请求接收者消除彼此之间耦合,让对象之间的调用关系更加灵活,实现解耦
作用场景
界面的每个按钮都是一条命令、模拟cmd、订单的撤销\恢复、触发-反馈机制
实例
创建命令接收者
//命令接收者 public class LightReceiver { public void on() { System.out.println("light on.."); } public void off() { System.out.println("light off..."); } }
1
2
3
4
5
6
7
8
9
10
11创建命令接口
//命令接口 public interface Command { //执行操作 void execute(); //撤销 void undo(); }
1
2
3
4
5
6
7实现具体命令
//命令接收者,实现命令接口 public class LightOnCommand implements Command { private final LightReceiver lightReceiver; public LightOnCommand(LightReceiver lightReceiver) { this.lightReceiver = lightReceiver; } @Override public void execute() { lightReceiver.on(); } @Override public void undo() { lightReceiver.off(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//命令接收者,实现命令接口 public class LightOffCommand implements Command { private final LightReceiver lightReceiver; public LightOffCommand(LightReceiver lightReceiver) { this.lightReceiver = lightReceiver; } @Override public void execute() { lightReceiver.off(); } @Override public void undo() { lightReceiver.on(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/** * 没有任何命令,即空执行。用于初始化按钮,当调用空命令时,无忍耐和操作 * 这也是一种设计模式,省去对空判断 */ public class NoCommand implements Command{ @Override public void execute() { System.out.println("nothing..."); } @Override public void undo() { System.out.println("nothing..."); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15创建命令调用者
//命令调用者 public class RemoteController { private final List<Command> onCommands; private final List<Command> offCommands; //执行撤销的命令 private Command undoCommand; public RemoteController() { onCommands = new ArrayList<>(); offCommands = new ArrayList<>(); initCommand(); } //初始化命令 private void initCommand() { for (int i = 0; i < 5; i++) { onCommands.add(new NoCommand()); offCommands.add(new NoCommand()); } } //插入角色 public void setCommand(int no, Command onCommand, Command offCommand) { onCommands.add(no, onCommand); offCommands.add(no, offCommand); } //按钮开按钮 public void onButtonWasPush(int no) { undoCommand = onCommands.get(no); undoCommand.execute(); } //按钮关按钮 public void offButtonWasPush(int no) { undoCommand = offCommands.get(no); undoCommand.execute(); } //撤销按钮 public void undoButtonWasPush() { if (undoCommand != null) { undoCommand.undo(); undoCommand = null; } else { System.out.println("nothing..."); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47调用
public class CommandTest { public static void main(String[] args) { RemoteController controller = new RemoteController(); LightReceiver lightReceiver = new LightReceiver(); LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver); LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver); controller.setCommand(0, lightOnCommand, lightOffCommand); controller.onButtonWasPush(0); System.out.println("--------"); controller.undoButtonWasPush(); System.out.println("--------"); controller.offButtonWasPush(0); System.out.println("--------"); controller.undoButtonWasPush(); System.out.println("--------"); controller.undoButtonWasPush(); System.out.println("--------"); controller.onButtonWasPush(2); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
输出结果
light on..
--------
light off...
--------
light off...
--------
light on..
--------
nothing...
--------
nothing...
2
3
4
5
6
7
8
9
10
11
注意事项
- 核心:将发起请求的对象和执行请求的对象通过命令对象解耦,命令对象起到纽带桥梁作用
- 容易设计一个命令队列,只需要把命令对象放到队列,就可以多线程执行命令
- 容易实现对请求的撤销和重做
- 可能导致系统有过多的具体命令类,增加系统的复杂度
- 空命令是一种设计模式,省去了判空操作
# 访问者模式
概念
Visitor pattern 封装一些作用于某种和数据结构的各元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新的操作
主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
基本工作原理:在被访问者类中加一个对外提供接待访问者的接口
作用场景
需要对一个对象结构中的对象进行很多不同操作(操作彼此无关联)同时需要避免这些操作污染对象的类。
实例
创建一个结果抽象类
public abstract class Action { public abstract void getResult(Person person); }
1
2
3
4
5具体结果类实现
public class SuccessAction extends Action{ @Override public void getResult(Person person) { System.out.println(person.getClass().getSimpleName() + "评价:成功"); } }
1
2
3
4
5
6
7public class FailAction extends Action{ @Override public void getResult(Person person) { System.out.println(person.getClass().getSimpleName() + "评价:失败"); } }
1
2
3
4
5
6对象类抽象
public abstract class Person { public abstract void accept(Action action); }
1
2
3具体对象类 双分派
public class Man extends Person { @Override public void accept(Action action) { action.getResult(this); } }
1
2
3
4
5
6/* 使用了双分派:首先在客户端程序中将具体的状态作为参数传递到Woman中(第一次分派) woman类中调用作为参数的具体方法中的getResult()方法,将自己作为参数传入(this),完成第二次分派 达到解耦 */ public class Woman extends Person{ @Override public void accept(Action action) { action.getResult(this); } }
1
2
3
4
5
6
7
8
9
10
11对象结构
public class ObjectStructure { //维护了一个集合 private final List<Person> persons = new ArrayList<>(); public void attach(Person person) { persons.add(person); } public void detach(Person person) { persons.remove(person); } public void display(Action action) { for (Person person : persons) { person.accept(action); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18调用
public class VisitorTest { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); objectStructure.attach(new Man()); objectStructure.attach(new Woman()); SuccessAction successAction = new SuccessAction(); FailAction failAction = new FailAction(); objectStructure.display(successAction); System.out.println("----"); objectStructure.display(failAction); } }
1
2
3
4
5
6
7
8
9
10
11
12
输出结果
Man评价:成功
Woman评价:成功
----
Man评价:失败
Woman评价:失败
2
3
4
5
双分派
所谓双分派是指不管类如何变化,我们都能找到期望的方法运行
意味着得到执行的操作取决于请求的种类和两个接收者的类型
注意事项
- 访问者模式符合单一职责原则,让程序具有优秀扩展性,灵活性高
- 对功能进行统一,可以做报表,UI,拦截器与过滤器,适用于数据结构相对稳定的系统
- 具体元素对访问者公布细节,关注其他类的内部细节,违背迪米特法则。使具体元素变更比较困难
- 违背依赖倒转原则,访问者依赖具体元素而不是抽象元素
- 要求使用系统有比较稳定的数据接口密切功能需求经常变化
# 迭代器模式
概念
Iterator pattern 行为型模式
提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不许呀知道集合对象的底层表示,即:不暴露其内部的结构
作用场景
当要展示一组相似对象,或扁你一组相同对象时使用
List<E>
实例
创建一个接口
public interface College { String getName(); void addDepartment(String name, String desc); //获取迭代器 Iterator createIterator(); }
1
2
3
4
5
6
7创建不同数据类型的迭代器实现类
public class ComputerCollegeIterator implements Iterator { //需要知道department以何种方式存放 private final Department[] departments; private int position; public ComputerCollegeIterator(Department[] departments) { this.departments = departments; } @Override public boolean hasNext() { return position < departments.length && departments[position] != null; } @Override public Object next() { return departments[position++]; } @Override public void remove() { } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class InfoCollegeIterator implements Iterator { private final List<Department> departments; private int position = -1; public InfoCollegeIterator(List<Department> departments) { this.departments = departments; } @Override public boolean hasNext() { if (position>=departments.size() -1){ return false; } else { position++; return true; } } @Override public Object next() { return departments.get(position); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24接口实现类
public class ComputerCollege implements College { private final Department[] departments; int numOfDepartment = 0; public ComputerCollege() { this.departments = new Department[5]; } @Override public String getName() { return "ComputerCollege"; } @Override public void addDepartment(String name, String desc) { departments[numOfDepartment++] =new Department(name, desc); } @Override public Iterator createIterator() { return new ComputerCollegeIterator(departments); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class InfoCollege implements College { private final List<Department> list; public InfoCollege() { list = new ArrayList<>(); } @Override public String getName() { return "InfoCollege"; } @Override public void addDepartment(String name, String desc) { list.add(new Department(name,desc)); } @Override public Iterator createIterator() { return new InfoCollegeIterator(list); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22根元素
public class Department { private String name; private String desc; public Department(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } @Override public String toString() { return "Department{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33输出类
public class outPutImpl { List<College> colleges; public outPutImpl(List<College> colleges) { this.colleges = colleges; } public void printCollege() { Iterator<College> iterator = colleges.iterator(); while (iterator.hasNext()) { College college = iterator.next(); System.out.println("=====" + college.getName() + "======"); printDepartment(college.createIterator()); } } public void printDepartment(Iterator iterator) { while (iterator.hasNext()) { Department next = (Department) iterator.next(); System.out.println(next); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23调用
public class IteratorTest { public static void main(String[] args) { List<College> collegeList = new ArrayList<>(); ComputerCollege computerCollege = new ComputerCollege(); computerCollege.addDepartment("aaa","aaa1"); computerCollege.addDepartment("aaa2","aaa2"); InfoCollege infoCollege = new InfoCollege(); infoCollege.addDepartment("bbb1","bb"); infoCollege.addDepartment("bbb2","bb"); collegeList.add(computerCollege); collegeList.add(infoCollege); outPutImpl outPut = new outPutImpl(collegeList); outPut.printCollege(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
输出结果
=====ComputerCollege======
Department{name='aaa', desc='aaa1'}
Department{name='aaa2', desc='aaa2'}
=====InfoCollege======
Department{name='bbb1', desc='bb'}
Department{name='bbb2', desc='bb'}
2
3
4
5
6
注意事项
- 提供统一方法遍历对象,客户端不再考虑聚合类型,
- 隐藏聚合的内部结构,客户端遍历聚合时只需取迭代器
- 提供单一责任原则设计思想:一个类应该只有引起变化的原因
- 每个聚合对象都要生成迭代器,多个迭代器不好管理类
# 观察者模式
概念
observer pattern
对象之间多对一依赖的一种设计方案,被依赖的对象为Subject
,依赖对象为Observer
,Subject通知Observer变化,比如牛奶站Subject与用户Observer组成一对多关系。
作用场景
JDK中的Observable类
实例
被观察者接口
//接口,由weatherData实现 public interface Subject { void registerObserver(Observers observers); void removeObserver(Observers observers); void notifyObserver(); }
1
2
3
4
5
6
7
8
9具体实现
public class WeatherData implements Subject { private float temperature; private float pressure; private float humidity; private final List<Observers> obs; public WeatherData() { this.obs = new ArrayList<>(); } public void dataChange() { notifyObserver(); } public void setData(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; dataChange(); } @Override public void registerObserver(Observers observers) { obs.add(observers); } @Override public void removeObserver(Observers observers) { obs.remove(observers); } @Override public void notifyObserver() { for (Observers ob : obs) { ob.update(this.temperature, this.pressure, this.humidity); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38观察者接口
//观察者接口 public interface Observers { void update(float temperature, float pressure, float humidity); }
1
2
3
4观察者实现类
public class CurrentConditions implements Observers { private float temperature; private float pressure; private float humidity; @Override public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } private void display() { System.out.println("====temperature: " + temperature + "======"); System.out.println("====temperature: " + pressure + "======"); System.out.println("====temperature: " + humidity + "======"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class BaiduSite implements Observers{ private float temperature; private float pressure; private float humidity; @Override public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } private void display() { System.out.println("====BaiduSite temperature: " + temperature + "======"); System.out.println("====BaiduSite temperature: " + pressure + "======"); System.out.println("====BaiduSite temperature: " + humidity + "======"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19调用
public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditions currentConditions = new CurrentConditions(); weatherData.registerObserver(currentConditions); BaiduSite baiduSite = new BaiduSite(); weatherData.registerObserver(baiduSite); weatherData.setData(10f,1f,2f); System.out.println("========="); weatherData.removeObserver(baiduSite); weatherData.setData(10f,1f,2f); }
1
2
3
4
5
6
7
8
9
10
11
12
输出结果
====temperature: 10.0======
====temperature: 1.0======
====temperature: 2.0======
====BaiduSite temperature: 10.0======
====BaiduSite temperature: 1.0======
====BaiduSite temperature: 2.0======
=========
====temperature: 10.0======
====temperature: 1.0======
====temperature: 2.0======
Process finished with exit code 0
2
3
4
5
6
7
8
9
10
11
12
注意事项
- 以集合方式管理用户(Observer),包括注册,移除和通知
- 增加观察者无需修改核心类,遵守ocp原则
# 中介者模式
概念
Mediator pattern:用一个中介对象来分装一系列的对象交互,中介者使各个对象不需要显式相互引用,从而使其耦合松散,而且可以独立改变他们之间的交互
中介者模式属于行为型模式,使代码易于维护
作用场景
实例
中介者抽象类
public abstract class Mediator { //将中介者/同事对象加入到集合中 abstract void register(String colleagueName, Colleague colleague); //接收消息,具体的同时对象发出消息 abstract void getMessage(int stateChange,String colleagueName); abstract void sendMessage(); }
1
2
3
4
5
6
7同事抽象类
public abstract class Colleague { private final Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public Mediator getMediator() { return mediator; } abstract void sendMessage(int stateChange); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15具体同事类
public class Alarm extends Colleague { public Alarm(Mediator mediator, String name) { super(mediator, name); mediator.register(name, this); } public void sendAlarm(int stateChange) { sendMessage(stateChange); } @Override void sendMessage(int stateChange) { this.getMediator().getMessage(stateChange, this.name); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class CoffeeMachine extends Colleague{ public CoffeeMachine(Mediator mediator, String name) { super(mediator, name); mediator.register(name, this); } @Override void sendMessage(int stateChange) { this.getMediator().getMessage(stateChange, this.name); } public void sendCoffeeMachine(int stateChange) { sendMessage(stateChange); } public void finishCoffee(){ System.out.println("coffee is ok"); sendMessage(0); } public void startCoffee(){ System.out.println("coffee start..."); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class Tv extends Colleague { public Tv(Mediator mediator, String name) { super(mediator, name); mediator.register(name, this); } @Override void sendMessage(int stateChange) { this.getMediator().getMessage(stateChange, this.name); } public void sendTv(int stateChange) { sendMessage(stateChange); } public void startTv() { System.out.println("tv start..."); } public void stopTv() { System.out.println("tv stop..."); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23中介者具体类
public class ConcreteMediator extends Mediator { private final HashMap<String, Colleague> colleagueMap; private final HashMap<String, String> interMap; public ConcreteMediator() { this.colleagueMap = new HashMap<>(); this.interMap = new HashMap<>(); } @Override void register(String colleagueName, Colleague colleague) { colleagueMap.put(colleagueName, colleague); interMap.put(colleague.getClass().getSimpleName(), colleagueName); // if (colleague instanceof Alarm){ // interMap.put("Alarm",colleagueName); // } } @Override void getMessage(int stateChange, String colleagueName) { if (colleagueMap.get(colleagueName) instanceof Alarm) { switch (stateChange) { case 0: ((CoffeeMachine) (colleagueMap.get("CoffeeMachine"))).startCoffee(); ((Tv) (colleagueMap.get("Tv"))).startTv(); break; case 1: ((Tv) (colleagueMap.get("Tv"))).stopTv(); break; } } if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) { switch (stateChange) { case 0: ((Tv) (colleagueMap.get("Tv"))).stopTv(); break; case 1: ((Tv) (colleagueMap.get("Tv"))).startTv(); break; } } } @Override void sendMessage() { } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48调用
public static void main(String[] args) { ConcreteMediator mediator = new ConcreteMediator(); Alarm alarm = new Alarm(mediator, "alarm"); CoffeeMachine coffee = new CoffeeMachine(mediator, "CoffeeMachine"); Tv tv = new Tv(mediator,"Tv"); alarm.sendAlarm(0); System.out.println("========="); coffee.finishCoffee(); System.out.println("========="); alarm.sendAlarm(1); }
1
2
3
4
5
6
7
8
9
10
11
输出结果
coffee start...
tv start...
=========
coffee is ok
tv stop...
=========
tv stop...
2
3
4
5
6
7
注意事项
- 多个类相互耦合,形成网状结构,使用中介者模式将网状结构分离为星型结构进行解耦
- 减少类间依赖,降低耦合,,符合迪米特法则
- 中介者承担较多责任,一旦中介者出现问题,整个系统都会受到影响
- 若设计不当,中介者本身将变得过于复杂
# 备忘录模式
概念
Memento pattern :在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
备忘录模式属于行为型模式
作用场景
- 游戏存档、
- IE中的后退、
- window的ctrl+z、
- 数据库的事务管理,
- 后悔药
实例
对象
public class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } //保存一个状态对象 public Memento saveStateMemento() { return new Memento(state); } //回档 public void getStateFromMemento(Memento memento) { state = memento.getState(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20状态对象
public class Memento { private final String state; public Memento(String state) { this.state = state; } public String getState() { return state; } }
1
2
3
4
5
6
7
8
9
10
11
12状态管理器
public class caretaker { private final List<Memento> list = new ArrayList<>(); public void add(Memento memento) { list.add(memento); } public Memento get(int index) { return list.get(index); } }
1
2
3
4
5
6
7
8
9
10
11调用
public static void main(String[] args) { Originator originator = new Originator(); caretaker caretaker = new caretaker(); originator.setState("状态1"); caretaker.add(originator.saveStateMemento()); originator.setState("状态2"); caretaker.add(originator.saveStateMemento()); originator.setState("状态3"); System.out.println("当前State: "+ originator.getState()); originator.getStateFromMemento(caretaker.get(0)); System.out.println("恢复 1 State: "+ originator.getState()); originator.getStateFromMemento(caretaker.get(1)); System.out.println("恢复 2 State: "+ originator.getState()); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果
当前State: 状态3
恢复 1 State: 状态1
恢复 2 State: 状态2
2
3
注意事项
- 提供了一种可以恢复状态的机制,使用户能够比较方便地回到某个历史状态
- 实现信息的封装,使用户不需要关心状态的保存细节
- 如果类的成员变量过多,会占用较大资源,每保存一次都会消耗一定的内存
- 为节约内存,建议备忘录模式可以与原型模式配合使用
# 解释器模式
概念
interpreter pattern :是指给定一个语言表达式,定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
作用场景
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种简单的语言表达
- 一个简单语法徐娅解释的场景
- 具体如编译器、运算表达式计算、正则表达式、机器人等
代码实现
定义抽象类表达式
/** * 抽象类表达式,通过HashMap键值对,可以获取到变量的值 */ public abstract class Expression { //解释公式和数值 key就是公式(表达式) 参数[a,b,c], value就是具体值 abstract int interpreter(HashMap<String,Integer> var); }
1
2
3
4
5
6
7符号解析器
/** * 抽象的运算符号解析器 每个运算符号都只和自己左右两个数字有关系 * 但左右两个数字有可能也是要给解析结果,无论何种类型,都是Expression类的实现类 */ public class SymbolExpression extends Expression{ protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right) { this.left = left; this.right = right; } @Override int interpreter(HashMap<String, Integer> var) { return 0; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18运算解析器
/** * 加法解析器 */ public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } /** * 处理相加 * super.left.interpreter(var)返回left表达式对应的值 */ @Override int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var) + super.right.interpreter(var); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } @Override int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var) - super.right.interpreter(var); } }
1
2
3
4
5
6
7
8
9
10/** * 变量解释器 */ public class VarExpression extends Expression{ private final String key; public VarExpression(String key) { this.key = key; } /** *var就是{a=10,b=20} * interpreter根据变量名称,返回对应值 */ @Override int interpreter(HashMap<String,Integer> var){ return var.get(this.key); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19定义核心类
public class Calculator { //定义表达式 private final Expression expression; //构造函数 并解析 public Calculator(String expStr) { //安排运算先后顺序 Stack<Expression> stack = new Stack<>(); char[] charArray = expStr.toCharArray(); Expression left = null; Expression right = null; for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new AddExpression(left, right)); break; case '-': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: //如果是一个var,创建一个VarExpression对象,并push到stack stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } this.expression = stack.pop(); } public int run(HashMap<String, Integer> var) { //最后将表达式传递到expression的interpreter进行解释执行 return this.expression.interpreter(var); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39调用
public class InterpreterTest { public static void main(String[] args) { String expStr = getExpStr(); HashMap<String, Integer> var = getValue(expStr); Calculator calculator = new Calculator(expStr); System.out.println("运算结果: " + expStr + "=" + calculator.run(var)); } private static HashMap<String, Integer> getValue(String expStr) { HashMap<String, Integer> map = new HashMap<>(); for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') { if (!map.containsKey(String.valueOf(ch))) { System.out.print("请输入" + ch + "的值:"); String in = new Scanner(System.in).nextLine(); map.put(String.valueOf(ch), Integer.valueOf(in)); } } } return map; } private static String getExpStr() { System.out.println("please enter"); return new Scanner(System.in).nextLine(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27输出结果
please enter a+b 请输入a的值:1 请输入b的值:2 运算结果: a+b=3 Process finished with exit code 0
1
2
3
4
5
6
7
注意事项
- 当有一个语言需要解释执行,可将该语言中的句子表示一个抽象语法树,使用解释器使程序具有良好的扩展性
- 使用监视器会引起类膨胀,使用递归调用方法,会导致调试非常复杂,效率可能降低
# 状态模式
概念
State pattern:主要解穴对象在多种状态转换时,需要对外输出不同的行为的问题。
状态与行为一一对应,状态之间可以相互转换
当一个对象的内在状态改变,允许改变其行为。
作用场景
同一事件或者对象有很多种状态,状态之间相互转换,对不同的状态要求有不同的额行为时候,可考虑使用状态模式
实例
状态抽象类
/** * 状态抽象类 */ public abstract class State { // 扣除积分 - 50 public abstract void deductMoney(); // 是否抽中奖品 public abstract boolean raffle(); // 发放奖品 public abstract void dispensePrize(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15具体实现类
/** * 不能抽奖状态 */ public class NoRaffleState extends State { // 初始化时传入活动引用,扣除积分后改变其状态 RaffleActivity activity; public NoRaffleState(RaffleActivity activity) { this.activity = activity; } // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态 @Override public void deductMoney() { System.out.println("扣除50积分成功,您可以抽奖了"); activity.setState(activity.getCanRaffleState()); } // 当前状态不能抽奖 @Override public boolean raffle() { System.out.println("扣了积分才能抽奖喔!"); return false; } // 当前状态不能发奖品 @Override public void dispensePrize() { System.out.println("不能发放奖品"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32/** * 可以抽奖的状态 */ public class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } //已经扣除了积分,不能再扣 @Override public void deductMoney() { System.out.println("已经扣取过了积分"); } //可以抽奖, 抽完奖后,根据实际情况,改成新的状态 @Override public boolean raffle() { System.out.println("正在抽奖,请稍等!"); Random r = new Random(); int num = r.nextInt(10); // 10%中奖机会 if(num == 0){ // 改变活动状态为发放奖品 context activity.setState(activity.getDispenseState()); return true; }else{ System.out.println("很遗憾没有抽中奖品!"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); return false; } } // 不能发放奖品 @Override public void dispensePrize() { System.out.println("没中奖,不能发放奖品"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42/** * 奖品发放完毕状态 * 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束 */ public class DispenseOutState extends State { // 初始化时传入活动引用 RaffleActivity activity; public DispenseOutState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("奖品发送完了,请下次再参加"); } @Override public boolean raffle() { System.out.println("奖品发送完了,请下次再参加"); return false; } @Override public void dispensePrize() { System.out.println("奖品发送完了,请下次再参加"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28/** * 发放奖品的状态 */ public class DispenseState extends State { // 初始化时传入活动引用,发放奖品后改变其状态 RaffleActivity activity; public DispenseState(RaffleActivity activity) { this.activity = activity; } // @Override public void deductMoney() { System.out.println("不能扣除积分"); } @Override public boolean raffle() { System.out.println("不能抽奖"); return false; } //发放奖品 @Override public void dispensePrize() { if(activity.getCount() > 0){ System.out.println("恭喜中奖了"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); }else{ System.out.println("很遗憾,奖品发送完了"); // 改变状态为奖品发送完毕, 后面我们就不可以抽奖 activity.setState(activity.getDispensOutState()); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40抽奖活动核心类
/** * 抽奖活动 */ public class RaffleActivity { // state 表示活动当前的状态,是变化 State state = null; // 奖品数量 int count = 0; // 四个属性,表示四种状态 State noRafflleState = new NoRaffleState(this); State canRaffleState = new CanRaffleState(this); State dispenseState = new DispenseState(this); State dispensOutState = new DispenseOutState(this); //构造器 //1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态) //2. 初始化奖品的数量 public RaffleActivity( int count) { this.state = getNoRafflleState(); this.count = count; } //扣分, 调用当前状态的 deductMoney public void debuctMoney(){ state.deductMoney(); } //抽奖 public void raffle(){ // 如果当前的状态是抽奖成功 if(state.raffle()){ //领取奖品 state.dispensePrize(); } } public State getState() { return state; } public void setState(State state) { this.state = state; } //这里请大家注意,每领取一次奖品,count-- public int getCount() { int curCount = count; count--; return curCount; } public void setCount(int count) { this.count = count; } public State getNoRafflleState() { return noRafflleState; } public void setNoRafflleState(State noRafflleState) { this.noRafflleState = noRafflleState; } public State getCanRaffleState() { return canRaffleState; } public void setCanRaffleState(State canRaffleState) { this.canRaffleState = canRaffleState; } public State getDispenseState() { return dispenseState; } public void setDispenseState(State dispenseState) { this.dispenseState = dispenseState; } public State getDispensOutState() { return dispensOutState; } public void setDispensOutState(State dispensOutState) { this.dispensOutState = dispensOutState; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91调用
public static void main(String[] args) { // 创建活动对象,奖品有1个奖品 RaffleActivity activity = new RaffleActivity(1); // 我们连续抽300次奖 for (int i = 0; i < 10; i++) { System.out.println("--------第" + (i + 1) + "次抽奖----------"); // 参加抽奖,第一步点击扣除积分 activity.debuctMoney(); // 第二步抽奖 activity.raffle(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果
--------第1次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第2次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第3次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第4次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第5次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第6次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第7次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第8次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
恭喜中奖了
--------第9次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾,奖品发送完了
--------第10次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
注意事项
- 状态模式使得代码具有很强可读性,每个状态行为封装到对应的一个类中
- 方便维护,将容易产生问题的if-else删除
- 符合开闭原则,容易增删状态
- 将会产生很多类,每个状态对应一个类,状态过多加大维护难度
# 策略模式
概念
strategy pattern:定义算法族,分别封装起来,让他们之间可以相互替换,此模式让算法变化独立于使用算法的用户
这算法体现了几个设计原则:
- 把变化的代码从不变的代码中分离出来
- 针对接口编程而不是具体类(定义策略接口)
- 多用组合、聚合,少用继承(客户通过组合方式使用策略)
作用场景
jdk-arrays
实例
定义一个功能接口
//功能 public interface FlyBehavior { void fly(); }
1
2
3
4接口实现不同策略下的功能
public class GoodFlyBehavior implements FlyBehavior{ @Override public void fly() { System.out.println("good fly..."); } }
1
2
3
4
5
6public class NoFlyBehavior implements FlyBehavior{ @Override public void fly() { System.out.println(" 不会飞翔 "); } }
1
2
3
4
5
6public class BadFlyBehavior implements FlyBehavior{ @Override public void fly() { System.out.println("bad fly..."); } }
1
2
3
4
5
6对象抽象类,规定基本功能
public abstract class Duck { //属性, 策略接口 protected FlyBehavior flyBehavior; protected QuackBehavior quackBehavior; abstract void display();//显示信息 public void quack() { System.out.println("鸭子嘎嘎叫~~"); } public void swim() { System.out.println("鸭子会游泳~~"); } public void fly() { if (flyBehavior != null) { flyBehavior.fly(); } } public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31具体对象类
public class PekingDuck extends Duck { //假如北京鸭可以飞翔,但是飞翔技术一般 public PekingDuck() { flyBehavior = new BadFlyBehavior(); } @Override void display() { System.out.println("~~北京鸭~~~"); } }
1
2
3
4
5
6
7
8
9
10
11
12public class ToyDuck extends Duck{ public ToyDuck() { flyBehavior = new NoFlyBehavior(); } @Override public void display() { System.out.println("玩具鸭"); } //需要重写父类的所有方法 public void quack() { System.out.println("玩具鸭不能叫~~"); } public void swim() { System.out.println("玩具鸭不会游泳~~"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21调用
public static void main(String[] args) { PekingDuck pekingDuck = new PekingDuck(); pekingDuck.display(); pekingDuck.quack(); pekingDuck.fly(); System.out.println("==========="); ToyDuck toyDuck = new ToyDuck(); toyDuck.display(); toyDuck.quack(); toyDuck.fly(); //策略替换 toyDuck.setFlyBehavior(new GoodFlyBehavior()); toyDuck.fly(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果
~~北京鸭~~~
鸭子嘎嘎叫~~
bad fly...
===========
玩具鸭
玩具鸭不能叫~~
不会飞翔
good fly...
2
3
4
5
6
7
8
注意事项
- 策略模式的关键:分析项目中的变化部分与不变部分
- 核心思想:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承
- 体现开闭原则(对修改关闭,对扩展开放),客户端增加行为不用修改原有代码,只需要添加一种策略(行为),避免使用多重if-else语句
- 提供可以替换继承关系办法,策略模式将算法封装在独立的Strategy类中使易于切换、理解、扩展
- 每添加一个策略就要增加一个类,策略过多导致类数目庞大
# 职责链模式
概念
Chain of Responsibility pattern:责任链模式,为请求创建一个接收者对象的链。 行为型模式
这种模式对请求发送者和接收者进行解耦
通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同请求传给下一个接收者
作用场景
多个对象处理同一请求,如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器
实例
责任人抽象类
public abstract class Approve { Approve approve;//下一个处理者 String name;//名称 public Approve(String name) { this.name = name; } //下一个处理者 public void setApprove(Approve approve) { this.approve = approve; } //处理审批请求的方法,得到请求,处理是子类完成 abstract void processRequest(PurchaseRequest request); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17责任人具体实现
public class CollegeApprove extends Approve{ public CollegeApprove(String name) { super(name); } @Override void processRequest(PurchaseRequest request) { if (request.getPrice()>5000&&request.getPrice()<10000){ System.out.println("请求编号" + request.getId() + "被" + this.name + "处理"); }else { approve.processRequest(request); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class DepartmentApprove extends Approve { public DepartmentApprove(String name) { super(name); } @Override void processRequest(PurchaseRequest request) { if (request.getPrice()<=5000){ System.out.println("请求编号" + request.getId() + "被" + this.name + "处理"); }else { approve.processRequest(request); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class SchoolMasterApprove extends Approve{ public SchoolMasterApprove(String name) { super(name); } @Override void processRequest(PurchaseRequest request) { if (request.getPrice()>=10000){ System.out.println("请求编号" + request.getId() + "被" + this.name + "处理"); }else { approve.processRequest(request); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14请求实体类
public class PurchaseRequest { private final int type;//请求类型 private final float price; private final int id; public PurchaseRequest(int type, float price, int id) { this.type = type; this.price = price; this.id = id; } public int getType() { return type; } public float getPrice() { return price; } public int getId() { return id; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23实现
public static void main(String[] args) { //请求 PurchaseRequest purchaseRequest = new PurchaseRequest(1, 5400f, 1); //相关审批人 DepartmentApprove department = new DepartmentApprove("department"); CollegeApprove college = new CollegeApprove("college"); SchoolMasterApprove master = new SchoolMasterApprove("master"); //设置下一责任负责人,形成环状 department.setApprove(college); college.setApprove(master); master.setApprove(department); department.processRequest(purchaseRequest); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输出结果
请求编号1被college处理
注意事项
- 将请求与处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,在链较长情况下需要控制链的最大节点数量,避免出现超长链无意识地破坏系统性能
- 调试不方便,采用类似递归方式,逻辑较复杂