0%

7大设计原则和23种设计模式

7大设计原则和23种设计模式

1.前言

  1. 设计模式不是一种新的语言,也不是什么新api,更不是什么新语法。
  2. 设计模式是前辈们不断总结,不断打磨出的设计方法。不同的设计模式适用于不同的场景。
  3. 设计模式公认的有23种,分别对应23种不同的设计场景。
  4. 千万不要认为任何一种设计模式能解决任何问题,每一种设计模式只能用于适用的场景,不是万能的。
  5. 设计模式不是万能的,有优点也有缺点,不能为了适用设计模式而使用设计模式。切记防止‘’‘模式的滥用’
  6. 23种设计模式,背后其实是7大设计原则。也就是说,每一个设计模式都归属于一个或者多个设计原则
  7. 七大设计原则背后总结为一个字:分
  8. 7大设计原则分别是:
    • 单一职责原则
    • 接口隔离原则
    • 里氏替换原则
    • 迪米特法则(最少知道原则)
    • 依赖倒置原则
    • 开闭原则(OCP)
    • 组合优于继承原则

1.单一职责原则

​ 每个方法,每个类,每个框架都只负责一件事情

例如:Math.round()只负责完成四舍五入方法,其它不管(方法)

​ Read类只负责文件的读取(类)

​ SpringMVC只负责简化MVC的开发(框架)

反例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/11
*/
public class NegtiveTest {
public static void main(String[] args) throws IOException {
//统计一个文本文件中有多少个单词
Reader reader = new FileReader("C:\\Users\\asus\\Desktop\\2.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
String line = null;
StringBuilder stringBuilder = new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
// System.out.println(line+"!!");
stringBuilder.append(line);
stringBuilder.append(" ");
}
String[] strings = stringBuilder.toString().split("[^a-zA-Z]+");
System.out.println(strings.length);
Arrays.stream(strings).forEach(s -> System.out.println(s+"`````"));
// System.out.println(stringBuilder);
bufferedReader.close();
}
}
//以上代码违反了单一职责原则:
//1.代码重用性低
//2.可读性低

正例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/11
*/
public class PositiveTest {
public static void main(String[] args) throws IOException {
//统计一个文本文件中有多少个单词
String string = loadFile("C:\\Users\\asus\\Desktop\\2.txt");
String[] strings = getWords(string);
System.out.println(strings.length);
Arrays.stream(strings).forEach(s -> System.out.println(s + "`````"));
// System.out.println(stringBuilder);

}

/**
* 只负责返回单词数组
* @param string
* @return
*/
private static String[] getWords(String string) {
String[] strings = string.toString().split("[^a-zA-Z]+");
return strings;
}

/**
* 只负责读取文件
*
* @param path
* @return
* @throws IOException
*/
private static String loadFile(String path) throws IOException {
Reader reader = new FileReader(path);
BufferedReader bufferedReader = new BufferedReader(reader);
String line = null;
StringBuilder stringBuilder = new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
// System.out.println(line+"!!");
stringBuilder.append(line);
stringBuilder.append(" ");
}
bufferedReader.close();
return stringBuilder.toString();
}
}

2.开闭原则(OCP)

​ 1.对扩展新功能开放

​ 2.对修改旧功能关闭

反例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
@Data
public class Cellphone {
private String brand;
private Double price;

public void setPrice(Double price) {
this.price = price * 0.8;
}

public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
cellphone.setBrand("华为");
cellphone.setPrice(1999.0);
//所有手机打8折,违反开闭原则的做法是打开Cellphone源码,在setPrice上修改
System.out.println(cellphone);
}
}

正例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
@Data
public class Cellphone {
private String brand;
private Double price;

public void setPrice(Double price) {
this.price = price;
}

public static void main(String[] args) {
Cellphone cellphone = new DiscountCellphone();
cellphone.setBrand("华为");
cellphone.setPrice(1999.0);
//所有手机打8折,符合开闭原则的做法是新建一个子类,继承Cellphone重写父类setPrice方法
System.out.println(cellphone);
}
}

class DiscountCellphone extends Cellphone {
@Override
public void setPrice(Double price) {
super.setPrice(price * 0.8);
}
}

