7大设计原则+23种设计模式(Java代码版本)

设计原则

具体知识

  • 单一职责原则

    一个类=只有一个引起它变化的原因。(只负责担任一个职责

    如果一个类的职责过多,即耦合度太高=一个职责变化会影响到其他的职责

  • 开放封闭原则

    一个实体(类、函数、模块等)应该对外扩展开放,对内修改关闭(这个对应下面的第一点)

    1、即每次发生变化时,要通过添加新的代码来增强现有类型的行为,而不是修改原有的代码。
    2、符合开放封闭原则的最好方式是提供一个固有的接口,然后让所有可能发生变化的类实现该接口,让固定的接口与相关对象进行交互。

  • 里氏代替原则

    子类必须替换掉它们的父类型

    1、在软件开发过程中,子类替换父类后,程序的行为是一样的。
    2、只有当子类替换掉父类后软件的功能不受影响时,父类才能真正地被复用,而子类也可以在父类的基础上添加新的行为。

  • 依赖倒置原则

    细节应该依赖于抽象,而抽象不应该依赖于细节。(高层模块不应该依赖于底层模块,依赖关系应当是面向接口或抽象类,而不是具体实现类)
    依赖倒置的本质原则就是 :通过抽象(接口或抽象类)使各个类或模块实现彼此独立,互不影响,实现模块间的松耦合。

    • 每个类尽量都要有接口或抽象类,或者抽象类和接口两者都具备。
    • 变量的显示类型尽量是接口或者抽象类。
    • 任何类尽量不从具体类派生。
    • 尽量不要覆写基类的方法
    • 结合里氏替换原则。 父类出现的地方子类就能出现

    所谓的的 “面向接口编程,而不是面向实现编程”。这样可以降低客户与具体实现的耦合。

  • 接口隔离原则

    使用多个专门功能的接口,而不是使用单一的总接口。

    不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。

  • 最少知识原则(迪米特法则)

    一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

    1、关于迪米特法则的其他描述:只与你直接的朋友们通信;不要跟“陌生人”说话。(调用直接关联的类的方法,比如一个类里面关联另外的类,不应该再调用这个类关联的另外的类)
    2、外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

  • 合成复用原则

    在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。

    新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。

其实还是符合那句在计算机领域很多事情加多一层就能解决问题,例如既然耦合就加层接口

例子

单一职责原则

这个是最简单的,就不放例子了,尽量让类单一实现自己的职责就好了

开闭原则

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
package design_principles;

public class DrawingBoard {

/*
* 假设这里有个地方代码要调用这个类画圆,后面想画一个别的图案,就需要改画板类
* 但是呢,如果是更好的写法是不动已有的代码,扩展会更好*/
public static void main(String[] args) {
DrawingBoard drawingBoard = new DrawingBoard(new Circle(10));
drawingBoard.draw();
}

private Circle circle;

public DrawingBoard(Circle circle) {
this.circle = circle;
}

public void draw() {
System.out.println("画一个半径为" + circle.getRadius() + "的圆");
}
}

class Circle {
private int radius;

public Circle(int radius) {
this.radius = radius;
}

public int getRadius() {
return radius;
}

public void setRadius(int radius) {
this.radius = radius;
}
}

优化

当然也可以继承,然后重写新出一个类,替换就好,但是全都是继承也不太好

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
package design_principles.optimize;

/*
* 优化过后的确实结构更好,如果要改变类不用动原本写好的代码,写个新的图像类传进去就好
* */
public class NewDrawingBoard {

private Shape shape;

public NewDrawingBoard(Shape shape) {
this.shape = shape;
}

public void draw() {
shape.draw();
}
}


class Circle implements Shape {

private int radius;

public Circle(int radius) {
this.radius = radius;
}

@Override
public void draw() {
System.out.println("画一个半径为" + radius + "的圆");
}
}

//要画别的,加个新类
class Square implements Shape {

private int edge;

public Square(int edge) {
this.edge = edge;
}

@Override
public void draw() {
System.out.println("画一个边长为" + edge + "的方形");
}
}

interface Shape {
void draw();
}

class Test {
public static void main(String[] args) {
//NewDrawingBoard board = new NewDrawingBoard(new Circle(10));
NewDrawingBoard board = new NewDrawingBoard(new Square(10));
board.draw();
}
}

里氏替换原则

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 Rectangle {
protected int width;
protected int height;

public void setWidth(int width) {
this.width = width;
}

public void setHeight(int height) {
this.height = height;
}

public int getArea() {
return width * height;
}
}

// 子类:正方形
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width;
}

