设计模式原则

1
2
3
4
5
6
7
1、单一职责
2、开闭原则
3、接口隔离
4、里氏替换
5、依赖倒转
6、合成复用
7、迪米特法则
单一职责

对类来说,即一个类只负责一项职责

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Vehicle {
public void run(String vehicleName) {
System.out.println(vehicleName + "在陆地上运行");
};
}

class Test {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("汽车");
vehicle.run("飞机");
}
}
此方式违反了单一职责原则,Vehicle并不是只是单一的职责。并且如果我要对飞机进行特别修改的话,就会对汽车造成影响。
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
public class AirVehicle {
public void run(String vehicleName) {
System.out.println(vehicleName + "在天上运行");
};
}

class CarVehicle {
public void run(String vehicleName) {
System.out.println(vehicleName + "在陆地上运行");
};
}

class Test {
public static void main(String[] args) {
AirVehicle vehicle = new AirVehicle();
vehicle.run("汽车");
vehicle.run("飞机");
}
}
👆正确方式,或者如果类中职责足够简单的话,可以降级到方法的单一职责👇
public class Vehicle {
public void carRun(String vehicleName) {
System.out.println(vehicleName + "在地上运行");
};
public void AirRun(String vehicleName) {
System.out.println(vehicleName + "在天上运行");
};
}


class Test {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.carRun("汽车");
vehicle.AirRun("飞机");
}
}
接口隔离

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

image-20230529092043614

错误方式👆 上述违反了接口隔离,C、D类会把Interface中所有的方法都会实现,但是A、B类并不是会依赖Interface所有的方法。

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
interface Interface {
void operation_1();
void operation_2();
void operation_3();
void operation_4();
void operation_5();
}

class C implements Interface{

@Override
public void operation_1() {

}

@Override
public void operation_2() {

}

@Override
public void operation_3() {

}

@Override
public void operation_4() {

}

@Override
public void operation_5() {

}
}

class D implements Interface{

@Override
public void operation_1() {

}

@Override
public void operation_2() {

}

@Override
public void operation_3() {

}

@Override
public void operation_4() {

}

@Override
public void operation_5() {

}
}

class A {
public void op(Interface i) {
i.operation_1();
i.operation_2();
i.operation_3();
}
}

class B {
public void op(Interface i) {
i.operation_1();
i.operation_2();
i.operation_4();
i.operation_5();
}
}

image-20230529092744680

正确方式👆,C、D仅实现A,B依赖所需

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
interface Interface_1 {
void operation_1();

void operation_2();
}

interface Interface_2 {
void operation_3();
}

interface Interface_3 {

void operation_4();

void operation_5();
}

class C implements Interface_1, Interface_2 {

@Override
public void operation_1() {

}

@Override
public void operation_2() {

}


@Override
public void operation_3() {

}
}

class D implements Interface_1, Interface_3 {

@Override
public void operation_1() {

}

@Override
public void operation_2() {

}

@Override
public void operation_4() {

}

@Override
public void operation_5() {

}
}

class A {
public void op(Interface_1 interface1, Interface_2 interface2) {
interface1.operation_1();
interface1.operation_2();
interface2.operation_3();
}
}

class B {
public void op(Interface_1 interface1, Interface_3 interface3) {
interface1.operation_1();
interface1.operation_2();
interface3.operation_4();
interface3.operation_5();
}
}
依赖倒转原则

1、高层模块不应该依赖底层模块,二者都应该依赖器抽象

2、抽象不应该依赖细节,细节应该依赖抽象

3、依赖倒转的中心思想是面向接口编程

4、依赖倒转原则是基于这样的设计理念,相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础比细节为基础的架构要稳定的多

5、使用接口或者抽象类的目的是制定好规范,而不涉及到具体的操作,具体的操作应交给实现类

6、有三种传递方式:接口、setter、构造器

1
2
3
4
5
6
7
8
9
10
11
class Email {
public String getInfo() {
return "邮件";
}
}