3.接口隔离原则

​ 1.客户端不应该依赖它不需要的接口

​ 2.类间的依赖关系应该建立在最小接口上

反例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class InterfaceSegregationNegativeTest {


public static void main(String[] args) {
Animal animal = new Dog();
// animal.fly();
animal.run();
}
}
interface Animal {
void run();

void swim();

void fly();
}

class Dog implements Animal{

@Override
public void run() {
System.out.println("Dog is running");
}

@Override
public void swim() {
System.out.println("Dog is swimming");
}
//狗不具备飞行的能力,不应该实现飞行的方法
@Override
public void fly() {
throw new RuntimeException("Dog can not 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
48
49
50
51
52
53
54
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class InterfaceSegregationPositiveTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();
Bird bird = new Bird();
bird.fly();
}
}

interface RunAble {
void run();
}

interface SwimAble {
void swim();
}

interface Flyable {
void fly();
}

/**
* 狗会跑,会游泳,客户端只需要实现它需要的接口
*/
class Dog implements RunAble, SwimAble {

@Override
public void run() {
System.out.println("Dog is running");
}

@Override
public void swim() {
System.out.println("Dog is swimming");
}
}

/**
* 鸟会飞,只需要实现它需要的接口
*/
class Bird implements Flyable {

@Override
public void fly() {
System.out.println("Bird is flying");
}
}

4.依赖倒置原则

​ 上层不应该依赖于下层,上层和下层都应该依赖于抽象的接口

反例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class DependencyInversionNegativeTest {
public static void main(String[] args) {
//Person类依赖于Dog和Cat类,如果要喂鸟等其它动物,就要改Person的源代码,不利于扩展
Person person = new Person();
Cat cat = new Cat();
cat.setName("小猫");
Dog dog = new Dog();
dog.setName("小狗");
person.feed2(dog);
}
}

class Person {
void feed(Cat cat) {
System.out.println("人正在喂" + cat.getName());
cat.eat();
}

void feed2(Dog dog) {
System.out.println("人正在喂" + dog.getName());
dog.eat();
}
}

@Data
class Cat {
String name;

void eat() {
System.out.println("猫正在吃猫粮");
}
}

@Data
class Dog {
String name;

void eat() {
System.out.println("狗正在吃狗粮");
}
}

正例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class DependencyInversionPositiveTest {
public static void main(String[] args) {
//Person类、Dog类、Cat类都依赖于抽象的Animal接口,如果需要喂鸟只需要新建一个Bird类实现Animal即可,利于扩展
Person person = new Person();
Cat cat = new Cat();
cat.setName("小猫");
Dog dog = new Dog();
dog.setName("小狗");
person.feed(cat);
person.feed(dog);
}
}

class Person {
void feed(Animal animal) {
animal.eat();
}

}

interface Animal {
void eat();
}

@Data
class Cat implements Animal {
String name;

public void eat() {
System.out.println("猫正在吃猫粮");
}
}

@Data
class Dog implements Animal {
String name;

public void eat() {
System.out.println("狗正在吃狗粮");
}
}

5.迪米特法则(最少知道原则)

​ 迪米特法则,也叫最少知道原则(封装)

​ 一个类,对于其他类,要知道的越少越好

​ 只和朋友通信,什么是朋友:1、类中的字段2、方法的返回值 3、方法的参数 4、方法中实例化出的对象

反例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/18
*/
public class demeterNegativeTest {
public static void main(String[] args) {
Person person = new Person();
person.shutdownComputer();
}
}

class Computer {
public void saveData() {
System.out.println("保存数据");
}

public void killProcess() {
System.out.println("关闭程序");
}

public void closeScreen() {
System.out.println("关闭屏幕");
}

public void powerOff() {
System.out.println("关闭电源");
}
}

class Person {
private Computer computer = new Computer();
//Person对于Computer的细节知道的太多。对于person只需要知道关机键在哪里就行,不需要知道细节
//这种做法提高了代码的复杂度
public void shutdownComputer() {
computer.saveData();
computer.killProcess();
computer.closeScreen();
computer.powerOff();
}
}

反例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/18
*/
public class DemeterPositiveTest {
public static void main(String[] args) {
Person person = new Person();
person.shutdownComputer();
}
}
class Computer {
public void saveData() {
System.out.println("保存数据");
}

public void killProcess() {
System.out.println("关闭程序");
}

public void closeScreen() {
System.out.println("关闭屏幕");
}

public void powerOff() {
System.out.println("关闭电源");
}
public void shutdown() {
saveData();
killProcess();
closeScreen();
powerOff();
}
}

class Person {
private Computer computer = new Computer();
public void shutdownComputer() {
computer.shutdown();
}
}

6.里氏替换原则

​ 任何能使用父类对象的地方,都应该能透明地替换为子类对象。子类对象替换父类对象,语法不会报错,业务逻辑也不会出问题

反例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 方法重写:在子类和父类中,出现返回类型相同、方法名相同、方法参数相同的方法时,构成方法重写
* 方法重写的两个限制:1.子类重写父类方法时,子类方法的修饰符不能比父类更严格 2.子类重写父类方法时,不能抛出比父类更多的异常
* 为什么要有这两个限制? 为了子类对象替换父类对象时,语法不报错,满足里氏替换原则。
*/
class Fu {
public void f1(){

}
public void f2(){}
}

class Zi extends Fu{
private void f1() {

}
public void f2() throws Exception{

}
}

7.组合优于继承

​ 类与类之间的关系有3种:

​ 1.继承:子类继承父类

​ 2.依赖: 一个类的对象作为另一个类的局部变量

​ 3.关联:一个类的对象作为另一个类的字段(属性)

​ “关联”可以细分为:1.组合(关系强,鸟和翅膀) 2.聚合(关系弱,大雁和雁群)

​ 组合优于继承中的组合没有分的那么细,其实指的的就是关联关系。

例A:

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
/**
* <p>
* 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class AppTest {
public static void main(String[] args) {
MySet set = new MySet();
// set.add(1);
// set.add(2);
// set.add(3);

Set set1 = new HashSet();
set1.add(4);
set1.add(5);
set.addAll(set1);
System.out.println(set.getCount());
//想要的结果是2结果返回了4
//原因是addAll方法回调了add方法。所有这种代码没有完成需求
}
}

class MySet extends HashSet {
private int count = 0;

@Override
public boolean add(Object o) {
count++;
return super.add(o);
}

@Override
public boolean addAll(Collection c) {
count += c.size();
return super.addAll(c);
}

public int getCount() {
return count;
}
}

例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
/**
* <p>
* 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)
* 针对例A中出现的问题,addAll方法会回调add方法,只需要去掉子类中的addAll方法,不去重写父类的addAll方法即可,
* 因为addAll会去回调add方法
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class AppTest {
public static void main(String[] args) {
MySet set = new MySet();
// set.add(1);
// set.add(2);
// set.add(3);

Set set1 = new HashSet();
set1.add(4);
set1.add(5);
set.addAll(set1);
System.out.println(set.getCount());
//结果返回的2,已经满足了需求。
//问题:目前满足需求的前提是addAll方法会去回调add方法,但是万一jdk在未来的版本里面不再回调add方法,那么我们自定义的MySet这段代码就会被"撼动"!
//例如:HashMap在JDK1.6、1.7、1.8中底层分别变换了3次!
}
}

class MySet extends HashSet {
private int count = 0;

@Override
public boolean add(Object o) {
count++;
return super.add(o);
}


public int getCount() {
return count;
}
}

例C:

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
/**
* <p>
* 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)
* 针对例B中出现的问题,MySet必需依赖一个事实:addAll方法必需回调add方法,但是JDK未来的版本无法做这个保证
* 修改代码如下:我们亲自重写addAll方法,保证一定会回调add方法
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class AppTest {
public static void main(String[] args) {
MySet set = new MySet();
// set.add(1);
// set.add(2);
// set.add(3);

Set set1 = new HashSet();
set1.add(4);
set1.add(5);
set.addAll(set1);
System.out.println(set.getCount());
//结果:2
//问题1:如果在新的JDK版本中,突然多了一个添加元素到集合的方法:addSome,这个方法是我们始料未及的,我们的MySet根本没有重写addSome方法。
//这样在新版本中,我们的MySet也继承了addSome方法,当使用addSome方法添加元素时,就不会去统计元素添加的记录。
//问题2:我们重写了addAll和add方法,但是在HashSet的所有方法中,可能会有一些方法依赖于addAll和add方法。我们没头没脑地重写
//了别人的方法,就会导致其他依赖于这些方法的方法出现问题!
}
}

class MySet extends HashSet {
private int count = 0;

@Override
public boolean add(Object o) {
count++;
return super.add(o);
}

@Override
public boolean addAll(Collection c) {
boolean modified = false;
for (Object e : c)
if (add(e))
modified = true;
return modified;
}

public int getCount() {
return count;
}
}

例D:

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
/**
* <p>
* 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)
* 针对例C中出现的问题
* 修改代码如下:1.我们不再重写addAll和add方法。2我们额外制作两个方法addAll2和add2方法,来替换addAll和add方法,而且还要在类的api
* 文档里面说明方法怎么使用。
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class AppTest {
public static void main(String[] args) {
MySet set = new MySet();
// set.add(1);
// set.add(2);
// set.add(3);

Set set1 = new HashSet();
set1.add(4);
set1.add(5);
set.addAll2(set1);
System.out.println(set.getCount());
//结果:2
//此时,代码勉强满足了需求
//问题1:用户必须看了api文档才能知道怎么使用addAll2和add2方法,而且方法名不能写错
//问题2:万一在将来的某个版本JDK中,恰恰也出现了addAll2和add2方法,那就没辙了
//难道不能用继承了吗?
}
}

class MySet extends HashSet {
private int count = 0;

public boolean add2(Object o) {
count++;
return super.add(o);
}

public boolean addAll2(Collection c) {
boolean modified = false;
for (Object e : c)
if (add2(e))
modified = true;
return modified;
}

public int getCount() {
return count;
}
}

例E:

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
/**
* <p>
* 需求:制作一个集合,要求该集合能够记录曾经加过多少次元素。(不是统计某一时刻有多少元素)
* 针对例D中出现的问题
* 修改代码如下:1.我们的MySet不再继承HashSet 2.取而代之,我们让MySet和HashSet发生关联关系(组合)
* <p>
*
* @author xiemopeng
* @since 2020/9/16
*/
public class AppTest {
public static void main(String[] args) {
MySet set = new MySet();
// set.add(1);
// set.add(2);
// set.add(3);

Set set1 = new HashSet();
set1.add(4);
set1.add(5);
set.addAll(set1);
System.out.println(set.getCount());
//结果:2
//此时,可以说一声:完美
//问题1:难道以后不能用继承了吗?
//问题2:难道以后不能方法重写了吗?
//如果父类的作者和子类的作者不是同一个人,就不要继承,那么父类的作者不知道未来的子类会重写哪些方法,那么子类的作者也不知道未来的父类会修改或新增哪些方法
//如果父类和子类的作者是同一个人,那么可以大胆的使用继承,如果我们仅仅为了复用代码而且使用继承,难免会出现"沟通"上的问题。
}
}

class MySet {
private HashSet hashSet = new HashSet();
private int count = 0;

public boolean add(Object o) {
count++;
return hashSet.add(o);
}

public boolean addAll(Collection c) {
count += c.size();
return hashSet.addAll(c);
}

public int getCount() {
return count;
}
}

23种设计模式

1.简单工厂模式

反例:

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/21
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
Food food = new Hamburger();
food.eat();
//这种设计非常脆弱,因为只要作者修改了源代码,客户端也需要修改。这样服务端代码和客户端代码是耦合的
}
}
//================================================
//抽象产品
interface Food {
void eat();
}
//具体产品
class Hamburger implements Food{

@Override
public void eat() {
System.out.println("吃汉堡包");
}
}