@Override
public void setHeight(int height) {
this.height = height;
this.width = height;
}
}

这个设计就可能有潜在的问题,正方形的宽和高应该一定是相等的,但这里的设计我可以让宽为5,长为10

优化,依赖于组合而不是继承,也就是最后一个原则,单独实现一个正方形类就好

1
2
3
4
5
6
7
8
9
10
11
12
13
// 正方形类独立定义,而不是继承矩形
public class Square {
private int side;

public void setSide(int side) {
this.side = side;
}

public int getArea() {
return side * side;
}
}

依赖导致原则

这个其实在开闭原则也可以看到,这两个原则经常一起使用

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
package design_principles.dependence_inversion_principle.origin;

public class EmailService {
public void sendMessage(String message){

}
}

class SmsService{
public void sendMessage(String message){

}
}

//这个要是要变短信就很难搞了,继承重写就不太好了,因为跟具体的实现功能的类的关系为具体实现了
class UserNotification{

private EmailService service;

public UserNotification(EmailService service) {
this.service = service;
}

public void send(String s){
service.sendMessage(s);
}
}

优化

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 design_principles.dependence_inversion_principle.optimize;

public interface MessageService {
void senMessage(String message);
}

class EmailService implements MessageService{

@Override
public void senMessage(String message) {
//发送邮件
}
}

class SmsService implements MessageService{

@Override
public void senMessage(String message) {
//发送短息
}
}

//给用户发送信息的类
//通过参数传递具体的实现类进来,给用户发送信息的这个类跟具体服务类的关系之间就在这里用MessageService接口关联了
//而不是直接将属性指定为具体的类
class UserNotification{
private MessageService messageService;

public UserNotification(MessageService messageService) {
this.messageService = messageService;
}

//发送
public void sendMessage(String message){
messageService.senMessage(message);
}
}

接口隔离原则

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 design_principles.interface_isolation_principle.origin;

/**
* 接口隔离的目的是让每个接口尽量精简,并且让接口和每个类紧紧关联,而不是设计一个“胖接口”让多个实现类共同依赖
* 这样还让代码更加精简了
*/
public interface Animal {
void eat();
void sleep();
void fly();
}

class Dog implements Animal{
@Override
public void eat() {

}

@Override
public void sleep() {

}

/**
* 这就不符合接口隔离原则了,因为狗不会fly,但是实现了这个接口就要实现fly方法
*/
@Override
public void fly() {

}
}

优化

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
package design_principles.interface_isolation_principle.optimize;

//能吃能睡,但是不能飞
public class Dog implements Eater, Sleeper {
@Override
public void eat() {

}

@Override
public void sleep() {

}
}


//能吃能睡能飞
class Bird implements Eater, Sleeper, Flyer {

@Override
public void eat() {

}

@Override
public void sleep() {

}

@Override
public void fly() {

}
}

interface Eater {
void eat();
}

interface Sleeper {
void sleep();
}

interface Flyer {
void fly();
}

迪米特法则

最少知道原则,知道调用某个方法能实现就行,而不是知道他有什么属性,调用属性的方法实现

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 Student {
private String name;

public String getName() {
return name;
}
}

public class Teacher {
private List<Student> students;

public List<Student> getStudents() {
return students;
}
}

public class School {
private List<Teacher> teachers;

public List<Teacher> getTeachers() {
return teachers;
}
}

public void printStudentNames(School school) {
for (Teacher teacher : school.getTeachers()) {
for (Student student : teacher.getStudents()) {
System.out.println(student.getName());
}
}
}

上面不符合迪米特法则,直接去获取属性了,应该提供一个方法给外部调用

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
// Teacher 类增加一个方法来返回学生姓名
public class Teacher {
private List<Student> students;

public List<String> getStudentNames() {
return students.stream().map(Student::getName).collect(Collectors.toList());
}
}