class Person {
public void getMessage(Email email) { // 此处不应该是一个具体的类,应该是一个接口或抽象类
System.out.println(email.getInfo());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Email implements Receiver{
public String getInfo() {
return "邮件信息";
}
}

class Wechat implements Receiver{
public String getInfo() {
return "微信信息";
}
}

interface Receiver {
String getInfo();
}

class Person {
public void getMessage(Receiver receiver) { // 替换成接口,扩展性更强
System.out.println(receiver.getInfo());
}
}
里氏替换

1、所有引用基类的地方必须能透明地使用其子类的对象

2、在子类中尽量不要重写父类的方法

3、可以通过聚合、组合、依赖来决绝问题

image-20230529120352687

1
2
3
4
5
6
7
8
9
10
class A {
public void run() {};
}

class B extends A {
@Override
public void run() {
System.out.println("重写A类run, 继承就没意义了");
}
}

image-20230529120833727

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Base {
// 更加基础的方法
}

class A extends Base{
public void run() {
System.out.println("A类的run");
};
}

class B extends Base {
private A a = new A();
public void run() {
System.out.println("B类的run");
}
public void runWithA() {
a.run();
}
}
开闭原则

1、对提供方扩展开放,对使用方修改关闭;用抽象扩展框架,用实现扩展细节

2、软件变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化

上方依赖倒转例子满足开闭原则

迪米特法则

1、一个对象应该对其他对象保持最少的了解

2、类与类的关系越密切,耦合度越大

3、迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。不管被依赖的类多么复杂,都尽量将逻辑封装在类的内部,对外提供public方法,不对外泄露任何信息。

对非直接朋友的耦合:非直接朋友(局部变量)

错误方式👇

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
public class Test {
public static void main(String[] args) {
//创建一个SchoolManager对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工ID和学校总部的员工信息
schoolManager.PRINTaLLEmployee(new CollegeManager());
}
}
//学校总部员工类
class Employee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//学院的员工类
class CollegeEmployee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//管理学院员工的管理类
class CollegeManager{
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
//增加了10个员工到list
for (int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工ID="+i);
list.add(emp);
}
return list;
}
}
//学校管理类
class SchoolManager{
//返回学校总部的员工
public List<Employee> getAllEmployee(){
List<Employee>list = new ArrayList<Employee>();
for (int i = 0; i < 5; i++) {
Employee emp = new Employee();
emp.setId("学校总部员工ID="+i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(ID)
void PRINTaLLEmployee(CollegeManager sub){
//分析问题
//1、这两的CollegeEmployee不是SchoolManager的直接朋友
//2、CollegeEmployee是以局部变量方式出现在SchoolManager
//3、违反了迪米特法则
//获取学院员工
List<CollegeEmployee>list1 = sub.getAllEmployee();
System.out.println("===========学院员工==============");
for (CollegeEmployee e :list1) {
System.out.println(e.getId());
}
//获取学院总部员工
List<Employee>list2 = this.getAllEmployee();
System.out.println("===========学院总部员工==============");
for (Employee e :list2) {
System.out.println(e.getId());
}
}
}

正确👇

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
public class Test {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
}
}
//学校总部员工类
class Employee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//学院的员工类
class CollegeEmployee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//管理学院员工的管理类
class CollegeManager{
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
//增加了10个员工到list
for (int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工ID="+i);
list.add(emp);
}
return list;
}
//输出学院员工的信息
public void printEmployee(){
//获取到学院员工
List<CollegeEmployee>list1 = getAllEmployee();
System.out.println("===========学院员工==============");
for (CollegeEmployee e :list1) {
System.out.println(e.getId());
}
}
}
//学校管理类
class SchoolManager{
//返回学校总部的员工
public List<Employee> getAllEmployee(){
List<Employee>list = new ArrayList<Employee>();
for (int i = 0; i < 5; i++) {
Employee emp = new Employee();
emp.setId("学校总部员工ID="+i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(ID)
void printAllEmployee(CollegeManager sub){
//分析问题
//1、将输出学院员工方法,封装到CollegeManager
sub.printEmployee();

//获取学院总部员工
List<Employee>list2 = this.getAllEmployee();
System.out.println("===========学院总部员工==============");
for (Employee e :list2) {
System.out.println(e.getId());
}
}
}
合成复用

1、尽量使用聚合和组合,而不是继承image-20230529162711308

原则总结:

1、找出应用中需要变化之外,把他们独立出来,不要和那些不需要变化的代码混在一起

2、针对接口编程,而不是针对实现编程

3、为了交互对象之间的松耦合设计而努力