正例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/21
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
Food food = SimpleFactory.getFood(3);
food.eat();
//简单工厂
//优点:1.把具体产品的类型从客户端,解耦出来
// 2.服务端如果修改了具体产品的类名, // 客户端不需要修改代码,符合面向接口编程
//缺点:
//1.客户端需要记住常量和具体产品的映射关系,例如1表示Hamburger 2表示RiceNoodle
//2.如果具体产品特别多,服务端的代码就会变得很臃肿
//3.最重要的是,客户端如果想要扩展新的具体产品,势必需要修改服务端源代码,这样会违反开闭原则
}
}

//================================================
//抽象产品
interface Food {
void eat();
}

//具体产品
class Hamburger implements Food {

@Override
public void eat() {
System.out.println("吃汉堡包");
}
}

//具体产品
class RiceNoodle implements Food {

@Override
public void eat() {
System.out.println("吃米线");
}
}

class SimpleFactory {
public static Food getFood(int i) {
Food food = null;
switch (i) {
case 1:
food = new Hamburger();
break;
case 2:
food = new RiceNoodle();
break;
default:
break;
}
return food;

}
}

2.工厂方法模式

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
/**
* <p>
* 工厂方法优点:1.具有简单工厂的优点,服务端修改了具体产品的类名,客户端不需要知道
* 2.当客户端需要扩展一个新的具体产品的时候,不需要修改服务端作者的源码,只需要自定义一个新的工厂即可
* <p>
* 疑问:1.我们知道简单工厂和工厂方法都有共同的优点,服务端修改了具体产品的类名,客户端不需要知道。
* 但是,现在的代码,客户端依赖于具体的工厂类名,如果作者修改了具体工厂的类名,那么客户端也需要随之修改,
* 绕了一圈又回去了!
* 解释:工厂的名称,视为接口,作者有责任,有义务保证工厂名称是稳定的。也就是说客户端虽然依赖于工厂名称,但是在IT业内
* 所有的工厂名称是趋于稳定的(不是100%不会变),至少工厂的名称要比具体产品的类名要更稳定!约定大于配置
* 疑问2:既然产品是我们自己扩展出来的,为什么不直接实名话具体产品呢?我们就是扩展产品的作者,想怎么改类名都可以自己把控,为什么
* 还要扩展产品工厂呢?
* 解释:作者在开发功能的时候,不只会开发一些抽象产品、具体产品、对应的工厂,还会配图搭配一些提前做好的框架!
*
* <p>
*
* @author xiemopeng
* @since 2020/9/21
*/
public class FactoryTest {
public static void main(String[] args) {
// FoodFactory foodFactory = new HamburgerFactory();
// Food food = foodFactory.getFood();
// food.eat();
FoodFactory bzFactory = new BZFactory();
Food food = bzFactory.getFood();
food.eat();
JudgeTaste.taste(bzFactory);
}
}