// School 类增加一个方法来返回所有学生姓名
public class School {
private List<Teacher> teachers;

public List<String> getAllStudentNames() {
List<String> studentNames = new ArrayList<>();
for (Teacher teacher : teachers) {
studentNames.addAll(teacher.getStudentNames());
}
return studentNames;
}
}

// 使用时
public void printStudentNames(School school) {
for (String name : school.getAllStudentNames()) {
System.out.println(name);
}
}

合成复用原则

优先使用已有代码(组合来实现功能),而不是通过继承来扩展功能

1
2
3
4
5
6
7
// 打印类
public class Printer {
public void printText(String text) {
System.out.println("Printing text: " + text);
}
}

这时候要扩展功能,可以借助上面的打印类来完成任务,这时候可以组合两个类,而不是直接继承类来扩展

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 TextProcessor {
public String process(String text) {
// 进行一些文本处理,例如去除敏感词
return text.replaceAll("敏感词", "***");
}
}

// 组合 TextProcessor 和 Printer
public class AdvancedPrinter {
private Printer printer;
private TextProcessor processor;

public AdvancedPrinter() {
this.printer = new Printer();
this.processor = new TextProcessor();
}

public void printText(String text) {
// 使用组合的 TextProcessor 来处理文本
String processedText = processor.process(text);
printer.printText(processedText);
}
}

设计模式

image-20240423211739681

创建型(5种,简单工厂不算标准的)

对类的实例化进行抽象。封装了具体类的信息,隐藏了类的实例化过程

  • 单例模式
  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式

单例模式(Singleton)

单例类必须自己创建自己的唯一实例:把类的构造方法私有化,内部进行实例化,不让外部调用构造方法实例化
单例类必须给所有其他对象提供这一实例:定义共有方法提供该类全局唯一访问点,外部通过调用getInstance()方法来返回唯一实例

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package DesignPatterns;

public class SingleExample {

/**
* 1. 饿汉式
* 最简单的单例实现方式。依赖JVM类加载机制,保证单例只会被创建1次,即线程安全。
* JVM在类的初始化阶段(即 在Class被加载后、被线程使用前),会执行类的初始化,
* 在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化
*/

/* private static SingleExample instance = new SingleExample();

//函数构造权限为私有权限,其他地方无法创建
private SingleExample(){

}

public static SingleExample getInstance() {
return instance;
}*/



/**
* 2. 懒汉式(按需、延迟创建单例)
* 最简单的单例实现方式。有需要时才手动创建单例,即 线程不安全。
*/

/* private static SingleExample instance = null;

//函数构造权限为私有权限,其他地方无法创建
private SingleExample(){

}

public static SingleExample newInstance() {
// 先判断单例是否为空,以避免重复创建
if( instance == null){
instance = new SingleExample();
}
return instance;
}*/



/**
* 针对懒汉式优化1
* 使用同步锁
*/

/* private static SingleExample instance = null;

//函数构造权限为私有权限,其他地方无法创建
private SingleExample(){

}

public static synchronized SingleExample newInstance() {
// 先判断单例是否为空,以避免重复创建
if( instance == null){
instance = new SingleExample();
}
return instance;
}*/



/**
* 针对懒汉式优化2
* 同时也针对同步锁的优化,因为同步锁每次访问都要进行线程同步(即 调用synchronized锁),造成过多的同步开销(加锁 = 耗时、耗能)
* 双重校验锁
*/

private static volatile SingleExample instance = null;

//函数构造权限为私有权限,其他地方无法创建
private SingleExample(){

}

public static SingleExample newInstance() {
// 先判断单例是否为空,以避免重复创建
if(instance == null){
synchronized (SingleExample.class){
if (instance == null){
instance = new SingleExample();
}
}
}
return instance;
}

// 说明
// 校验锁1:第1个if
// 作用:若单例已创建,则直接返回已创建的单例,无需再执行加锁操作
// 即直接跳到执行 return ourInstance

// 校验锁2:第2个 if
// 作用:防止多次创建单例问题
// 原理
// 1. 线程A调用newInstance(),当运行到②位置时,此时线程B也调用了newInstance()
// 2. 因线程A并没有执行instance = new Singleton();,此时instance仍为空,因此线程B能突破第1层 if 判断,运行到①位置等待synchronized中的A线程执行完毕
// 3. 当线程A释放同步锁时,单例已创建,即instance已非空
// 4. 此时线程B 从①开始执行到位置②。此时第2层 if 判断 = 为空(单例已创建),因此也不会创建多余的实例
}

