设计模式学习

urlyy

参考博客:
简单工厂模式、工厂模式以及抽象工厂模式(具体)
秒懂设计模式之抽象工厂模式(Abstract FactoryPattern)
抽象工厂模式(通俗易懂)
23种设计模式详解(全23种)

工厂:实例化对象的时候不再使用 new Object()形式,通过一个工厂获得对象

类型:创建型

简单工厂模式

可以根据用户的选择条件来实例化相关的类。
可以看做一条上面有很多种不同实例对象的流水线,用户只需要输入一条命令给switch case(多条机械手),其中一条与命令相对应的机械手就会取出对应的实例对象。
在这里插入图片描述
在这里插入图片描述

操作接口

1
2
3
4
5
package 简单工厂模式.opertion;

public interface Operation {
public double getResult(double numberA,double numberB) throws Exception;
}

具体操作类

加减乘除同理,除法可以多加一个判断除数为0

1
2
3
4
5
6
7
8
package 简单工厂模式.opertion;

public class Add implements Operation{
@Override
public double getResult(double numberA, double numberB) {
return numberA + numberB;
}
}

工厂类

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
package 简单工厂模式.factory;

import 简单工厂模式.opertion.*;

public class EasyFactory {
public static Operation createOperation(String name) {
Operation operationObj = null;
switch (name) {
case "+":
operationObj = new Add();
break;
case "-":
operationObj = new Sub();
break;
case "*":
operationObj = new Mul();
break;
case "/":
operationObj = new Div();
break;
default:
}
return operationObj;
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 简单工厂模式.client;

import 简单工厂模式.factory.EasyFactory;
import 简单工厂模式.opertion.Operation;

public class Client {
public static void main(String[] args) throws Exception {
Operation add = EasyFactory.createOperation("+");
Operation sub = EasyFactory.createOperation("-");
Operation mul = EasyFactory.createOperation("*");
Operation div = EasyFactory.createOperation("/");

System.out.println(add.getResult(3, 2));
System.out.println(sub.getResult(3, 2));
System.out.println(mul.getResult(3, 2));
System.out.println(div.getResult(3, 2));
}
}

我们无需提供具体的子类类名,只需要提供一个字符串即可得到相应的实例对象。
当子类的类名更换或者增加子类时我们都无需修改客户端代码,只需要在简单工厂类上增加一个分支判断代码即可。

优点:

工厂隐藏了创建对象时进行加工的过程,相比于直接在客户端new对象并进行加工,方便且易于维护。

缺点

当工厂中生产的对象种类很多、每个创建方法需要写很多的代码,会导致这个简单工厂类很庞大臃肿。
每次增加或者删除子类对象的创建方法都需要打开简单工厂类来进行修改,耦合性高,而且也违反了开闭原则。

工厂模式

相较于简单工厂模式,就是用更多的类来取代switch case分支,每一个分支归于一个类
还拿那个流水线做例子的话,就是为每一种产品开辟一条对应的流水线

在这里插入图片描述在这里插入图片描述

操作接口和具体操作类

同上面的简单工厂模式中的

工厂接口

1
2
3
4
5
6
7
package 工厂模式.factory;

import 简单工厂模式.opertion.Operation;

public interface OperationFactory {
public Operation createOperation() ;
}

具体的工厂类

加减乘除同理

1
2
3
4
5
6
7
8
9
10
package 工厂模式.factory;

import 简单工厂模式.opertion.Add;
import 简单工厂模式.opertion.Operation;

public class AddFactory implements OperationFactory{
@Override
public Operation createOperation() {
return new Add();
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package 工厂模式.client;

import 工厂模式.factory.AddFactory;
import 工厂模式.factory.DivFactory;
import 工厂模式.factory.MulFactory;
import 工厂模式.factory.SubFactory;

public class Client {
public static void main(String[] args) throws Exception {
//生成所有的具体工厂实例
AddFactory addFactory = new AddFactory();
SubFactory subFactory = new SubFactory();
MulFactory mulFactory = new MulFactory();
DivFactory divFactory = new DivFactory();
//进行操作
System.out.println(addFactory.createOperation().getResult(3,2));
System.out.println(subFactory.createOperation().getResult(3,2));
System.out.println(mulFactory.createOperation().getResult(3,2));
System.out.println(divFactory.createOperation().getResult(3,2));
}
}

优点

相对于简单工厂模式,进行了解耦,并克服了违背开闭原则的缺点,又保持了封装对象创建过程的优点。

缺点

把简单工厂的内部逻辑判断转移到了客户端代码来进行,因此客户端的代码也增加了不少。
每增加一个产品类,就需要增加一个对应的工厂类,增加了额外的开发量。

抽象工厂模式

相较于前两种模式,该模式不以产品而是以品牌家族建立工厂
在这里插入图片描述

品牌接口

1
2
3
4
5
6
7
8
9
10
package 抽象工厂模式.factory;

import 抽象工厂模式.product.computer.Computer;
import 抽象工厂模式.product.phone.Phone;

public interface ProductFactory {
Phone getPhoneProduct();
Computer getComputerProduct();
}

小米品牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 抽象工厂模式.factory;

import 抽象工厂模式.product.computer.Computer;
import 抽象工厂模式.product.computer.XiaoMiComputer;
import 抽象工厂模式.product.phone.Phone;
import 抽象工厂模式.product.phone.XiaoMiPhone;

public class XiaoMiFactory implements ProductFactory{
@Override
public Phone getPhoneProduct() {
return new XiaoMiPhone();
}

@Override
public Computer getComputerProduct() {
return new XiaoMiComputer();
}
}

华为品牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 抽象工厂模式.factory;

import 抽象工厂模式.product.computer.Computer;
import 抽象工厂模式.product.computer.HuaWeiComputer;
import 抽象工厂模式.product.phone.HuaWeiPhone;
import 抽象工厂模式.product.phone.Phone;

public class HuaWeiFactory implements ProductFactory{
@Override
public Phone getPhoneProduct() {
return new HuaWeiPhone();
}

@Override
public Computer getComputerProduct() {
return new HuaWeiComputer();
}
}

电脑产品接口

1
2
3
4
5
6
package 抽象工厂模式.product.computer;

public interface Computer{
void playComputerGame();
}

小米电脑

1
2
3
4
5
6
7
8
9
10
package 抽象工厂模式.product.computer;

public class XiaoMiComputer implements Computer{

@Override
public void playComputerGame() {
System.out.println("玩小米电脑游戏");
}
}

华为电脑

1
2
3
4
5
6
7
8
9
package 抽象工厂模式.product.computer;

public class HuaWeiComputer implements Computer{
@Override
public void playComputerGame() {
System.out.println("玩华为电脑游戏");
}
}

手机接口

1
2
3
4
5
6
package 抽象工厂模式.product.phone;

public interface Phone {
void playPhoneGame();
}

华为手机

1
2
3
4
5
6
7
8
9
package 抽象工厂模式.product.phone;

public class HuaWeiPhone implements Phone{
@Override
public void playPhoneGame() {
System.out.println("玩华为手机游戏");
}
}

小米手机

1
2
3
4
5
6
7
8
9
package 抽象工厂模式.product.phone;

public class XiaoMiPhone implements Phone{
@Override
public void playPhoneGame() {
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
package 抽象工厂模式.client;

import 抽象工厂模式.factory.HuaWeiFactory;
import 抽象工厂模式.factory.XiaoMiFactory;
import 抽象工厂模式.product.computer.Computer;
import 抽象工厂模式.product.phone.Phone;

public class Client {
public static void main(String[] args) {
System.out.println("============小米产品============");
//创建小米工厂
XiaoMiFactory xiaoMiFactory = new XiaoMiFactory();

//生产小米手机
Phone xiaoMiPhone = xiaoMiFactory.getPhoneProduct();
xiaoMiPhone.playPhoneGame();

//生产小米电脑
Computer xiaoMiComputer = xiaoMiFactory.getComputerProduct();
xiaoMiComputer.playComputerGame();

System.out.println("============华为产品============");
//创建华为工厂
HuaWeiFactory huaWeiFactory = new HuaWeiFactory();

//生产华为手机
Phone huaWeiPhone = huaWeiFactory.getPhoneProduct();
huaWeiPhone.playPhoneGame();

//生产华为电脑
Computer huaWeiComputer = huaWeiFactory.getComputerProduct();
huaWeiComputer.playComputerGame();
}
}

效果

1
2
3
4
5
6
7
8
============小米产品============
玩小米手机游戏
玩小米电脑游戏
============华为产品============
玩华为手机游戏
玩华为电脑上的游戏

Process finished with exit code 0

优点:

一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建)

缺点:

产品族扩展非常困难,要增加一个系列的某一产品,既要修改工厂抽象类里加代码,又修改具体的实现类里面加代码;
增加了系统的抽象性和理解难度

三种工厂模式的使用选择

  • 简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)

  • 工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)

  • 抽象工厂 :用来生产不同产品族的全部产品。(支持拓展增加产品;支持增加产品族)

建造者模式

类型:创建型

参考
23 种设计模式详解(全23种)
设计模式:建造者模式(Builder)
在这里插入图片描述

简单来说,就是有一个管事的和一堆各司其职(制造产品)的工人,这个管事的人可以每次吩咐一个人去完成他的任务,需要完成任务A就找到工人A,需要完成任务B就找到工人B。管事的只负责找到相应的工人,工人只负责完成自己的任务。

包结构

在这里插入图片描述

需要被建造的bean

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
package 建造者模式.bean;

public class Animal {
private String head;
private String body;
private String feet;
private String tail;

public String getHead() {
return head;
}

public void setHead(String head) {
this.head = head;
}

public String getBody() {
return body;
}

public void setBody(String body) {
this.body = body;
}

public String getFeet() {
return feet;
}

public void setFeet(String feet) {
this.feet = feet;
}

public String getTail() {
return tail;
}

public void setTail(String tail) {
this.tail = tail;
}

@Override
public String toString() {
return "Animal{" +
"head='" + head + '\'' +
", body='" + body + '\'' +
", feet='" + feet + '\'' +
", tail='" + tail + '\'' +
'}';
}
}

建造者基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package 建造者模式.builder;

import 建造者模式.bean.Animal;

public abstract class AnimalBuilder {
protected Animal animal;

public Animal getAnimal(){
return animal;
}

public void createAnimal(){
animal = new Animal();
}

public abstract void buildHead();
public abstract void buildBody();
public abstract void buildFeet();
public abstract void buildTail();
}

具体的建造者

造猫工人

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
package 建造者模式.builder;

import 建造者模式.bean.Animal;

public class CatBuilder extends AnimalBuilder {
@Override
public void buildHead() {
super.animal.setHead("猫的头");
}

@Override
public void buildBody() {
super.animal.setBody("猫的身子");
}

@Override
public void buildFeet() {
super.animal.setFeet("猫的脚");
}

@Override
public void buildTail() {
super.animal.setTail("猫的尾巴");
}
}

造狗工人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package 建造者模式.builder;

public class DogBuilder extends AnimalBuilder{
@Override
public void buildHead() { super.animal.setHead("狗的头"); }

@Override
public void buildBody() { super.animal.setBody("狗的身子"); }

@Override
public void buildFeet() {
super.animal.setFeet("狗的脚");
}

@Override
public void buildTail() {
super.animal.setTail("狗的尾巴");
}
}

负责管事的director

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
package 建造者模式.director;

import 建造者模式.bean.Animal;
import 建造者模式.builder.AnimalBuilder;

public class Director {
private AnimalBuilder animalBuilder;

public void setAnimalBuilder(AnimalBuilder animalBuilder) {
this.animalBuilder = animalBuilder;
}

public void createAnimal(){
//这里就可以控制赋值的顺序
animalBuilder.createAnimal();
animalBuilder.buildHead();
animalBuilder.buildBody();
animalBuilder.buildFeet();
animalBuilder.buildTail();
}

public Animal getAnimal() {
return animalBuilder.getAnimal();
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package 建造者模式.client;

import 建造者模式.bean.Animal;
import 建造者模式.builder.CatBuilder;
import 建造者模式.builder.DogBuilder;
import 建造者模式.director.Director;

public class Client {
public static void main(String[] args) {
//先new出director对象
Director director = new Director();
System.out.println("============创造猫咪============");
director.setAnimalBuilder(new CatBuilder());
director.createAnimal();
Animal cat = director.getAnimal();
System.out.println(cat);
System.out.println("============创造炫狗============");
director.setAnimalBuilder(new DogBuilder());
director.createAnimal();
Animal dog = director.getAnimal();
System.out.println(dog);
}
}

效果

1
2
3
4
5
6
============创造猫咪============
Animal{head='猫的头', body='猫的身子', feet='猫的脚', tail='猫的尾巴'}
============创造炫狗============
Animal{head='狗的头', body='狗的身子', feet='狗的脚', tail='狗的尾巴'}

Process finished with exit code 0

优点

  • 使用建造者模式可以使客户端不必知道产品内部的组成细节。(封装性)
  • 具体的建造者之间是相互独立的,对系统的扩展非常有利。(扩展性)
  • 由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他模块产生任何影响。

缺点

对不同类型的对象需要实现不同的具体构造器的类,这可能会大大增加类的数量

与工厂模式的区别

建造者模式构建对象的时候,通常构建对象的过程需要多个步骤,而且是将这些复杂的构建过程封装起来。
工厂模式构建对象通常只有一个步骤,调用一个工厂方法就可以生成一个对象。

建造者模式关注的是零件类型和装配工艺(顺序),而工厂模式是创建一个对象,这是最大不同的地方。

建造者模式的使用场景

1)相同的方法,不同的执行顺序,产生不同的事件结果时,可以使用建造者模式。
2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不想同时,可以使用建造者模式。
3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这时候可以使用建造者模式。

参考
23种设计模式详解(全23种)
23种设计模式(5):原型模式

原型模式

类型:创建型

通过复制现有实例来创建新的实例,无需知道相应类的信息。

关键字:Clone

深拷贝和浅拷贝

浅拷贝:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

深拷贝:将一个对象复制后,不论是基本数据类型还是引用类型都是重新创建的。

简单来说,就是深拷贝进行了完全彻底的拷贝,而浅拷贝不彻底。
clone明显是深拷贝,clone出来的对象是是不能去影响原型对象

优点

先说优点是因为怕有人觉得为什么不直接new
原因:

  • 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
  • 使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

角色

Client:客户端
Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口
ConcretePrototype:具体的原型类

UML图

在这里插入图片描述

原型模式的具体实现:一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。

包结构

在这里插入图片描述

原型类

1
2
3
4
5
6
7
8
9
package 原型模式.prototype;

public class PrototypeEmail implements Cloneable{
@Override
public PrototypeEmail clone() throws CloneNotSupportedException {
return (PrototypeEmail) super.clone();
}
}

具体类

1
2
3
4
5
6
7
8
9
10
package 原型模式.concretePrototype;

import 原型模式.prototype.PrototypeEmail;

public class Email extends PrototypeEmail {
public void print(int i){
System.out.println("制造出了第"+i+"封信");
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 原型模式.client;

import 原型模式.concretePrototype.Email;

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Email em = new Email();
for(int i=1;i<=10;i++){
Email email = (Email)em.clone();
email.print(i);
}
}
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
制造出了第1封信
制造出了第2封信
制造出了第3封信
制造出了第4封信
制造出了第5封信
制造出了第6封信
制造出了第7封信
制造出了第8封信
制造出了第9封信
制造出了第10封信

Process finished with exit code 0

在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。

原型模式的注意事项

1. 使用原型模式复制对象不会调用类的构造方法

因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。

而在单例模式中,只要将构造方法的访问权限设置为private类型,就可以实现单例。但是clone方法直接无视构造方法的权限.

所以,单例模式与原型模式是冲突的,在使用时要特别注意。

2. 深拷贝与浅拷贝。

Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。
如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 原型模式.prototype;

import java.util.ArrayList;

public class PrototypeEmail implements Cloneable{
private ArrayList arrayList = new ArrayList();
@Override
public PrototypeEmail clone() throws CloneNotSupportedException {
PrototypeEmail pe=(PrototypeEmail)super.clone();
//这里对引用对象另行拷贝了
pe.arrayList = (ArrayList)this.arrayList.clone();
return pe;
}
}

java提供的大部分的容器类都实现了Cloneable接口
会发生深拷贝的有java中的8种基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。

参考
行为型模式(一):责任链模式

责任链模式

类型:行为型

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

角色:

  1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

UML

在这里插入图片描述
在这里插入图片描述

模式的应用场景

  1. 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
  2. 可动态指定一组对象处理请求,或添加新的处理者。
  3. 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

包结构

在这里插入图片描述

处理者基类

1
2
3
4
5
6
7
8
9
10
11
12
13
package 责任链模式.handler;

public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler){
this.nextHandler=nextHandler;
}
public Handler getNextHandler(){
return this.nextHandler;
}
public abstract String handleReq(double reqMoney);
}

权限从小到大的处理者

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
package 责任链模式.handler;

public class SmallHandler extends Handler{
private double permission = 1e3;

@Override
public String handleReq(double reqMoney) {
if(reqMoney<=permission){
System.out.println("小领导还是有这个能力的");
return "小领导办了";
}else{
System.out.println("小领导没这个权限,去找中领导吧");
return this.getNextHandler().handleReq(reqMoney);
}
}
}
//////////////////////////////////////////////////////////////////////
package 责任链模式.handler;

public class MediumHandler extends Handler{
private double permission = 1e4;

@Override
public String handleReq(double reqMoney) {
if(reqMoney<=permission){
System.out.println("中领导还是有这个能力的");
return "中领导办了";
}else{
System.out.println("中领导没这个权限,去找大领导吧");
return this.getNextHandler().handleReq(reqMoney);
}
}
}
//////////////////////////////////////////////////////////////////////
package 责任链模式.handler;

public class BigHandler extends Handler{
private double permission = 1e5;

@Override
public String handleReq(double reqMoney) {
if(reqMoney<=permission){
System.out.println("大领导还是有这个能力的");
return "大领导办了";
}else{
System.out.println("大领导没这个权限,去找更大领导吧");
return this.getNextHandler().handleReq(reqMoney);
}
}
}
//////////////////////////////////////////////////////////////////////
package 责任链模式.handler;

public class BiggerHandler extends Handler{
private double permission = 1e6;

@Override
public String handleReq(double reqMoney) {
if(reqMoney<=permission){
System.out.println("更大领导还是有这个能力的");
return "更大领导办了";
}else{
System.out.println("要这么多钱干嘛?不批");
return "这笔钱没批下来";
}
}
}
//////////////////////////////////////////////////////////////////////

客户端

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
package 责任链模式.client;

import 责任链模式.handler.BigHandler;
import 责任链模式.handler.BiggerHandler;
import 责任链模式.handler.MediumHandler;
import 责任链模式.handler.SmallHandler;

public class Client {
public static void main(String[] args) {
BiggerHandler biggerHandler = new BiggerHandler();
BigHandler bigHandler = new BigHandler();
MediumHandler mediumHandler = new MediumHandler();
SmallHandler smallHandler = new SmallHandler();
//先设置next
bigHandler.setNextHandler(biggerHandler);
mediumHandler.setNextHandler(bigHandler);
smallHandler.setNextHandler(mediumHandler);
//开始处理
for(double i=100;i<=1e6;i=i*10){
double req = i+1;
System.out.println("----请求经费为"+req+"----");
String resp= smallHandler.handleReq(req);
System.out.println("最终是"+resp+'\n');
}
}
}

效果

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
----请求经费为101.0----
小领导还是有这个能力的
最终是小领导办了

----请求经费为1001.0----
小领导没这个权限,去找中领导吧
中领导还是有这个能力的
最终是中领导办了

----请求经费为10001.0----
小领导没这个权限,去找中领导吧
中领导没这个权限,去找大领导吧
大领导还是有这个能力的
最终是大领导办了

----请求经费为100001.0----
小领导没这个权限,去找中领导吧
中领导没这个权限,去找大领导吧
大领导没这个权限,去找更大领导吧
更大领导还是有这个能力的
最终是更大领导办了

----请求经费为1000001.0----
小领导没这个权限,去找中领导吧
中领导没这个权限,去找大领导吧
大领导没这个权限,去找更大领导吧
要这么多钱干嘛?不批
最终是这笔钱没批下来


Process finished with exit code 0

模式的扩展

职责链模式存在以下两种情况:
1、纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
2、不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

参考
JAVA设计模式初探之适配器模式
适配器模式详解

适配器模式

类型:结构型

将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作

适用场景

  1. 系统需要使用现有的类,但现有的类却不兼容。
  2. 需要建立一个可以重复使用的类,用于一些彼此关系不大的类,并易于扩展,以便于面对将来会出现的类。
  3. 需要一个统一的输出接口,但是输入类型却不可预知。

模式中的角色

目标接口(Target):

客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。

需要适配的类(Adaptee):

需要适配的类或适配者类。

适配器(Adapter):

通过包装一个需要适配的对象,把原接口转换成目标接口。 

两种实现方式:

1. 类的适配器模式(采用继承实现)

对于
在这里插入图片描述
在这里插入图片描述

Adaptee(厂商标准电压)

1
2
3
4
5
6
7
package 适配器模式.类适配器模式.adaptee;

public class Voltage220 {
public String beforeVoltage(){
return "220V";
}
}

Target(用户手机的电压接口)

1
2
3
4
5
6
7
8
9
10
11
12
package 适配器模式.类适配器模式.target;

public interface Voltage5 {
String afterVoltage();
}
---------------------------------------
package 适配器模式.类适配器模式.target;

public interface Voltage10 {
String afterVoltage();
}
---------------------------------------

Adapter(电压适配器)

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
package 适配器模式.类适配器模式.adapter;

import 适配器模式.类适配器模式.adaptee.Voltage220;
import 适配器模式.类适配器模式.target.Voltage5;

public class VoltageAdapter5 extends Voltage220 implements Voltage5 {

@Override
public String afterVoltage() {
return "5V";
}
}
-----------------------------------------------------------------------
package 适配器模式.类适配器模式.adapter;

import 适配器模式.类适配器模式.adaptee.Voltage220;
import 适配器模式.类适配器模式.target.Voltage10;

public class VoltageAdapter10 extends Voltage220 implements Voltage10 {

@Override
public String afterVoltage() {
return "10V";
}
}
-----------------------------------------------------------------------

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package 适配器模式.类适配器模式.client;


import 适配器模式.类适配器模式.adapter.VoltageAdapter10;
import 适配器模式.类适配器模式.adapter.VoltageAdapter5;

public class Client {
public static void main(String[] args) {
System.out.println("适配5V的电压");
VoltageAdapter5 va5 = new VoltageAdapter5();
System.out.println("----------适配之前的标准电压---------");
System.out.println(va5.beforeVoltage());
System.out.println("----------适配手机之后的电压---------");
System.out.println(va5.afterVoltage());
System.out.println();
System.out.println("适配10V的电压");
VoltageAdapter10 va10 = new VoltageAdapter10();
System.out.println("----------适配之前的标准电压---------");
System.out.println(va10.beforeVoltage());
System.out.println("----------适配手机之后的电压---------");
System.out.println(va10.afterVoltage());
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
适配5V的电压
----------适配之前的标准电压---------
220V
------------适配之后的电压-----------
5V

适配10V的电压
----------适配之前的标准电压---------
220V
------------适配之后的电压-----------
10V

Process finished with exit code 0

2. 对象适配器(采用对象组合方式实现)

在这里插入图片描述
这种方法其实和类适配器的区别就是适配器部分,用组合代替继承
只有Adapter类有点不同
在这里插入图片描述

Adapter(电压适配器)

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
package 适配器模式.对象适配器模式.adapter;

import 适配器模式.对象适配器模式.adaptee.Voltage220;
import 适配器模式.对象适配器模式.target.Voltage5;

public class VoltageAdapter5 implements Voltage5 {

Voltage220 voltage220;

public VoltageAdapter5(Voltage220 voltage220){
this.voltage220 = voltage220;
}

public String beforeVoltage(){
return voltage220.beforeVoltage();
}

@Override
public String afterVoltage() {
return "5V";
}
}
-----------------------------------------------------------------------
package 适配器模式.对象适配器模式.adapter;

import 适配器模式.对象适配器模式.adaptee.Voltage220;
import 适配器模式.对象适配器模式.target.Voltage10;

public class VoltageAdapter10 implements Voltage10 {

Voltage220 voltage220;

public VoltageAdapter10(Voltage220 voltage220){
this.voltage220 = voltage220;
}

public String beforeVoltage(){
return voltage220.beforeVoltage();
}

@Override
public String afterVoltage() {
return "10V";
}
}
-----------------------------------------------------------------------

模式优缺点

  1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码
  2. 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
  3. 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

类适配器模式还具有如下优点:

由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

类适配器模式的缺点如下:

对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。

对象适配器模式还具有如下优点:

一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

对象适配器模式的缺点如下:

与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

模式扩展

默认适配器模式(Default Adapter Pattern)或缺省适配器模式

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。因此也称为单接口适配器模式。
在这里插入图片描述

  1. 适配者接口是一个接口,通常在该接口中声明了大量的方法
  2. 默认适配器类是缺省适配器模式的核心类,使用空方法的形式实现了在ServiceInterface接口中声明的方法。通常将它定义为抽象类,因为对它进行实例化没有任何意义。
  3. 具体业务类是缺省适配器类的子类,在没有引入适配器之前,它需要实现适配者接口,因此需要实现在适配者接口中定义的所有方法,而对于一些无须使用的方法也不得不提供空实现。在有了缺省适配器之后,可以直接继承该适配器类,根据需要有选择性地覆盖在适配器类中定义的方法。

双向适配器

在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器
在这里插入图片描述

装饰者模式

类型:结构型

动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。

具体做法就是不断的包装,然后获得总价可以通过递归的形式由内向外获得所有花费之和
可以参考下面这张图

  1. 将LongBlack咖啡先包装进Milk类的对象内,表示加入了牛奶
  2. 然后再包装进Chocalate类的对象内,表示加入了巧克力
  3. 然后又包装进Chocalate类的对象内,表示再次加入了巧克力
    在这里插入图片描述

角色

  1. Component(被装饰对象的基类)
    定义一个对象接口,可以给这些对象动态地添加职责。
  2. ConcreteComponent(具体被装饰对象)
    定义一个对象,可以给这个对象添加一些职责。
  3. Decorator(装饰者抽象类)
    维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。
  4. ConcreteDecorator(具体装饰者)
    具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。

UML

在这里插入图片描述

包结构

在这里插入图片描述

component

基类

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
package 装饰者模式.component;

public abstract class Dish {
private String description="";
private int price=0;

public abstract int getAllCost();

public abstract String getAllDesc();


public void setDescription(String description) {
this.description = description;
}

public String getDescription() {
return description;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package 装饰者模式.component;

public class Fish extends Dish{
public Fish(){
super.setDescription("一条鱼");
super.setPrice(100000);
}
@Override
public int getAllCost() {
return super.getPrice();
}

@Override
public String getAllDesc() {
return super.getDescription();
}
}
--------------------------------------------
package 装饰者模式.component;

public class Soup extends Dish{
public Soup(){
super.setDescription("一碗汤");
super.setPrice(1000);
}
@Override
public int getAllCost() {
return super.getPrice();
}

@Override
public String getAllDesc() {
return super.getDescription();
}
}
--------------------------------------------
package 装饰者模式.component;

public class Tofu extends Dish{
public Tofu(){
super.setDescription("一块豆腐");
super.setPrice(10000);
}
@Override
public int getAllCost() {
return super.getPrice();
}

@Override
public String getAllDesc() {
return super.getDescription();
}
}

装饰者

基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package 装饰者模式.decorator;

import 装饰者模式.component.Dish;

public class Seasoning extends Dish{
private Dish dish;
public Seasoning(Dish dish){
this.dish=dish;
}

@Override
public int getAllCost() {
//用递归的方法去获得价格
//加号前面为该菜之前的花费,加号后面为当前调料的花费
return dish.getAllCost()+super.getPrice();
}

@Override
public String getAllDesc() {
//用递归的方法去获得描述
return dish.getAllDesc()+super.getDescription();
}
}

具体子类

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
package 装饰者模式.decorator;

import 装饰者模式.component.Dish;

public class Oil extends Seasoning{
public Oil(Dish dish) {
super(dish);
super.setPrice(100);
super.setDescription("+油");
}
}
----------------------------------------
package 装饰者模式.decorator;

import 装饰者模式.component.Dish;

public class Salt extends Seasoning{
public Salt(Dish dish) {
super(dish);
super.setPrice(10);
super.setDescription("+盐");
}
}
----------------------------------------
package 装饰者模式.decorator;

import 装饰者模式.component.Dish;

public class Sugar extends Seasoning{

public Sugar(Dish dish) {
super(dish);
super.setPrice(1);
super.setDescription("+糖");
}
}

客户端

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
package 装饰者模式.client;

import 装饰者模式.component.Dish;
import 装饰者模式.component.Fish;
import 装饰者模式.component.Tofu;
import 装饰者模式.decorator.Oil;
import 装饰者模式.decorator.Salt;
import 装饰者模式.decorator.Sugar;

public class Client {
public static void main(String[] args) {
//未经处理的鱼
Dish fish = new Fish();
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加油
fish = new Oil(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加盐
fish = new Salt(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加糖
fish = new Sugar(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加盐
fish = new Salt(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
/*-----------------------------------------------------*/
//未经处理的豆腐
Dish tofu = new Tofu();
tofu=new Oil(new Salt(tofu));
System.out.println(tofu.getAllDesc());
System.out.println("花费为"+tofu.getAllCost()+'\n');
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
一条鱼
花费为100000

一条鱼+油
花费为100100

一条鱼+油+盐
花费为100110

一条鱼+油+盐+糖
花费为100111

一条鱼+油+盐+糖+盐
花费为100121

一块豆腐+盐+油
花费为10110


Process finished with exit code 0

总结

  • 装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。
  • 在这里使用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。
  • 虽然装饰者模式可以为设计注入弹性,但装饰者也常常造成设计中有大量的小对象,如果过度使用会让程序变得很复杂。
    • 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型

参考
设计模式-外观模式(家庭影院你值得拥有)
23种设计模式详解(全23种)

外观模式

类型:结构型模式

外观模式可以理解为转换一群接口,客户只要调用这一个接口而不用调用多个接口才能达到目的,也不需关心这个子系统的内部细节。就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用。

角色

  • Facade外观类:提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给相应子系统对象。
  • System子系统:处理Facade对象指派的任务,是功能的实际提供者。
  • Client客户端:外观接口调用测试者。

UML

在这里插入图片描述

包结构

在这里插入图片描述

system

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
package 外观模式.system;

public class CPU {

public void start() {
System.out.println("cpu启动");
}

public void shutDown() {
System.out.println("CPU关闭");
}
}
------------------------------------------
package 外观模式.system;

public class Disk {
public void start() {
System.out.println("Disk启动");
}

public void shutDown() {
System.out.println("Disk关闭");
}
}
------------------------------------------
package 外观模式.system;

public class Memory {
public void start() {
System.out.println("Memory启动");
}

public void shutDown() {
System.out.println("Memory关闭");
}
}
------------------------------------------

facade

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
package 外观模式.facade;

import 外观模式.system.CPU;
import 外观模式.system.Disk;
import 外观模式.system.Memory;

public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;

public Computer() {
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}

public void start() {
System.out.println("电脑开始开机");
cpu.start();
disk.start();
memory.start();
System.out.println("电脑开机完毕");
}

public void shutDown() {
System.out.println("电脑开始关机");
cpu.shutDown();
disk.shutDown();
memory.shutDown();
System.out.println("电脑关机完毕");
}
}

client

1
2
3
4
5
6
7
8
9
10
11
12
13
package 外观模式.client;

import 外观模式.facade.Computer;

public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
System.out.println("---用户开机---");
computer.start();
System.out.println("\n---用户关机---");
computer.shutDown();
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---用户开机---
电脑开始开机
cpu启动
Disk启动
Memory启动
电脑开机完毕

---用户关机---
电脑开始关机
CPU关闭
Disk关闭
Memory关闭
电脑关机完毕

Process finished with exit code 0

优点

  1. 外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展。
  2. 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复 杂性。
  3. 当系统需要进行分层设计时,可以考虑外观模式帮我们更好的划分访问的层次。

总结

  • 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时
    可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。
  • 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。 要以让系统有层次,利于维护为目的。

参考
组合模式

组合模式

类型:结构型模式

将对象组合成树形结构来表示“部分-整体”的层次结构,使得客户能以一致的方式处理个别对象和组合对象。
如何解决:叶子和组合节点实现统一接口,叶子和组合节点分别重写接口的方法,使得调用方法一致但产生不同结果

关键代码:组合节点内部组合该接口,并且含有内部属性 List,里面放 Component。

角色

  • Component 抽象组件:为组合中所有对象提供一个接口,不管是叶子对象还是组合对象。
  • Composite 组合节点对象:实现了Component的所有操作,并且持有子节点对象。
  • Leaf 叶节点对象:叶节点对象没有任何子节点,实现了Component中的某些操作。

UML

在这里插入图片描述

包结构

在这里插入图片描述

component(基类)

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
package 组合模式.component;

import java.util.List;

public abstract class Node {
protected String name;

public Node(String name) {
this.name = name;
}

public abstract void doOperation();

public void add(Node c) {
System.out.println("叶子结点不能添加子结点");
}

public void remove(Node c) {
System.out.println("叶子结点没有子结点");
}

public Node getChild(int i) {
System.out.println("叶子结点没有子结点");
return null;
}

public List<Node> getChildren() {
System.out.println("叶子结点没有子结点");
return null;
}
}

两个子类

leaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 组合模式.leaf;

import 组合模式.component.Node;

public class Leaf extends Node {
public Leaf(String name) {
super(name);
}

@Override
public void doOperation() {
System.out.println("-----叶子节点:"+name+"的操作-----");
}
}

composite

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
package 组合模式.composite;

import 组合模式.component.Node;

import java.util.ArrayList;
import java.util.List;

public class NotLeaf extends Node {
private List<Node> nodes = new ArrayList<>();

public NotLeaf(String name) {
super(name);
}

@Override
public void doOperation() {
System.out.println("-----非叶结点:"+name+"的操作-----");
//进行所有子节点的操作
for (Node node : nodes) {
node.doOperation();
}
}

@Override
public void add(Node c) {
nodes.add(c);
}

@Override
public void remove(Node c) {
nodes.remove(c);
}

@Override
public Node getChild(int i) {
return nodes.get(i);
}

@Override
public List<Node> getChildren() {
return nodes;
}
}

client(就随便调用方法了,这个模式的树结构在输出语句上看起来挺麻烦)

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
package 组合模式.client;

import 组合模式.component.Node;
import 组合模式.composite.NotLeaf;
import 组合模式.leaf.Leaf;

public class Client {
public static void main(String[] args) {
Node node1 = new NotLeaf("根结点");
Node node2 = new Leaf("第一个叶子结点");
Node node3 = new NotLeaf("第一个非叶结点");
node1.add(node2);
node1.add(node3);
node1.doOperation();
System.out.print('\n');
Node node4 = new Leaf("第二个叶子结点");
Node node5 = new NotLeaf("第二个非叶结点");
node2.add(node4);
node3.add(node5);
node5.add(node4);
node5.doOperation();
System.out.print('\n');
node1.doOperation();
System.out.print('\n');
node1.remove(node3);
node1.doOperation();
System.out.print('\n');
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-----非叶结点:根结点的操作-----
-----叶子节点:第一个叶子结点的操作-----
-----非叶结点:第一个非叶结点的操作-----

叶子结点不能添加子结点
-----非叶结点:第二个非叶结点的操作-----
-----叶子节点:第二个叶子结点的操作-----

-----非叶结点:根结点的操作-----
-----叶子节点:第一个叶子结点的操作-----
-----非叶结点:第一个非叶结点的操作-----
-----非叶结点:第二个非叶结点的操作-----
-----叶子节点:第二个叶子结点的操作-----

-----非叶结点:根结点的操作-----
-----叶子节点:第一个叶子结点的操作-----


Process finished with exit code 0

优点

  1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;

  2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

缺点

  1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系;

  2. 不容易限制容器中的构件;

  3. 不容易用继承的方法来增加构件的新功能

参考
设计模式之命令模式
设计模式6-命令模式(Command)解析+案例实践+总结

命令模式

类型:行为型

将一个请求封装为一个对象,从而使你可用不同的请求对象对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。

角色

  • Receive接收者角色,处理命令
  • Command命令角色,需要执行的请求
  • Invoker调用者角色,接收命令,并执行命令

UML

在这里插入图片描述

包结构

在这里插入图片描述

receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 命令模式.receiver;

public class TV {
public void playCCTV1() {
System.out.println("--CCTV1--");
}

public void playCCTV2() {
System.out.println("--CCTV2--");
}

public void playCCTV3() {
System.out.println("--CCTV3--");
}
}

command

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
package 命令模式.command;

import 命令模式.receiver.TV;

public abstract class Command {
protected TV tv;

public Command(TV tv){
this.tv=tv;
}

public abstract void excute();
}

子类

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
package 命令模式.command;

import 命令模式.receiver.TV;

public class CCTV1Command extends Command{
public CCTV1Command(TV tv) {
super(tv);
}

@Override
public void excute() {
super.tv.playCCTV1();
}
}
---------------------------------------------
package 命令模式.command;

import 命令模式.receiver.TV;

public class CCTV2Command extends Command{
public CCTV2Command(TV tv) {
super(tv);
}

@Override
public void excute() {
super.tv.playCCTV2();
}
}
---------------------------------------------
package 命令模式.command;

import 命令模式.receiver.TV;

public class CCTV3Command extends Command{
public CCTV3Command(TV tv) {
super(tv);
}

@Override
public void excute() {
super.tv.playCCTV3();
}
}
---------------------------------------------

invoker

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
package 命令模式.invoker;

import 命令模式.command.Command;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TVController {
//创建存储历史命令记录的数组
List<Command> historyRecord = new ArrayList<>();

public void inputCommand(Command command){
historyRecord.add(command);
command.excute();
}

public void undo(){
if(historyRecord.isEmpty()){
System.out.println("频道为空,不能执行撤回操作");
return;
}
if(historyRecord.size()==1){
System.out.println("已经是第一个操作了,不能再撤回了");
return;
}
historyRecord.remove(historyRecord.size()-1);
historyRecord.get(historyRecord.size()-1).excute();
}
}

client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package 命令模式.client;

import 命令模式.command.CCTV1Command;
import 命令模式.command.CCTV2Command;
import 命令模式.command.CCTV3Command;
import 命令模式.invoker.TVController;
import 命令模式.receiver.TV;

public class Client{
public static void main(String[] args) {
TV tv = new TV();
TVController tc = new TVController();
System.out.println("------切换频道中-----");
tc.inputCommand(new CCTV2Command(tv));
tc.inputCommand(new CCTV3Command(tv));
tc.inputCommand(new CCTV1Command(tv));
System.out.println("-------撤销操作-------");
tc.undo();
tc.undo();
tc.undo();
}
}

效果

1
2
3
4
5
6
7
8
9
10
------切换频道中-----
--CCTV2--
--CCTV3--
--CCTV1--
-------撤销操作-------
--CCTV3--
--CCTV2--
已经是第一个操作了,不能再撤回了

Process finished with exit code 0

优点

  1. 更松散的耦合,将发起命令的客户端与具体处理命令的接收者完全解耦,客户端完全不知道接收者是什么样子。
  2. 更动态的控制,把请求封装起来,可以动态的对请求进行参数化、队列化和日志化等,使系统更灵活。
  3. 复合命令,很容易地组合命令,即宏命令,使系统功能更强大。
  4. 更好的扩展,很容易添加新的命令

缺点

类数量随命令数量增长而增长。可能造成类数量过多。(设计模式常见的缺点)

参考
23 种设计模式详解(全23种)

模板方法

类型:行为型

在父类中定义一个完成该事情的总方法,每个步骤的具体实现,由子类完成。
(感觉就是多态的内容)

角色

  • 抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。
  • 具体类(ConcreteClass):实现抽象类中的抽象方法,即不同的对象的具体实现细节。

UML

在这里插入图片描述

包结构

在这里插入图片描述

模板类

1
2
3
4
5
6
7
8
9
10
11
12
package 模板方法模式;

public abstract class Dish {
public void doDish(){
prepare();
cook();
serve();
}
public abstract void prepare();
public abstract void cook();
public abstract void serve();
}

具体子类

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
package 模板方法模式;

public class EggSoup extends Dish {
@Override
public void prepare() {
System.out.println("准备鸡蛋");
}

@Override
public void cook() {
System.out.println("煮蛋汤");
}

@Override
public void serve() {
System.out.println("将汤倒入碗中,上菜");
}
}
----------------------------------------------------
package 模板方法模式;

public class Steak extends Dish {
@Override
public void prepare() {
System.out.println("准备牛排");
}

@Override
public void cook() {
System.out.println("煎牛排");
}

@Override
public void serve() {
System.out.println("将牛排放入盘子上,上菜");
}
}
----------------------------------------------------

客户端

1
2
3
4
5
6
7
8
9
10
11
12
package 模板方法模式;

public class Client {
public static void main(String[] args) {
System.out.println("-----制作蛋汤----Client-");
Dish eggSoup = new EggSoup();
eggSoup.doDish();
System.out.println("-----制作牛排-----");
Dish steak = new Steak();
steak.doDish();
}
}

效果

1
2
3
4
5
6
7
8
9
10
-----制作蛋汤-----
准备鸡蛋
煮蛋汤
将汤倒入碗中,上菜
-----制作牛排-----
准备牛排
煎牛排
将牛排放入盘子上,上菜

Process finished with exit code 0

优点:

  1. 具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
  2. 代码复用的基本技术,在数据库设计中尤为重要。
  3. 存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。

缺点:

  • 每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。(设计模式常见缺点)

策略模式

类型:行为类

定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

与模板方法模式的比较

策略模式与模板方法模式很像,区别在于客户端调用方法时,模板方法模式调用方法的主体是抽象模板类,策略方法调用方法的主体是上下文类(一个在抽象策略类和具体策略类外部的类)

模板模式一般只针对一套算法,注重对同一个算法的不同细节进行抽象提供不同的实现。而策略模式注重多套算法多套实现,在算法中间不应该有交集,因此算法和算法之间一般不会有冗余代码!

策略模式优缺点

优点:

  • 横向扩展性好,灵活性高

缺点

  • 客户端需要知道全部策略,若策略过多会导致复杂度升高

模板模式优缺点

优点:

  • 可维护性好,纵向扩展性好

缺点

  • 耦合性较高,子类无法影响父类公用模块代码

与建造者模式的区别

参考
策略模式和建造者模式

(1)建造者模式是创建型的,也就是说用来创建对象的,而策略模式属于行为型模式,通过将行为封装成对象来降低类之间的耦合度;

(2)策略模式的抽象类仅仅定义了一个算法接口,而建造者模式的抽象类则已经定义好了算法骨架或者过程的步骤,也就是说策略模式的各具体策略在实现上可以差之千里,但是建造者模式的具体建造者必须按照接口中定义好的骨架或步骤去实现;

(3)策略模式的StrategyContext类通过提供一个上下文环境来维护具体策略;而建造者模式的Director类则是封装了Product类的创建细节,便于客户端程序调用。

UML

在这里插入图片描述

包结构

在这里插入图片描述

strategy

策略基类

1
2
3
4
5
6
package 策略模式.strategy;

public abstract class Action {
public abstract void doAction();
}

具体策略类

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
package 策略模式.strategy;

public class Walk extends Action{
@Override
public void doAction() {
System.out.println("走起路来了");
}
}
----------------------------------------------
package 策略模式.strategy;

public class Jump extends Action{
@Override
public void doAction() {
System.out.println("跳起来了");
}
}
----------------------------------------------
package 策略模式.strategy;

public class Run extends Action{
@Override
public void doAction() {
System.out.println("跑起步来了");
}
}
----------------------------------------------

上下文类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 策略模式.context;

import 策略模式.strategy.Action;

public class Context {
private Action action;

public void setAction(Action action){
this.action=action;
}

public void doAction(){
this.action.doAction();
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package 策略模式.client;

import 策略模式.context.Context;
import 策略模式.strategy.Jump;
import 策略模式.strategy.Run;
import 策略模式.strategy.Walk;

public class Client {
public static void main(String[] args) {
Context actionContext = new Context();
System.out.println("-------------------");
actionContext.setAction(new Walk());
actionContext.doAction();
System.out.println("-------------------");
actionContext.setAction(new Jump());
actionContext.doAction();
System.out.println("-------------------");
actionContext.setAction(new Run());
actionContext.doAction();
System.out.println("-------------------");
}
}

效果

1
2
3
4
5
6
7
8
9
-------------------
走起路来了
-------------------
跳起来了
-------------------
跑起步来了
-------------------

Process finished with exit code 0

优点

  1. 策略模式提供了对 “开闭原则” 的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  2. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
  3. 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式而是通过继承,这样算法的使用就和算法本身混在一起,不符合 “单一职责原则”,而且使用继承无法实现算法或行为在程序运行时的动态切换
  4. 使用策略模式可以避免多重条件选择语句。多重条件选择语句是硬编码,不易维护。
  5. 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

缺点

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
  2. 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
  3. 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

参考
设计模式 | 备忘录模式及典型应用
23 种设计模式详解(全23种)
备忘录模式 - 行为模式

备忘录模式

类型:行为型

不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。

适用场景:

  • 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
  • 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。

角色

  • 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息
  • 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改

UML

在这里插入图片描述

包结构

在这里插入图片描述

memento(一个bean,存放状态)

1
2
3
4
5
6
7
8
9
10
11
package 备忘录模式.memento;

public class Record {
private int place;
public Record(int place){
this.place=place;
}
public int getPlace(){
return this.place;
}
}

careTaker(内置一个集合,用于存放许多历史记录(即前面那个bean)),负责对于记录的存取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package 备忘录模式.careTaker;

import 备忘录模式.memento.Record;

import java.util.ArrayList;
import java.util.List;

public class CareTaker {
private List<Record> recordList = new ArrayList<>();
public void saveRecord(Record record){
recordList.add(record);
}
public Record getRecord(int idx){
return recordList.get(idx);
}
}

originator(生成记录的类,同时具有回到某一个历史记录的能力)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package 备忘录模式.originator;

import 备忘录模式.memento.Record;

public class Player {
private int place;

public Record createRecord(){
return new Record(this.place);
}

public void restore(Record record){
this.place=record.getPlace();
}

public int getPlace() {
return this.place;
}

public void setPlace(int place) {
this.place = place;
}
}

客户端(我这里把他作为一场游戏的进行了)

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
package 备忘录模式.client;

import 备忘录模式.careTaker.CareTaker;
import 备忘录模式.originator.Player;

public class Game {
public static void main(String[] args) {
Player player = new Player();
CareTaker careTaker = new CareTaker();
//玩家走到1位置
player.setPlace(1);
careTaker.saveRecord(player.createRecord());
//玩家走到2位置
player.setPlace(2);
careTaker.saveRecord(player.createRecord());
//玩家走到3位置
player.setPlace(3);
careTaker.saveRecord(player.createRecord());
//当前玩家到达的位置
System.out.println("玩家到达了"+player.getPlace());
//玩家想回到第一次走到的位置
player.restore(careTaker.getRecord(0));
System.out.println("玩家回到了"+player.getPlace());
}
}

效果

1
2
3
4
玩家到达了3
玩家回到了1

Process finished with exit code 0

优点

  • 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
  • 备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。

缺点

  • 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

参考
设计模式(五)观察者模式

观察者模式

类型:行为型

定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己

UML

在这里插入图片描述

角色

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知

  • Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己

  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

包结构

在这里插入图片描述

subject

接口

1
2
3
4
5
6
7
8
9
package 观察者模式.subject;

import 观察者模式.observer.Student;

public interface Subject{
void addStu(Student student);
void deleteStu(Student student);
void notify(String msg);
}

具体子类

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
package 观察者模式.subject;

import 观察者模式.observer.Student;

import java.util.LinkedList;
import java.util.List;

public class Teacher implements Subject{
private List<Student> studentList = new LinkedList<>();

@Override
public void addStu(Student student) {
studentList.add(student);
}

@Override
public void deleteStu(Student student) {
studentList.remove(student);
}

@Override
public void notify(String msg) {
for(Student student:studentList){
student.remind(msg);
}
}
}

observer

接口

1
2
3
4
5
package 观察者模式.observer;

public interface Observer {
void remind(String msg);
}

具体子类

1
2
3
4
5
6
7
8
9
10
11
12
package 观察者模式.observer;

public class Student implements Observer{
private String name;
public Student (String name){
this.name=name;
}
@Override
public void remind(String msg) {
System.out.println(name+"被告知消息:"+msg);
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package 观察者模式.client;

import 观察者模式.observer.Student;
import 观察者模式.subject.Teacher;

public class Client {
public static void main(String[] args) {
Teacher teacher = new Teacher();
System.out.println("-------------------------------");
Student zhang =new Student("张三");
teacher.addStu(zhang);
teacher.notify("张三加入我们班了");
System.out.println("-------------------------------");
Student li =new Student("李四");
teacher.addStu(li);
teacher.notify("李四来我们班了");
System.out.println("-------------------------------");
teacher.notify("老师要抽人回答问题了");
System.out.println("-------------------------------");
teacher.deleteStu(li);
teacher.notify("李四退出我们班了");
System.out.println("-------------------------------");
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
-------------------------------
张三被告知消息:张三加入我们班了
-------------------------------
张三被告知消息:李四来我们班了
李四被告知消息:李四来我们班了
-------------------------------
张三被告知消息:老师要抽人回答问题了
李四被告知消息:老师要抽人回答问题了
-------------------------------
张三被告知消息:李四退出我们班了
-------------------------------

Process finished with exit code 0

优点

解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。

缺点

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现

参考
JAVA设计模式之单例模式

单例模式

类型:创建型

确保一个类最多只有一个实例,并提供一个全局访问点
单例模式有以下特点:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

采取的措施:
通过将构造方法限定为private避免了类在外部被实例化(暂且当做没有反射机制)
在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问

饿汉型

相对而言要考虑的较少,因为他的机制使他天生就是线程安全的,故放在第一个

1
2
3
4
5
6
7
8
9
10
11
12
package 单例模式.singleton;

public class PreloadSingleton {
public static PreloadSingleton instance = new PreloadSingleton();

//避免了类在外部被实例化
private PreloadSingleton() { };

public static PreloadSingleton getInstance() {
return instance;
}
}

懒汉型

版本1.0

最基本的样子,当第一次调用getInstance方法时才new实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 单例模式.singleton;

public class LazyLoadSingleton {
private static LazyLoadSingleton instance=null;

private LazyLoadSingleton(){ }

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

但是它是线程不安全的,并发环境下很可能出现多个LazyLoadSingleton实例

版本2.0

使用线程同步,保证线程安全
但是,如果要经常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和阻塞线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 单例模式.singleton;

public class LazyLoadSingleton {
private static LazyLoadSingleton instance=null;

private LazyLoadSingleton(){ }

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

版本3.0

把sychronized加在if(instance==null)判断语句里面,保证instance未实例化的时候才加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package 单例模式.singleton;

public class LazyLoadSingleton {
private static LazyLoadSingleton instance=null;

private LazyLoadSingleton(){ }

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

终极版本

new 一个对象是有代码执行顺序的,而并发环境下是无法保证顺序性的
因此,我们需要使用另一个关键字volatile保证对象实例化过程的顺序性。

memory=allocate();//1:初始化内存空间
ctorInstance(memory);//2:初始化对象
instance=memory();//3:设置instance指向刚分配的内存地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package 单例模式.singleton;

public class LazyLoadSingleton {
private static volatile LazyLoadSingleton instance=null;

private LazyLoadSingleton(){ }

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

总结

线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,这三种实现在资源加载和性能方面有些区别。

资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

迭代器模式

类型:行为类

提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。

UML

在这里插入图片描述

角色

  • 抽象容器:一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
  • 具体容器:就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
  • 抽象迭代器:定义遍历元素所需要的方法,一般来说会有这么三个方法:取得第一个元素的方法first(),取得下一个元素的方法next(),判断是否遍历结束的方法isDone()(或者叫hasNext()),移出当前对象的方法remove(),
  • 迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代。

代码

这个感觉没啥好说的,用过集合的人应该都有所体会,迭代器关键就在于他实现了对内部结构的遍历算法,使内部结构对用户隐藏,对外部只暴露方法供调用
这里只是使用了一下迭代器,体现他的作用

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
package 迭代器模式;

import java.util.*;

public class Client {
public static void main(String[] args) {
System.out.println("--------------List集合的遍历");
List<String> list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
Iterator<String> iterator1 = list.listIterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}
System.out.println("-------------Map集合的遍历");
HashMap<String,String> map = new HashMap<>();
map.put("张三","三");
map.put("李四","四");
map.put("王五","五");
Iterator<Map.Entry<String,String>> iterator2 = map.entrySet().iterator();//返回所有的entry实体

System.out.println("------------遍历键值对");
while (iterator2.hasNext()) {
Map.Entry<String, String> next1 = iterator2.next();
String key = next1.getKey();
String value = next1.getValue();
System.out.println(key+" "+value);
}
System.out.println("------------遍历键");
Iterator iterator3 = map.keySet().iterator();
while (iterator3.hasNext()) {
System.out.println(iterator3.next());
}
System.out.println("------------遍历值");
Iterator iterator4 = map.values().iterator();
while (iterator4.hasNext()) {
System.out.println(iterator4.next());
}
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--------------List集合的遍历
张三
李四
王五
赵六
-------------Map集合的遍历
------------遍历键值对
李四 四
张三 三
王五 五
------------遍历键
李四
张三
王五
------------遍历值




Process finished with exit code 0

优点

  • 简化了遍历方式
  • 可以提供多种遍历方式,比如提供正序遍历和倒序遍历,用户只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
  • 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心

缺点

对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。

参考
桥接模式(Bridge Pattern)-(最通俗易懂的案例)

桥接模式

通过使用封装、聚合及继承等行为让不同的类承担不同的职责。
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
简单而言就是对于每一种特点,抽象成一个类

角色

  • Client 类:桥接模式的调用者
  • 抽象类(Abstraction) :维护了 Implementor / 即它的实现类 ConcreteImplementorA…, 二者是聚合关系, Abstraction充当桥接类
  • RefinedAbstraction : 是 Abstraction 抽象类的子类
  • Implementor : 行为实现类的接口
  • ConcreteImplementorA /B :行为的具体实现类
    从 UML 图:这里的抽象类和接口是聚合的关系,是调用和被调用关系

UML

在这里插入图片描述

包结构

在这里插入图片描述

shape

抽象类

1
2
3
4
5
6
7
8
9
10
11
package 桥接模式.shape;

import 桥接模式.color.Color;

public abstract class Shape {
protected Color color;
protected String shape;
public abstract String getShape();
public abstract void setColor(Color color);
public abstract Color getColor();
}

具体

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
package 桥接模式.shape;

import 桥接模式.color.Color;

public class Circle extends Shape{
public Circle(){
super.shape="圆形";
}

@Override
public String getShape() {
return super.shape;
}

@Override
public void setColor(Color color) {
super.color = color;
}

@Override
public Color getColor() {
return super.color;
}
}
-----------------------------------------------------
package 桥接模式.shape;

import 桥接模式.color.Color;

public class Rectangle extends Shape{
public Rectangle(){
super.shape="长方形";
}

@Override
public String getShape() {
return super.shape;
}

@Override
public void setColor(Color color) {
super.color=color;
}

@Override
public Color getColor() {
return super.color;
}
}
-----------------------------------------------------

color

接口

1
2
3
4
5
package 桥接模式.color;

public interface Color {
String getColor();
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package 桥接模式.color;

public class Green implements Color{
private String color = "绿色";
@Override
public String getColor() {
return this.color;
}
}
----------------------------------------------------------
package 桥接模式.color;

public class Red implements Color{
private String color = "红色";
@Override
public String getColor() {
return this.color;
}
}
----------------------------------------------------------

优点:

  • 实现抽象和实现的分离
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统
  • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法

缺点:

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化维度,因此其使用范围具有一定的局限性。

参考
设计模式入门——中介者模式(mediator)
23种设计模式(7):中介者模式

中介者模式

类型:行为型

在这里插入图片描述
这个中介者负责与对象之间联系,对象与对象之间不再进行直接的交互,也就是对对象关系进行解耦

角色

  • 抽象中介者(mediator):定义一个接口用于和对象通信(SmartDevice)
  • 具体中介者(concretemediator):协调各同事对象实现协作,了解维护各个同事()
  • 抽象同事角色(colleague):规定了同事的基本类型
  • 具体同事角色(concreteColleague):每个同事都知道中介者对象,要与同事通信则把通信告诉中介者

UML

在这里插入图片描述

包结构

在这里插入图片描述

同事类

基类

1
2
3
4
5
6
7
8
9
10
11
12
13
package 中介者模式.colleague;

public abstract class AbstractColleague {
protected String book;

public String getBook() {
return book;
}

public void setBook(String book) {
this.book = book;
}
}

具体子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package 中介者模式.colleague;

import 中介者模式.mediator.AbstractMediator;

public class Wang extends AbstractColleague{
public void giveBookToZhang(AbstractMediator mediator){
System.out.println("老王将书"+book+"给了中介者");
mediator.wangGiveZhang(super.book);
}
}
---------------------------------------------------------------
package 中介者模式.colleague;

import 中介者模式.mediator.AbstractMediator;

public class Zhang extends AbstractColleague{
public void giveBookToWang(AbstractMediator mediator){
System.out.println("老张将书"+book+"给了中介者");
mediator.zhangGiveWang(super.book);
}
}

中介者

基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package 中介者模式.mediator;

import 中介者模式.colleague.AbstractColleague;

public abstract class AbstractMediator {
protected AbstractColleague wang;
protected AbstractColleague zhang;

public AbstractMediator(AbstractColleague wang, AbstractColleague zhang) {
this.wang = wang;
this.zhang = zhang;
}

public abstract void wangGiveZhang(String book);
public abstract void zhangGiveWang(String book);
}

具体子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package 中介者模式.mediator;

import 中介者模式.colleague.AbstractColleague;

public class Mediator extends AbstractMediator {

public Mediator(AbstractColleague wang, AbstractColleague zhang) {
super(wang, zhang);
}

@Override
public void wangGiveZhang(String book) {
System.out.println("中介者得到了老王给来的书"+book+"并交给了老张");
super.zhang.setBook(book);
}

@Override
public void zhangGiveWang(String book) {
System.out.println("中介者得到了老张给来的书"+book+"并交给了老王");
super.wang.setBook(book);
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package 中介者模式.client;

import 中介者模式.colleague.AbstractColleague;
import 中介者模式.colleague.Wang;
import 中介者模式.colleague.Zhang;
import 中介者模式.mediator.AbstractMediator;
import 中介者模式.mediator.Mediator;

public class Client {
public static void main(String[] args) {
Wang wang = new Wang();
Zhang zhang = new Zhang();
AbstractMediator mediator = new Mediator(wang,zhang);
System.out.println("--------先让老王获得书--------");
wang.setBook("《假如我有三天光明》");
System.out.println("---------再让老王把书给老张---------");
wang.giveBookToZhang(mediator);
System.out.println("-----------查看老张手上的书-----------");
System.out.println(zhang.getBook());
}
}

效果

1
2
3
4
5
6
7
8
--------先让老王获得书--------
---------再让老王把书给老张---------
老王将书《假如我有三天光明》给了中介者
中介者得到了老王给来的书《假如我有三天光明》并交给了老张
-----------查看老张手上的书-----------
《假如我有三天光明》

Process finished with exit code 0

模式优缺点

中介者模式很容易在系统中应用,也很容易在系统中误用,当系统出现了‘多对多’交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。

优点:

  • Mediator的出现减少了各个Colleague的耦合,使得可以独立的改变复用各个ColleagueMediator;
  • 由于把对象如何协作进行了抽象,将中介者作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到了他们之间的交互上来,也就是站在一个更宏观的角度去看待系统

缺点

  • 由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂

参考
23种设计模式(14):解释器模式
设计模式(二十)解释器模式

解释器模式(据说用的非常少,我就没怎么研究了)

类型:行为型

给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。

角色

  • 抽象解释器(AbstractExpression):具体的解释任务由各个实现类完成。
  • 终结符表达式(TerminalExpression):实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结表达式,但有多个实例,对应不同的终结符。
  • 非终结符表达式(NonterminalExpression):文法中的每条规则对应于一个非终结表达式,非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式
  • 上下文(Context): 上下文环境类,包含解释器之外的全局信息
  • 客户类(Client): 客户端,解析表达式,构建抽象语法树,执行具体的解释操作等.

UML图

在这里插入图片描述

代码

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
class Context {}
abstract class Expression {
public abstract Object interpreter(Context ctx);
}

class TerminalExpression extends Expression {
public Object interpreter(Context ctx){
return null;
}
}

class NonterminalExpression extends Expression {
public NonterminalExpression(Expression...expressions){

}
public Object interpreter(Context ctx){
return null;
}
}

public class Client {
public static void main(String[] args){
String expression = "";
char[] charArray = expression.toCharArray();
Context ctx = new Context();
Stack<Expression> stack = new Stack<Expression>();
for(int i=0;i<charArray.length;i++){
//进行语法判断,递归调用
}
Expression exp = stack.pop();
exp.interpreter(ctx);
}
}

文法递归的代码部分需要根据具体的情况来实现,因此在代码中没有体现。抽象表达式是生成语法集合的关键,每个非终结符表达式解释一个最小的语法单元,然后通过递归的方式将这些语法单元组合成完整的文法,这就是解释器模式。

优点

  1. 扩展性强,若要新增乘,除,添加相应的非终结表达式,修改计算逻辑即可。

缺点

  1. 需要建大量的类,因为每一种语法都要建一个非终结符的类。
  2. 解释的时候采用递归调用方法,导致有时候函数的深度会很深,影响效率。

参考
23种设计模式(9):访问者模式

访问者模式

类型:行为类

角色

  • 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
  • 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情
  • 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
  • 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
  • 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。

UML

在这里插入图片描述

适用场景

  • 假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。
  • 假如一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去。

包结构

在这里插入图片描述

访问者

接口

1
2
3
4
5
6
7
8
9
package 访问者模式.visitor;

import 访问者模式.element.ConcreteElement1;
import 访问者模式.element.ConcreteElement2;

public interface IVisitor {
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package 访问者模式.visitor;

import 访问者模式.element.ConcreteElement1;
import 访问者模式.element.ConcreteElement2;

public class Visitor implements IVisitor {

@Override
public void visit(ConcreteElement1 el1) {
el1.doSomething();
}

@Override
public void visit(ConcreteElement2 el2) {
el2.doSomething();
}
}

元素

基类

1
2
3
4
5
6
7
8
package 访问者模式.element;

import 访问者模式.visitor.IVisitor;

public abstract class Element {
public abstract void accept(IVisitor visitor);
public abstract void doSomething();
}

具体类

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
package 访问者模式.element;

import 访问者模式.visitor.IVisitor;

public class ConcreteElement1 extends Element {
@Override
public void doSomething(){
System.out.println("这是元素1");
}

@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
--------------------------------------------------------------
package 访问者模式.element;

import 访问者模式.visitor.IVisitor;

public class ConcreteElement2 extends Element {
@Override
public void doSomething(){
System.out.println("这是元素2");
}

@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}

结构对象

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
package 访问者模式.objectStructure;

import 访问者模式.element.ConcreteElement1;
import 访问者模式.element.ConcreteElement2;
import 访问者模式.element.Element;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ObjectStructure {
public static List<Element> getList(){
List<Element> list = new ArrayList<>();
Random ran = new Random();
for(int i=0; i<10; i++){
int a = ran.nextInt(100);
if(a>50){
list.add(new ConcreteElement1());
}else{
list.add(new ConcreteElement2());
}
}
return list;
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package 访问者模式.client;

import 访问者模式.objectStructure.ObjectStructure;
import 访问者模式.visitor.Visitor;
import 访问者模式.element.Element;

import java.util.List;

public class Client {
public static void main(String[] args){
List<Element> list = ObjectStructure.getList();
for(Element e: list){
e.accept(new Visitor());
}
}
}

效果

1
2
3
4
5
6
7
8
9
10
11
12
这是元素2
这是元素1
这是元素1
这是元素2
这是元素2
这是元素2
这是元素1
这是元素2
这是元素1
这是元素2

Process finished with exit code 0

优点

  • 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
  • 扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。

缺点

  • 增加新的元素类比较困难,每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数量不确定的情况下,应该慎用访问者模式。

参考博客
设计模式之状态模式(State)

状态模式

类型:行为型

一个拥有状态的context对象,在不同的状态下,其行为会发生改变。

角色

  • 环境角色(Context):客户程序需要的接口,并且维护一个具体状态的实例,这个实例决定当前状态。
  • 状态角色(State):定义一个接口以封装与使用环境角色的一个特定状态的相关行为。
  • 具体状态角色(ConcreteState):实现状态角色定义的接口,结构十分简单与策略模式相似。

UML

在这里插入图片描述

包结构

在这里插入图片描述

state

基类

1
2
3
4
5
package 状态模式.state;

public interface State {
void handle();
}

子类

都类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package 状态模式.state;

public class Sended implements State {
@Override
public void handle() {
System.out.println("已发货!");
}
}
-------------------------------------------
package 状态模式.state;

public class Recieved implements State {
@Override
public void handle() {
System.out.println("已确认收获!");
}
}

context(环境)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 状态模式.context;

import 状态模式.state.State;

public class Context {
private State state;

public Context() {}

public Context(State state) {
this.state = state;
}

public void setState(State state) {
System.out.println("订单信息已更新!");
this.state = state;
this.state.handle();
}
}

client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 状态模式.client;

import 状态模式.context.Context;
import 状态模式.state.*;

public class Client {
public static void main(String [] args) {
Context context = new Context();
context.setState(new Booked());
context.setState(new Payed());
context.setState(new Sended());
context.setState(new InWay());
context.setState(new Recieved());
}
}

优点

  • 应用状态模式使程序扩展起来变的简单
  • 避免了多重条件语句的应用
  • 程序结构会变得比较清晰

缺点

  • 应用状态模式使类变的过多,而且简单的关系会变的理解困难
  • 没有很好的遵守开闭原则,引入新的新的状态会导致原有状态的修改
  • 没有很好的处理耦合关系,从例子中可以看出,应用状态模式不仅会使状态类和环境类进行关联而且状态类之间也有关系存在

参考博客
秒懂设计模式之享元模式(Flyweight Pattern)
深入理解享元模式

享元模式

类型:结构型

享元模式通过共享技术实现相同或相似对象的重用,例子可参考常量池

在享元模式中可以共享的相同内容称为 内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为 外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。

由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。也就是说,享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。

在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)(用于存储具有相同内部状态的享元对象)。
在享元模式中,共享的是享元对象的内部状态,外部状态需要通过环境来设置。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为细粒度对象
享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。

角色

  • Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态;
  • ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
  • UnsharedConcreteFlyweight: 非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象
  • FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;

UML

在这里插入图片描述

包结构

享元类

接口

1
2
3
4
5
package 享元模式.flyWeight;

public interface AbstractColor {
void doOperation(int r,int c);
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
package 享元模式.flyWeight;

public class Color implements AbstractColor{
private String colorName;
@Override
public void doOperation(int r, int c) {
System.out.println("将颜色:"+this.colorName+"画在("+r+","+c+")上");
System.out.println("该颜色的hashcode为"+this.hashCode());
}

public Color(String colorName){this.colorName=colorName;}
}

生产享元的工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 享元模式.flyWeightFactory;

import 享元模式.flyWeight.AbstractColor;
import 享元模式.flyWeight.Color;

import java.util.HashMap;

public class ColorPool {
private HashMap<String, AbstractColor> hashMap = new HashMap<>();

public AbstractColor get(String colorName){
AbstractColor color = hashMap.get(colorName);
if(color==null){
color = new Color(colorName);
hashMap.put(colorName,color);
}
return color;
}
}

客户端测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 享元模式.client;

import 享元模式.flyWeightFactory.ColorPool;

public class Client {
public static void main(String[] args) {
ColorPool colorPool= new ColorPool();
colorPool.get("绿色").doOperation(1,2);
colorPool.get("绿色").doOperation(7,10);
colorPool.get("红色").doOperation(3,4);
colorPool.get("红色").doOperation(10,90);
}
}

效果

可以发现虽然获得了四个享元,但实际上只有两种hashcode,即只有两个不同对象
并且对于同一种对象,进行了不同的操作

1
2
3
4
5
6
7
8
9
10
将颜色:绿色画在(1,2)上
该颜色的hashcode为356573597
将颜色:绿色画在(7,10)上
该颜色的hashcode为356573597
将颜色:红色画在(3,4)上
该颜色的hashcode为1735600054
将颜色:红色画在(10,90)上
该颜色的hashcode为1735600054

Process finished with exit code 0

优点

  • 它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份;
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化;
  • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长

参考博客
代理模式的使用总结
23 种设计模式详解(全23种)

代理模式

类型:结构型

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

为什么要用代理模式?

  • 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口

  • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。通过类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

UML

在这里插入图片描述

代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理

静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
##

包结构

在这里插入图片描述

接口

1
2
3
4
5
package 代理模式;

public interface Person {
void buyHouse();
}

委托类(客户)

1
2
3
4
5
6
7
8
9
package 代理模式;

public class BuyHousePerson implements Person{

@Override
public void buyHouse() {
System.out.println("我要买一间.....的屋子,我的预算是....元");
}
}

代理类(中介)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 代理模式;

public class BuyHouseProxy implements Person{
private BuyHousePerson principal;
public BuyHouseProxy(BuyHousePerson principal){
System.out.println("我太忙了,于是找了这家中介");
this.principal=principal;
}
@Override
public void buyHouse() {
System.out.println("我是中介,我来替用户挑房子");
this.principal.buyHouse();
}
}

客户端测试

1
2
3
4
5
6
7
8
package 代理模式;

public class Client {
public static void main(String[] args) {
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(new BuyHousePerson());
buyHouseProxy.buyHouse();
}
}

效果

1
2
3
4
5
我太忙了,于是找了这家中介
我是中介,我来替用户挑房子
我要买一间.....的屋子,我的预算是....元

Process finished with exit code 0

优点:

可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:

代理对象与目标对象要实现相同的接口,我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改

动态代理

代理类在程序运行时创建的代理方式被成为动态代理。

动态代理有以下特点:

  1. 代理对象,不需要实现接口
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

代理类不用再实现接口了。但是,要求被代理对象必须有接口。

动态代理实现:

Java.lang.reflect.Proxy类可以直接生成一个代理对象

1
2
//生成一个代理对象
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • 参数1:ClassLoader loader 代理对象的类加载器 一般使用被代理对象的类加载器

  • 参数2:Class<?>[] interfaces 代理对象的要实现的接口 一般使用的被代理对象实现的接口

  • 参数3:InvocationHandler h (接口)执行处理类

1
2
//调用代理类的任何方法,此方法都会执行
InvocationHandler中的 invoke(Object proxy, Method method, Object[] args)
  • 参数3.1:代理对象(慎用)

  • 参数3.2:当前执行的方法

  • 参数3.3:当前执行的方法运行时传递过来的参数

第一步:编写动态处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}

第二步:编写测试类

1
2
3
4
5
6
7
8
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}

动态代理总结:

虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏(我们要使用被代理的对象的接口),因为它的设计注定了这个遗憾。

CGLIB代理

待续

创建型:对于对象的实例化进行解耦

单例

有且只能有一个实例,且全局可访问。注意懒汉(提前实例化但是浪费内存)和饿汉(延后实例化但是要处理线程安全问题)的区别。

三个工厂(只有后两个属于GOF23种设计模式)

简单工厂

  • 实现:
    定义一个产品接口和诸多实现了该接口的具体产品类
    定义一个工厂类,内含一个生产产品的方法,通过传入的参数来判断实例化哪种产品并返回。

    即只有一个工厂,内置多个生产方法(或者多个if else)

  • 使用案例:
    想获得加法操作对象,就向工厂类的生产产品方法传入字符+,返回new AddOperation()

工厂方法

  • 实现
    也定义一个产品接口和诸多实现了该接口的具体产品类
    注意这里,定义的是工厂接口和诸多实现了该接口的具体工厂类,每个具体工厂类都有对应的生产产品的方法

    即有多个工厂,每个工厂都生产对应的产品

  • 使用案例
    想获得加法操作对象,就调用AddFactorygetOperation方法,返回new AddOperation()

抽象工厂

  • 实现
    相较于工厂方法,为了减少工厂的数量,让每个工厂都可以生产多种产品。
    如小米工厂生产小米手机和小米电脑、华为工厂生产华为手机和华为电脑。
  • 使用案例
    新建小米工厂,调用mi.getPhone获得小米手机,调用mi.getComputer获得小米电脑。华为同理。

建造者

  • 实现
    有零件类、工人类、机器类
    其实就是工人类封装了组装零件的细节,直接返回了机器对象
  • 对比
    - 与工厂模式:工厂模式注重于对新建对象方式的解耦,其新建对象是一步完成的。而建造者模式是需要组装多个部件才能新建完成一个对象的,注重于这个组装的过程及组装顺序。

原型

  • 实现
    其实就是使用了对象的拷贝取代了new,因为二者在效率上是有差异的。

结构型:

适配器

  • 实现
    参考变压器、编译器等。变压器类继承220V电压类、实现变压器接口。5V变压器重写变压方法为转换为5V,10V变压器重写为转换为10V。

桥接

感觉图片会好理解一点
原本的继承关系,不同维度的属性通过层层继承实现
在这里插入图片描述
独立各维度的属性后如下
在这里插入图片描述

组合

  • 实现
    定义叶节点类和非叶节点类,他们都继承节点抽象类,将数据以树形方式构建起来

装饰者

  • 实现
    其实就是把被装饰的东西注入到有装饰方法的对象里,层层包装
  • 使用案例
    1
    2
    3
    4
    5
    奶茶 milkTea = new 奶茶()
    糖添加者 sugar = new 糖添加者(milkTea)
    芋圆添加者 yuYuan = new 芋圆添加者(sugar)
    布丁添加者 buDing = new 布丁添加者(yuYuan)
    sout(buDing)//加了糖、芋圆、布丁的奶茶

外观

  • 实现
    封装内部复杂细节(关乎到多个类、多个系统的”复杂”,而不只是过程的复杂),对外提供简单接口

享元

  • 实现
    享元即共享的元素,参考Java的常量池,将常用的对象用池子保存并管理,多次调用只会获得同一个对象。

代理

  • 实现
    联想买房的场景,将买房者对象注入中介对象的属性中,让中介提出买房者的要求,接收卖房者的反馈,起到保护买房者的作用。

行为型

访问者TODO

没想明白,先放篇感觉可以的、别人的博客 设计模式之访问者模式

模板

  • 实现
    思想上就是面向对象的继承,使用抽象类和子类继承实现就行

策略

  • 实现
    其实就是将switch-case的各个case用多种策略类表示,这些策略类继承于一个策略基类,以方便修改,即替换对象而不是更改方法细节。

状态

  • 实现
    如果类有状态属性的话,就可以让多种状态以多种状态类表示,对象变换状态就是注入新状态。

观察者

  • 实现
    以师生为例,老师为被观察者,学生为观察者。老师有一个存储学生的集合,老师和学生都有一个进行操作的方法。老师进行操作时,还会调用集合内学生的操作方法。这样子看起来就是”老师一动,学生就动了”。

备忘录

  • 实现
    在正常业务的基础上,定义记录类和记录者类,业务对象新增读取记录方法。在客户端,对于业务对象的每次操作,记录者类根据当前状态创建记录并进行存储和管理,业务对象想读取历史记录时,调用自己的读取记录方法,传入记录者类返回的记录对象即可。

中介者

  • 实现
    参考消息队列。

迭代器

  • 实现
    参考Java、C++的迭代器,即各集合提供统一接口、封装复杂细节

命令

  • 实现
    有命令发送者、命令接收者、命令三个类
    命令对象保存了接收者对象,并有一个成员方法(例如excute)是调用了接收者处理该命令的方法
    发送者只负责调用excute
    这样子就保证了发送者和接收者的解耦,因为发送者无需知道接收者是谁,接收者是在新建命令对象时存储在命令中的,并且是由命令主动去让接收者使用自己的

责任链

  • 实现
    联想到过滤器这东西,一层一层更密的筛子对混合物进行过滤。在代码实现上,每层过滤器都是一个类,且他们都保存了下一层过滤器的引用,以方便在自己这里过滤不了时将混合物送往下一层过滤器进行过滤。而混合物只需调用一次过滤操作就行,过滤的过程由过滤器们联动实现。

解释器(用得少、难理解、遂不概括)

收集了一下设计模式常见优缺点(自行对号入座吧)

优点

  • 提高可复用性
  • 提高可维护性
  • 降低耦合度
  • 降低代码复杂性
  • 提高可读性
  • 提高可扩展性
  • 增强健壮性

缺点

  • 类的数量增多,增加复杂性
  • 系统更抽象,理解难度增加
  • 修改成本增大
  • 扩展困难
  • 容易引入风险
  • 请求处理速度变慢
  • 客户端必须知晓细节并自行决策
  • 增加了资源占用,如内存等
  • 标题: 设计模式学习
  • 作者: urlyy
  • 创建于 : 2021-09-07 12:13:29
  • 更新于 : 2023-06-19 13:13:39
  • 链接: https://urlyy.github.io/2021/09/07/设计模式学习/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
设计模式学习