//客户端扩展的新产品工厂
class BZFactory implements FoodFactory {

@Override
public Food getFood() {
return new BZ();
}
}

//客户端扩展的新的具体产品
class BZ implements Food {

@Override
public void eat() {
System.out.println("吃包子");
}
}

//================================================
//抽象产品
interface Food {
void eat();
}

//具体产品
class Hamburger implements Food {

@Override
public void eat() {
System.out.println("吃汉堡包");
}
}

//具体产品
class RiceNoodle implements Food {

@Override
public void eat() {
System.out.println("吃米线");
}
}

interface FoodFactory {
Food getFood();
}

class HamburgerFactory implements FoodFactory {

@Override
public Food getFood() {
return new Hamburger();
}

}

class JudgeTaste {
public static void taste(FoodFactory foodFactory) {
Food food = foodFactory.getFood();
food.eat();
System.out.println("评委1,品尝");
Food food2 = foodFactory.getFood();
food2.eat();
System.out.println("评委2,品尝");
Food food3 = foodFactory.getFood();
food3.eat();
System.out.println("评委3,品尝");
}
}

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* <p>
* 针对工厂方法的问题,当有多个产品等级的时候(食物、饮料、甜品。。。。),工厂类就会很多
*修改代码如下,使用抽象工厂设计模式
优点:1.仍然有简单工厂和工厂方法的优点
2.抽象工厂把工厂类的数量减少了,无论多少个产品等级,工厂就一套
缺点:当产品等级变化时,会引起以前的工厂类代码修改,违反了开闭原则
总结:当产品等级比较固定时,可以使用抽象工厂
当产品等级经常变化,不建议使用抽象工厂
* <p>
*
* @author xiemopeng
* @since 2020/9/21
*/
public class FactoryTest {
public static void main(String[] args) {
Factory factory = new KFCFactory();
// Food food = factory.getFood();
// food.eat();
// Factory CMJFactory = new CMJFactory();
// Food food1 = CMJFactory.getFood();
// food1.eat();
JudgeTaste.taste(factory);

// DrinkFactory drinkFactory = new ColaFactory();
// Drink drink = drinkFactory.getDrink();
// drink.drink();
}
}