简单工厂模式(SimpleFactory Pattern)

通过在工厂类定义一个静态方法负责生产产品对象实例。(类似现实生活中工厂生产产品)

将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产(实例化)的细节。

优点:将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。

缺点:工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。

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 abstract class Product {
public abstract void show();
}

/**
* 第二步:创建具体产品类(继承抽象产品类),定义生产的具体产品
*/
public class ProductA extends Product {
@Override
public void show() {
System.out.println("生产出A产品");
}
}

public class ProductB extends Product{
@Override
public void show() {
System.out.println("生产出B产品");
}
}

/**
* 创建工厂类,通过创建静态方法从而根据传入不同参数创建不同具体产品类的实例
*/
public class Factory {
public static Product product(String type) {
switch (type) {
case "A":
return new ProductA();
case "B":
return new ProductB();
default:
return null;
}
}
}

工厂方法模式(Factory Method)

工厂方法模式可理解为多态工厂模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定该实例化(创建)哪一个类。

解决了简单工厂的缺点,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。

(添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;一个具体工厂只能创建一种具体产品)

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
/**
* 创建抽象产品类,定义具体产品的公共接口
*/
public abstract class Product {
public abstract void show();
}

/**
* 创建具体产品类(继承抽象产品类),定义生产的具体产品
*/
public class ProductA extends Product {
@Override
public void show() {
System.out.println("生产出A产品");
}
}

public class ProductB extends Product {
@Override
public void show() {
System.out.println("生产出B产品");
}
}

/**
* 创建抽象工厂类,定义具体工厂的公共接口
*/
public abstract class Factory {
public abstract Product produce();
}

/**
* 创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法
*/
public class FactoryA extends Factory {
@Override
public Product produce() {
return new ProductA();
}
}

public class FactoryB extends Factory{
@Override
public Product produce() {
return new ProductB();
}
}

//后续需要生产什么产品就调用哪个工厂

抽象工厂模式(Abastract Factory)

抽象工厂模式,即Abstract Factory Pattern,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类。
允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。
可解决工厂模式缺点:每个工厂只能创建一类产品。

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
// 抽象产品接口,增加这里的接口的话,下面的GUIFactory就要动原本的代码,增加产品族就没事
interface Button {
void render();
}

interface TextField {
void input();
}

// 具体产品(Windows 系列)
class WindowsButton implements Button {
@Override
public void render() {
System.out.println("Render a Windows Button");
}
}

class WindowsTextField implements TextField {
@Override
public void input() {
System.out.println("Input in a Windows TextField");
}
}

// 具体产品(MacOS 系列)
class MacOSButton implements Button {
@Override
public void render() {
System.out.println("Render a MacOS Button");
}
}

class MacOSTextField implements TextField {
@Override
public void input() {
System.out.println("Input in a MacOS TextField");
}
}

// 抽象工厂接口
interface GUIFactory {
Button createButton();
TextField createTextField();
}

// 具体工厂(Windows 系列)
class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}

@Override
public TextField createTextField() {
return new WindowsTextField();
}
}

// 具体工厂(MacOS 系列)
class MacOSFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}

@Override
public TextField createTextField() {
return new MacOSTextField();
}
}

// 客户端代码示例
public class Main {
public static void main(String[] args) {
// 创建 Windows 系列的 GUI 工厂
GUIFactory windowsFactory = new WindowsFactory();
Button windowsButton = windowsFactory.createButton();
TextField windowsTextField = windowsFactory.createTextField();
windowsButton.render();
windowsTextField.input();

// 创建 MacOS 系列的 GUI 工厂
GUIFactory macFactory = new MacOSFactory();
Button macButton = macFactory.createButton();
TextField macTextField = macFactory.createTextField();
macButton.render();
macTextField.input();
}
}

从上面可以看出来其实抽象工厂模式就是对工厂方法模式的产品多了一层产品族的抽象

三个工厂模式的对比