//================================================

interface Drink {
void drink();
}
class Cola implements Drink{

@Override
public void drink() {
System.out.println("喝可口可乐");
}
}
class SoybeanMilk implements Drink{

@Override
public void drink() {
System.out.println("喝豆浆");
}
}
//interface DrinkFactory {
// Drink getDrink();
//}
class KFCFactory implements Factory{

@Override
public Food getFood() {
return new Hamburger() {
};
}

@Override
public Drink getDrink() {
return new Cola() {
};
}
}
class CMJFactory implements Factory{

@Override
public Food getFood() {
return new RiceNoodle();
}

@Override
public Drink getDrink() {
return new SoybeanMilk() {
};
}
}


//抽象产品
interface Food {
void eat();
}

//具体产品
class Hamburger implements Food {

@Override
public void eat() {
System.out.println("吃汉堡包");
}
}

//具体产品
class RiceNoodle implements Food {

@Override
public void eat() {
System.out.println("吃米线");
}
}

interface Factory {
Food getFood();
Drink getDrink();
}

class JudgeTaste {
public static void taste(Factory factory) {
Food food = factory.getFood();
Drink drink = factory.getDrink();
food.eat();
drink.drink();
System.out.println("评委1,品尝");
Food food2 = factory.getFood();
Drink drink2 = factory.getDrink();
food2.eat();
drink2.drink();
System.out.println("评委2,品尝");
Food food3 = factory.getFood();
Drink drink3 = factory.getDrink();
food3.eat();
drink3.drink();
System.out.println("评委3,品尝");
}
}