简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品,需要动原本的代码,不算拓展增加)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族,这是为什么呢?是因为每个工厂需要能生产每个产品族的产品,加了一个新种类的产品,不同产品族都有这个产品,Factory类就需要加一个抽象方法生成这类产品,就需要动原本代码了)

建造者模式(Builder Pattern)

适用于一个类内部数据结构过于复杂时(用于很多数据,且组织装配复杂),通过构建者模式可以对类中的数据按部就班地创建与设置。即Builder模式可以将一个类的构建和表示进行分离。
创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使
得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态地创建具有复合属性的对象。

如果一个类构造器需要传入很多参数的时候就可以使用Builder进行重构

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
/**
* 未重构之前
*/
public class Computer {
private String cpu;
private String screen;
private String memory;
private String mainboard;

public Computer(String cpu, String screen, String memory, String mainboard) {
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainboard = mainboard;
}
}

/**
* 重构后
*/
public class NewComputer {
private String cpu;
private String screen;
private String memory;
private String mainboard;

private NewComputer(Builder builder) {
this.cpu = builder.cpu;
this.screen = builder.screen;
this.memory = builder.memory;
this.mainboard = builder.mainboard;
}

public static final class Builder {

private String cpu;
private String screen;
private String memory;
private String mainboard;

public Builder() {

}

public Builder cpu(String val) {
cpu = val;
return this;
}

public Builder screen(String val) {
screen = val;
return this;
}

public Builder memory(String val) {
memory = val;
return this;
}

public Builder mainboard(String val) {
mainboard = val;
return this;
}

public NewComputer build() {
return new NewComputer(this);
}
}

public static void main(String[] args) {
NewComputer newComputer = new Builder()
.cpu("")
.screen("")
.memory("")
.mainboard("")
.build();
}
}

原型模式(Prototype Pattern)

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能,(借助已经创建的实例作为原型,创建相同的新对象)

使用时机

类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。
一个对象需要供给其他对象访问,而且各个对象都需要修改其值时,可以拷贝多个对象供调用者访问,即保护性拷贝。

并且通过clone(java的clone方法就是在应用原型设计模式)可以不依赖于这个类的具体属性的实现和初始化

结构

  • 抽象原型类:规定访问原型对象实现具体的clone方法
  • 具体原型类:实现抽象类的clone方法,它是可以被复制的对象
  • 访问类:调用具体原型中的clone方法来复制新的对象

浅拷贝和深拷贝
浅拷贝又叫影子拷贝,上面我们在拷贝文档时并没有把原文档中的字段都重新构造了一遍,而只是拷贝了引用,也就是副文档的字段引用原始文档的字段,这样的话修改副文档中的内容就会连原始文档也改掉了,这就是浅拷贝
深拷贝就是在浅拷贝的基础上,对于引用类型的字段也要采用拷贝的形式,比如上面的images,而像String、int这些基本数据类型则没关系

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
92
93
94
95
96
import java.util.HashMap;
import java.util.Map;

// 抽象原型接口
interface Shape extends Cloneable {
Shape clone();
void draw();
}