4.代理模式

例A:

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
/**
* <p>
* 需求:制作一个能加减乘除的类
* 变化:添加日志功能,在每个方法执行时,在方法开始和结束时打印日志信息。
* <p>
*
* @author xiemopeng
* @since 2020/9/24
*/
public class DynamicProxyATest {
public static void main(String[] args) {
ICalc calc = new CalcImpl();
int add = calc.add(1, 2);
int sub = calc.sub(1, 2);
int mul = calc.mul(1, 2);
int div = calc.div(1, 2);
System.out.println("add = " + add);
System.out.println("sub = " + sub);
System.out.println("mul = " + mul);
System.out.println("div = " + div);
}


}

interface ICalc{
//加法
int add (int a,int b);
//减法
int sub (int a,int b);
//乘法
int mul (int a,int b);
//除法
int div (int a,int b);
}

class CalcImpl implements ICalc {

@Override
public int add(int a, int b) {
return a+b;
}

@Override
public int sub(int a, int b) {
return a-b;
}

@Override
public int mul(int a, int b) {
return a*b;
}

@Override
public int div(int a, int b) {
return a/b;
}

例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
/**
* <p>
* 需求:制作一个能加减乘除的类
* 针对A例中的变化,满足需求了。
* 但是有问题:1.代码重复 2.如果需求变化,要求将日志改成英文输出,就需要改很多地方 3.代码极具膨胀,核心代码和非核心代码耦合在一起
* 4.如果需求改变,要求加入求余,立方等等?
* <p>
*
* @author xiemopeng
* @since 2020/9/24
*/
public class DynamicProxyBTest {
public static void main(String[] args) {
ICalc calc = new CalcImpl();
int add = calc.add(1, 2);
int sub = calc.sub(1, 2);
int mul = calc.mul(1, 2);
int div = calc.div(1, 2);
System.out.println("add = " + add);
System.out.println("sub = " + sub);
System.out.println("mul = " + mul);
System.out.println("div = " + div);
}


}

interface ICalc {
//加法
int add(int a, int b);

//减法
int sub(int a, int b);

//乘法
int mul(int a, int b);

//除法
int div(int a, int b);
}

class CalcImpl implements ICalc {

@Override
public int add(int a, int b) {
System.out.println("add方法开始执行,入参:" + a + "," + b);
int i = a + b;
System.out.println("add方法执行结束,返回:" + i);
return i;
}

@Override
public int sub(int a, int b) {
System.out.println("sub方法开始执行,入参:" + a + "," + b);
int i = a - b;
System.out.println("sub方法执行结束,返回:" + i);
return i;
}

@Override
public int mul(int a, int b) {
System.out.println("mul方法开始执行,入参:" + a + "," + b);
int i = a * b;
System.out.println("mul方法执行结束,返回:" + i);
return i;
}

@Override
public int div(int a, int b) {
System.out.println("div方法开始执行,入参:" + a + "," + b);
int i = a / b;
System.out.println("div方法执行结束,返回:" + i);
return i;
}
}

例C:

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
/**
* <p>
* 需求:制作一个能加减乘除的类
* 针对B例中的问题,使用模板模式优化代码,提高代码重用性。
* 目前优点,解决的问题:1.代码重复 2.如果需求变化,要求将日志改成英文输出,就需要改很多地方
* 仍然存在的问题:
* 1.代码极具膨胀,核心代码和非核心代码耦合在一起
* 2.如果需求改变,要求加入求余,立方等等?
* <p>
*
* @author xiemopeng
* @since 2020/9/24
*/
public class DynamicProxyCTest {
public static void main(String[] args) {
CalcImpl calc = new CalcImpl();
int add = calc.add(1, 2);

int sub = calc.sub(1, 2);

int mul = calc.mul(1, 2);

int div = calc.div(1, 2);
// System.out.println("add = " + add);
// System.out.println("sub = " + sub);
// System.out.println("mul = " + mul);
// System.out.println("div = " + div);

//add方法开始执行,入参:[1, 2]
//add方法执行结束,返回:3
//sub方法开始执行,入参:[1, 2]
//sub方法执行结束,返回:-1
//mul方法开始执行,入参:[-1]
//mul方法执行结束,返回:2
//div方法开始执行,入参:[1, 2]
//div方法执行结束,返回:0
}


}

interface ICalc {
//加法
int add(int a, int b);

//减法
int sub(int a, int b);

//乘法
int mul(int a, int b);

//除法
int div(int a, int b);
}

class CalcImpl implements ICalc {

public void begin(String methodName, Object... parm) {
System.out.println(methodName + "方法开始执行,入参:" + Arrays.asList(parm).toString());
}

public void end(String methodName, int i) {
System.out.println(methodName + "方法执行结束,返回:" + i);
}

@Override
public int add(int a, int b) {
// System.out.println("add方法开始执行,入参:" + a + "," + b);
begin("add",a,b);
int i = a + b;
end("add",i);
// System.out.println("add方法执行结束,返回:" + i);
return i;
}

@Override
public int sub(int a, int b) {
// System.out.println("sub方法开始执行,入参:" + a + "," + b);
begin("sub",a,b);
int i = a - b;
end("sub",i);
// System.out.println("sub方法执行结束,返回:" + i);
return i;
}

@Override
public int mul(int a, int b) {
// System.out.println("mul方法开始执行,入参:" + a + "," + b);
begin("mul",a,b);
int i = a * b;
end("mul",i);
// System.out.println("mul方法执行结束,返回:" + i);
return i;
}

@Override
public int div(int a, int b) {
// System.out.println("div方法开始执行,入参:" + a + "," + b);
begin("div",a,b);
int i = a / b;
end("div",i);
// System.out.println("div方法执行结束,返回:" + i);
return i;
}
}

例D:

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
107
108
109
/**
* <p>
* 需求:制作一个能加减乘除的类
* 针对C例中的问题,我们来使用动态代理(jdk代理)
* <p>
* 动态代理完美解决以问题:
* 1.代码极具膨胀,核心代码和非核心代码耦合在一起
* 2.如果需求改变,要求加入求余,立方等等?
* <p>
*
* @author xiemopeng
* @since 2020/9/24
*/
class MyInvocationHandler implements InvocationHandler {
private Object target;

public MyInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "方法开始执行,入参:" + Arrays.asList(args).toString());
Object invoke = method.invoke(target, args);
System.out.println(method.getName() + "方法执行结束,返回:" + invoke + ",结果类型:" + invoke.getClass().getSimpleName());
return invoke;
}
}