// 具体原型类 - 圆形
class Circle implements Shape {
private int radius;

public Circle(int radius) {
this.radius = radius;
System.out.println("Initializing a Circle with radius: " + radius);
}

@Override
public Circle clone() {
try {
return (Circle) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}

@Override
public void draw() {
System.out.println("Drawing a Circle with radius: " + radius);
}
}

// 具体原型类 - 矩形
class Rectangle implements Shape {
private int width;
private int height;

public Rectangle(int width, int height) {
this.width = width;
this.height = height;
System.out.println("Initializing a Rectangle with width: " + width + ", height: " + height);
}

@Override
public Rectangle clone() {
try {
return (Rectangle) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}

@Override
public void draw() {
System.out.println("Drawing a Rectangle with width: " + width + ", height: " + height);
}
}

// 原型管理器(用于缓存和管理原型对象)
class ShapePrototypeManager {
private Map<String, Shape> shapeMap = new HashMap<>();

public void registerShape(String key, Shape shape) {
shapeMap.put(key, shape);
}

public Shape getShape(String key) {
Shape shape = shapeMap.get(key);
return shape != null ? shape.clone() : null;
}
}

// 客户端代码
public class Main {
public static void main(String[] args) {
// 创建原型管理器
ShapePrototypeManager prototypeManager = new ShapePrototypeManager();

// 初始化原型对象并注册
Circle circle = new Circle(5);
Rectangle rectangle = new Rectangle(10, 20);

prototypeManager.registerShape("circle", circle);
prototypeManager.registerShape("rectangle", rectangle);

// 克隆原型对象
Shape clonedCircle = prototypeManager.getShape("circle");
Shape clonedRectangle = prototypeManager.getShape("rectangle");

// 使用克隆对象
clonedCircle.draw();
clonedRectangle.draw();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Intent中的原型模式
@Override
public Object clone() {
return new Intent(this);
}

private Intent(Intent o, boolean all) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
if (o.mCategories != null) {
this.mCategories = new ArraySet<String>(o.mCategories);
}
}

我们常用的Intent,ArrayList等都实现clone
登录模块中保存的用户信息类需要通过服务器更新用户信息,但是有很多地方需要调用,需要设置为对其他用到的模块只读,这个时候可以考虑用原型模式进行保护性拷贝

原型设计模式解决对象创建开销大、构建复杂等问题,并且深拷贝和浅拷贝都符合这个设计模式,看具体业务需求,java引用类要做到深拷贝就实现Clonable接口实现clone方法

结构型(7种)

结构型模式描述如何将类和对象按某种布局组织成更大的结构,分为结构型设计模式和对象结构型设计模式,前者采用继承机制来组织类和接口,后者采用组合或聚合组合对象,对象型满足合成复用原则,具有更大的灵活性

  • 代理模式

  • 适配器模式

  • 装饰者模式

  • 桥接模式

  • 外观模式

  • 组合模式

  • 享元模式

代理模式(Proxy Pattern)

适配器模式(Adapter)

系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容
想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的一些类一起工作
需要一个统一的输出接口,而输入端的接口不可预知

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

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 interface MediaPlayer {
public void play(String audioType,String fileName);
}

interface AdvancedMediaPlayer{
public void playVlc(String fileName);

public void playMp4(String fileName);
}

/**
* 步骤二:创建实现了 AdvancedMediaPlayer 接口的实体类
*/
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("播放vlc文件,名字为" + fileName);
}

@Override
public void playMp4(String fileName) {
//什么都不做
}
}

class Mp4Player implements AdvancedMediaPlayer {

@Override
public void playVlc(String fileName) {
//什么都不做
}

@Override
public void playMp4(String fileName) {
System.out.println("播放mp4文件,名字为" + fileName);
}
}

/**
* 步骤三:创建实现了 MediaPlayer 接口的适配器类
*/
public class MediaAdapter implements MediaPlayer{

private AdvancedMediaPlayer advancedMediaPlayer;

public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")){
advancedMediaPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer = new Mp4Player();
}
}

@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")){
advancedMediaPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer.playMp4(fileName);
}
}
}

/**
* 步骤四:创建实现了 MediaPlayer 接口的实体类
*/
public class AudioPlayer implements MediaPlayer{

private MediaAdapter mediaAdapter;

@Override
public void play(String audioType, String fileName) {
//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}


看的有点懵逼,回头再看

  • 代理模式

看的有点懵逼,回头再看

  • 装饰模式(Decorate)

动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类(继承)更为灵活的方案。

装饰模式主要在于扩展了类的功能。装饰模式通过在被装饰组件的方法执行之前或之后加入新的方法来实现功能的扩展.

具体应用:Android源码中的ContextWrapper

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
/**
* 创建一个接口
*/
public interface Shape {
void draw();
}

/**
* 创建实现接口的实体类
*/
public class Rectangle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}

class Circle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Circle");
}
}

/**
* 创建实现了 Shape 接口的抽象装饰类
*/
public abstract class ShapeDecorator implements Shape{

protected Shape decoratedShape;

public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}

@Override
public void draw() {
decoratedShape.draw();
}
}

/**
* 创建扩展了 ShapeDecorator 类的实体装饰类
*/
public class RedShapeDecorator extends ShapeDecorator {

public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}

@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}

private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}

/**
* 具体使用
*/
public class DecoratorPatternDemo {
public static void main(String[] args) {

Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
//Shape redCircle = new RedShapeDecorator(new Circle());
//Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();

System.out.println("\nCircle of red border");
redCircle.draw();

System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}