public class DynamicProxyDTest {
public static void main(String[] args) {
ICalc c = new CalcImpl();
ICalc calc = (ICalc) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{ICalc.class}, new MyInvocationHandler(c));
calc.add(1, 2);
calc.sub(1, 2);
calc.mul(1, 2);
calc.div(1, 2);
calc.square(2, 2);

}


}

interface ICalc {
//加法
int add(int a, int b);

//减法
int sub(int a, int b);

//乘法
int mul(int a, int b);

//除法
int div(int a, int b);

//平方
double square(int a, int b);
}

class CalcImpl implements ICalc {

public void begin(String methodName, Object... parm) {
System.out.println(methodName + "方法开始执行,入参:" + Arrays.asList(parm).toString());
}

public void end(String methodName, int i) {
System.out.println(methodName + "方法执行结束,返回:" + i);
}

@Override
public int add(int a, int b) {
// System.out.println("add方法开始执行,入参:" + a + "," + b);
int i = a + b;
// System.out.println("add方法执行结束,返回:" + i);
return i;
}

@Override
public int sub(int a, int b) {
// System.out.println("sub方法开始执行,入参:" + a + "," + b);
int i = a - b;
// System.out.println("sub方法执行结束,返回:" + i);
return i;
}

@Override
public int mul(int a, int b) {
// System.out.println("mul方法开始执行,入参:" + a + "," + b);
int i = a * b;
// System.out.println("mul方法执行结束,返回:" + i);
return i;
}

@Override
public int div(int a, int b) {
// System.out.println("div方法开始执行,入参:" + a + "," + b);
int i = a / b;
// System.out.println("div方法执行结束,返回:" + i);
return i;
}

@Override
public double square(int a, int b) {
double i = Math.pow(a, b);
return i;
}
}
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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/25
*/
public class BuilderTest {
public static void main(String[] args) {
//游戏电脑
ComputerBuilder builder = new GameComputer();
Director director = new Director();
Computer build = director.build(builder);
System.out.println(build);
}
}
//=========================================================
@Data
class Computer {
private String cpu;
private String gpu;
private String hd;
}

interface ComputerBuilder{
void setCpu();
void setGpu();
void setHd();
Computer build();
}

class GameComputer implements ComputerBuilder{
private Computer computer = new Computer();
@Override
public void setCpu() {
computer.setCpu("I7");
}

@Override
public void setGpu() {
computer.setGpu("GTX2080ti");
}

@Override
public void setHd() {
computer.setHd("16G");
}

@Override
public Computer build() {
return computer;
}
}

class Director {
public Computer build(ComputerBuilder computerBuilder) {
computerBuilder.setCpu();
computerBuilder.setGpu();
computerBuilder.setHd();
return computerBuilder.build();
}
}

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
/**
* <p>
* <p>
*
* @author xiemopeng
* @since 2020/9/25
*/
public class BuilderTest {
public static void main(String[] args) {
//游戏电脑
ComputerBuilder builder = new GameComputer();
Director director = new Director();
Computer build = director.build(builder);
System.out.println(build);
}
}
//=========================================================
@Data
class Computer {
private String cpu;
private String gpu;
private String hd;
}

interface ComputerBuilder{
void setCpu();
void setGpu();
void setHd();
Computer build();
}

class GameComputer implements ComputerBuilder{
private Computer computer = new Computer();
@Override
public void setCpu() {
computer.setCpu("I7");
}

@Override
public void setGpu() {
computer.setGpu("GTX2080ti");
}

@Override
public void setHd() {
computer.setHd("16G");
}

@Override
public Computer build() {
return computer;
}
}

class Director {
public Computer build(ComputerBuilder computerBuilder) {
computerBuilder.setCpu();
computerBuilder.setGpu();
computerBuilder.setHd();
return computerBuilder.build();
}
}
-------------本文结束感谢您的阅读-------------