  • 外观模式(Facade Pattern)

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
用于为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

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
//相机功能接口
public interface Camera {

//打开相机
public void open();

//拍照
public void takePhoto();

//关闭相机
public void close();
}

//相机功能的实现类
class MiCamera implements Camera {

@Override
public void open() {
System.out.println("打开相机");
}

@Override
public void takePhoto() {
System.out.println("拍照");
}

@Override
public void close() {
System.out.println("关闭相机");
}
}

//电话功能接口
public interface Phone {
//打电话
public void dail();

//挂断
public void hangup();
}

//电话的实现类
class PhoneImpl implements Phone {

@Override
public void dail() {
System.out.println("打电话");
}

@Override
public void hangup() {
System.out.println("挂断");
}
}

//具体使用,外观
public class MobilePhone {
private Phone mPhone = new PhoneImpl();
private Camera mCamera = new MiCamera();

//拍照
public void takePhoto() {
mCamera.open();
mCamera.takePhoto();
mCamera.close();
}

//视频聊天
public void videoChat() {
mCamera.open();
mPhone.dail();
}

public static void main(String[] args) {
MobilePhone mobilePhone = new MobilePhone();
mobilePhone.takePhoto();
mobilePhone.videoChat();
}
}

应用:很多的第三方SDK,比如友盟统计;我们平时开发过程中封装的模块,比如网络模块、ImageLoader模块等

行为型

  • 模板方法模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 责任链模式

模板方法模式(Template Method)

定义一个模板结构,将具体内容延迟到子类去实现。在不改变模板结构的前提下在子类中重新定义模板中的内容。
模板方法模式是基于”继承“的;

  • 观察者模式(Observer Pattern)
角色 说明
抽象主题/被观察者(Observable) 抽象主题把所有的观察者对象的引用保存在一个集合里,每个主题可以有任意数量的观察者,抽象主题提供接口,可以增加和删除观察者对象
具体的主题(具体的被观察者) 也就是抽象主题的子类,该角色将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,通知所有注册过的观察者
抽象观察者 观察者的抽象类,定义了一个更新的接口
具体观察者 实现了抽象观察者的更新接口,在被观察者状态发生变化时更新自身的状态

观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。

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
92
93
94
95
96
97
98
99
/**
* 1.创建Subject类(被观察者)
*/
public class Subject {

private List<Observer>observers = new ArrayList<>();

private int state;

public int getState() {
return state;
}

public void setState(int state) {
this.state = state;
notifyAllObservers();//state发生变化,通知
}

public void attach(Observer observer){
observers.add(observer);
}

public void notifyAllObservers(){
for (Observer observer:observers){
observer.update();
}
}
}

/**
* 2.创建Observer类(抽象观察者)
*/
public abstract class Observer {
protected Subject subject;

public abstract void update();
}

/**
* 3.创建具体观察者类
*/
public class BinaryObserver extends Observer {

public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}

@Override
public void update() {
System.out.println( "Binary String: "
+ Integer.toBinaryString( subject.getState() ) );
}
}

class OctalObserver extends Observer {

public OctalObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}

@Override
public void update() {
System.out.println("Octal String: "
+ Integer.toOctalString(subject.getState()));
}
}

class HexaObserver extends Observer{

public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}

@Override
public void update() {
System.out.println( "Hex String: "
+ Integer.toHexString( subject.getState() ).toUpperCase() );
}
}


//使用
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();

new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);

System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}

  • 状态模式(State Pattern)

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
当代码中包含着大量与对象状态有关的条件语句。此时对象行为依赖于它的状态(属性),并可以根据它的状态改变它的相关行为。

优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。

注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

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
92
93
94
95
96
97
98
/**
* 登录状态接口
*/
public interface UserState {

/**
* 转发
*/
void forward();

/**
* 评论
*/
void comment();
}


/**
* 具体接口实现
*/
/**
* 登录状态
*/
public class LoginState implements UserState{
@Override
public void forward() {
System.out.println("转发成功");
}

@Override
public void comment() {
System.out.println("评论成功");
}
}


/**
* 未登录状态
*/
class UnLoginState implements UserState{

@Override
public void forward() {
System.out.println("未登录");
}

@Override
public void comment() {
System.out.println("未登录");
}
}


/**
* 使用
*/
public class LoginContext {

private UserState userState = new UnLoginState();//默认未登录

static LoginContext loginContext = new LoginContext();//单例

private LoginContext() {

}

public static LoginContext getLoginContext() {
return loginContext;
}

/**
* 状态改变,设置状态
*
* @param userState 状态
*/
public void setUserState(UserState userState) {
this.userState = userState;
}

public void forward() {
userState.forward();
}

public void comment() {
userState.comment();
}



public static void main(String[] args) {
LoginContext loginContext1 = getLoginContext();
//未登录
loginContext1.comment();
//登录后
loginContext1.setUserState(new LoginState());
loginContext1.comment();
}
}

  • 策略模式(Stratege Pattern)

状态模式和策略模式的结构几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态发生改变的时候,其行为也随之改变

状态模式:状态模式关注对象的内部状态改变,并相应地改变对象的行为。它将状态的管理和对象的行为解耦,使得状态变化不会导致对象行为的复杂性增加。
策略模式:策略模式关注对象的行为的变化,并提供了一种灵活的方式来选择不同的行为或算法。它将算法的选择和使用从对象中解耦,使得算法的变化不会影响到对象的使用者。

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
/**
* 接口
*/
public interface CalculateStragety {

/**
* 根据公里数计算价格
*
* @param km 公里数
* @return 价格
*/
int calculatePrice(int km);
}

/**
* 策略1
*/
public class BusStragety implements CalculateStragety {

/**
* 十公里之内一元,超过十公里每加一元钱可以坐5公里
* @param km 公里数
* @return 公交车车费
*/
@Override
public int calculatePrice(int km) {
//超过十公里的总距离
int extraTotal = km - 10;

// 超过的距离是5公里的倍数
int extraFactor = extraTotal / 5;

//超过的距离对5公里取余
int fraction = extraTotal % 5;

//价格计算
int price = 1 + extraFactor * 1 ;

return fraction > 0 ? ++price : price;
}
}

/**
* 策略2
*/
class TaxiStragety implements CalculateStragety {

/**
* 出租车车费为每公里2元
* @param km 公里数
* @return 出租车车费
*/
@Override
public int calculatePrice(int km) {
return km * 2;
}
}

/**
* 使用
*/
public class TrafficCalculator {
public static void main(String[] args) {
TrafficCalculator trafficCalculator = new TrafficCalculator();
trafficCalculator.setCalculateStragety(new BusStragety());
trafficCalculator.calculatePrice(66);
}

CalculateStragety mCalculateStragety;

/**
* 根据需要注入相应的策略
*
* @param calculateStragety 注入的策略
*/
public void setCalculateStragety(CalculateStragety calculateStragety) {
mCalculateStragety = calculateStragety;
}

/**
* 把具体的计算委托给注入的策略
*
* @param km 公里数
* @return 车费
*/
private int calculatePrice(int km) {
return mCalculateStragety.calculatePrice(km);
}
}

  • 责任链模式(Chain of Responsibility Pattern)

职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

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
92
93
94
95
96
97
98
/**
* 创建抽象的记录器类。
*/
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;

protected int level;

//责任链中的下一个元素
protected AbstractLogger nextLogger;

public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}

public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger !=null){
nextLogger.logMessage(level, message);
}
}

abstract protected void write(String message);

}

/**
* 创建扩展了该记录器类的实体类
*/
public class ConsoleLogger extends AbstractLogger {

public ConsoleLogger(int level){
this.level = level;
}

@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}

class ErrorLogger extends AbstractLogger {

public ErrorLogger(int level){
this.level = level;
}

@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}

class FileLogger extends AbstractLogger {

public FileLogger(int level){
this.level = level;
}

@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}

/**
* 具体使用
*/
public class ChainPatternDemo {

private static AbstractLogger getChainOfLoggers(){

AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);

return errorLogger;
}

public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();

loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");

loggerChain.logMessage(AbstractLogger.DEBUG,
"This is a debug level information.");

loggerChain.logMessage(AbstractLogger.ERROR,
"This is an error information.");
}
}