LieBrother

当才华撑不起野心时,应该静下心来学习;当能力驾驭不了目标时,应该沉下心来历练。


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

行为型模式:迭代器模式

发表于 2019-02-28   |     |   阅读次数

LieBrother原文:
行为型模式:迭代器模式

景

十一大行为型模式之六:迭代器模式。

简介

姓名 :迭代器模式

英文名 :Iterator Pattern

价值观 :人生没有回头路

个人介绍 :

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
(来自《设计模式之禅》)

你要的故事

大家伙听歌频率高么?是不是经常听歌曲来放松心情?我是经常会听歌,心情不好的时候听歌,心情好的时候也听歌。。。今天讲的迭代器模式,我们就拿听歌这件事来说说,大家都知道听歌有几种模式:单曲循环、列表循环、随机等等。。。现在网易云音乐还多了一个心动模式。

既然说到迭代器模式,那这里就要着重讲讲列表循环这个听歌模式,其他的就先抛到脑后。在列表循环中,歌曲从第一条播放到最后一条,也就是一个遍历歌单的过程。我们有 2 种实现方式,一种是没有迭代器,通过获取歌单,用 for 循环遍历每一个歌曲,然后播放;另外一种是使用迭代器,获取歌单的一个迭代器,通过迭代器来遍历每一个歌曲,然后播放。下面我们就用代码来实现这 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
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
public class NoIteratorTest {

public static void main(String[] args) {
NetEaseMusic1 netEaseMusic1 = new NetEaseMusic1();
netEaseMusic1.listenToMusicByLoop();
}
}

/**
* 网易云音乐
*/
class NetEaseMusic1 {

private IList1 songList;

public NetEaseMusic1() {
songList = new SongList1(3);
songList.add(new Song("让我留在你身边", "陈奕迅"));
songList.add(new Song("你曾是少年", "SHE"));
songList.add(new Song("Perfect", "Ed Sheeran"));
}

/**
* 列表循环
*/
public void listenToMusicByLoop() {
for (int i = 0; i < songList.size(); i++) {
System.out.println("听歌:" + ((ISong)songList.get(i)).getSongInfo());
}
}

}

/**
* 容器接口
*/
interface IList1 {

void add(Object object);

Object get(int index);

int size();
}

/**
* 歌单
*/
class SongList1 implements IList1 {

private ISong[] songs;
private int index;
private int size;

public SongList1(int size) {
songs = new ISong[size];
index = 0;
size = 0;
}

@Override
public void add(Object object) {
songs[index++] = (ISong) object;
size ++;
}

@Override
public Object get(int index) {
if (index < size) {
return songs[index];
}
return null;
}

@Override
public int size() {
return size;
}
}


/**
* 歌曲接口
*/
interface ISong {
String getSongInfo();
}

/**
* 歌曲
*/
class Song implements ISong{

private String name;
private String singer;

public Song(String name, String singer) {
this.name = name;
this.singer = singer;
}

@Override
public String getSongInfo() {
return this.name + "--" + this.singer;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSinger() {
return singer;
}

public void setSinger(String singer) {
this.singer = singer;
}

}

打印结果:
听歌:让我留在你身边--陈奕迅
听歌:你曾是少年--SHE
听歌:Perfect--Ed Sheeran

我们定义了 ISong 接口,里面有个 getSongInfo() 方法来获取歌曲信息,用 Song 类来定义歌曲。没有用 Java 自带的容器来存放歌曲,这里实现了一个自定义容器接口 IList1,定义 SongList1 来做歌曲的容器,为什么不用 Java 自带的 ArrayList 等等?因为 Java 自带的已经实现了迭代器功能了,我们这里自定义其实就是在模仿自带的容器的实现。NetEaseMusic1 类是充当网易云音乐客户端,在 listenToMusicByLoop() 方法中,我们可以看到是获取了歌单 songList,然后一个一个遍历,这是没有使用迭代器的代码。

下面看一下使用迭代器的代码是怎么样的。

用迭代器实现遍历

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
130
public class IteratorTest {

public static void main(String[] args) {
NetEaseMusic2 netEaseMusic2 = new NetEaseMusic2();
netEaseMusic2.listenToMusicByLoop();
}

}

/**
* 网易云音乐
*/
class NetEaseMusic2{

private IList2 songList;

public NetEaseMusic2() {
songList = new SongList2(3);
songList.add(new Song("让我留在你身边", "陈奕迅"));
songList.add(new Song("你曾是少年", "SHE"));
songList.add(new Song("Perfect", "Ed Sheeran"));
}

/**
* 列表循环
*/
public void listenToMusicByLoop() {
IIterator iterator = songList.iterator();
while (iterator.hasNext()) {
System.out.println("听歌:" + ((ISong)iterator.next()).getSongInfo());
}

}

}

/**
* 容器接口
*/
interface IList2 {

IIterator iterator();

void add(Object object);

Object get(int index);

int size();
}

/**
* 歌单
*/
class SongList2 implements IList2 {

private ISong[] songs;
private int index;
private int size;

public SongList2(int size) {
songs = new ISong[size];
index = 0;
size = 0;
}

@Override
public IIterator iterator() {
return new IteratorImpl(this);
}

@Override
public void add(Object object) {
songs[index++] = (ISong) object;
size ++;
}

@Override
public Object get(int index) {
if (index < size) {
return songs[index];
}
return null;
}

@Override
public int size() {
return size;
}
}


/**
* 迭代器
*/
interface IIterator {
Object next();
boolean hasNext();
}

/**
* 迭代器实现类
*/
class IteratorImpl implements IIterator {

private IList2 list;
private int index;

public IteratorImpl(IList2 list) {
this.list = list;
this.index = 0;
}

@Override
public Object next() {
return list.get(index++);
}

@Override
public boolean hasNext() {
if (index < list.size()) {
return true;
}
return false;
}
}

打印结果:
听歌:让我留在你身边--陈奕迅
听歌:你曾是少年--SHE
听歌:Perfect--Ed Sheeran

代码中我们自定义了一个迭代器接口 IIterator 和迭代器具体实现类 IteratorImpl,有关键的 2 个方法,hasNext() 判断是否有存在下一个元素,next() 获取下一个元素。而 IList2 接口则比 IList1 接口多了一个获取迭代器的方法 iterator(),这让网易云音乐在遍历歌单的时候,不用直接使用 songList 来遍历,而可以通过 songList.iterator() 获取迭代器来实现遍历的过程。NetEaseMusic2.listenToMusicByLoop() 这个方法里面就直接获取迭代器来遍历了。

代码:
Iterator Pattern

总结

迭代器模式是所有设计模式中使用最广泛的,有不少开发同学知道迭代器,但是不知道它是设计模式的。虽然迭代器的代码会比没有迭代器的代码复杂,但是加上迭代器可以让容器有统一的遍历代码风格,不用各自去实现遍历方法,有更好的封装性。在 Java 中,迭代器已经运用很广泛,比如 Java 中访问 MySQL 获取数据就是用迭代器来遍历数据的。好了,迭代器模式就讲到这,大家知道的知识就不多说啦。

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

行为型模式:策略模式

行为型模式:责任链模式

行为型模式:命令模式

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

公众号

行为型模式:策略模式

发表于 2019-02-26   |     |   阅读次数

LieBrother原文:
行为型模式:策略模式

景

十一大行为型模式之五:策略模式。

简介

姓名 :策略模式

英文名 :Strategy Pattern

价值观 :集计谋于一身

个人介绍 :
Define a family of algorithms,encapsulate each one,and make them interchangeable.
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
(来自《设计模式之禅》)

你要的故事

先看一张拍得不好看的图片

洗衣机面板

每天上完班回到家第一件事情是干啥?有人一进门就躺在沙发上闭目养神、有人一进门躺在沙发上玩手机、有人一进门就陪自己的小宠物玩等等。而我进门第一件事就是洗澡,洗完澡很容易就把一整天的疲惫感给消除掉,然后就可以开始美好的下班时光。现实没那么美好,洗完澡后还要洗衣服,大学手洗了 4 年的衣服,一出来工作,宿舍第一必需品就是洗衣机。细看洗衣机,有很多种洗衣类型,比如:标准、大物、快洗、轻柔。洗衣类型的区别在于洗衣服的过程不一样,洗衣过程包括有浸泡、洗涤、漂洗、脱水,还有洗衣服的时间也不一样。细想可以发现这 4 种洗衣类型其实是洗衣服的 4 种不同的策略,也即是 4 种不同的算法。根据这个思路,我们可以用代码实现它,定义一个接口 WashingStrategy 定义洗衣服类型,而这些类型都有各自的洗衣过程,比如标准洗衣类型就包括浸泡、洗涤、漂洗、脱水,而快洗则只包括洗涤、漂洗、脱水。而我们洗衣服则需要选择某个洗衣类型后,洗衣机就开始工作了。过程如下代码所示。

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
public class StrategyTest {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              

public static void main(String[] args) {
WashingStrategy washingStrategy = new StandardWashingStrategy();
WashingMachine washingMachine = new WashingMachine(washingStrategy);
washingMachine.washingClothes();
}

}

/**
* 洗衣类型
*/
interface WashingStrategy {
void washing();
}

/**
* 洗衣机
*/
class WashingMachine {
private WashingStrategy washingStrategy;

public WashingMachine(WashingStrategy washingStrategy) {
this.washingStrategy = washingStrategy;
}

public void washingClothes() {
this.washingStrategy.washing();
}
}

/**
* 标准
*/
class StandardWashingStrategy implements WashingStrategy{

@Override
public void washing() {
System.out.println("标准流程:");
System.out.println("[浸泡] 10 分钟");
System.out.println("[洗涤] 2 次,每次 15 分钟");
System.out.println("[漂洗] 1 次,每次 10 分钟");
System.out.println("[脱水] 5 分钟");
System.out.println("总共耗时:55 分钟");
}
}

/**
* 快洗
*/
class QuickWashingStrategy implements WashingStrategy {

@Override
public void washing() {
System.out.println("快洗流程:");
System.out.println("[洗涤] 1 次,每次 10 分钟");
System.out.println("[漂洗] 1 次,每次 10 分钟");
System.out.println("[脱水] 5 分钟");
System.out.println("总共耗时:25 分钟");
}
}

/**
* 大物
*/
class BigClothesWashingStrategy implements WashingStrategy {

@Override
public void washing() {
System.out.println("大物流程:");
System.out.println("[浸泡] 30 分钟");
System.out.println("[洗涤] 3 次,每次 15 分钟");
System.out.println("[漂洗] 2 次,每次 10 分钟");
System.out.println("[脱水] 5 分钟");
System.out.println("总共耗时:100 分钟");
}
}

/**
* 轻柔
*/
class SoftWashingStrategy implements WashingStrategy {

@Override
public void washing() {
System.out.println("轻柔流程:");
System.out.println("[浸泡] 10 分钟");
System.out.println("[漂洗] 2 次,每次 15 分钟");
System.out.println("[脱水] 5 分钟");
System.out.println("总共耗时:45 分钟");
}
}

标准流程:
[浸泡] 10 分钟
[洗涤] 2 次,每次 15 分钟
[漂洗] 1 次,每次 10 分钟
[脱水] 5 分钟
总共耗时:55 分钟

是不是感觉策略模式很简单呢?上面代码就是完整的策略模式示例,是不是感觉有些问题,这 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
/**
* 洗衣类型选择
*/
class WashingFactory {

public static WashingStrategy getWashingStrategy(String type) {
if ("Quick".equals(type)) {
return new QuickWashingStrategy();
}
if ("BigClothes".equals(type)) {
return new BigClothesWashingStrategy();
}
if ("Soft".equals(type)) {
return new SoftWashingStrategy();
}
return new StandardWashingStrategy();
}
}

public class StrategyTest {

public static void main(String[] args) {
WashingStrategy washingStrategy2 = WashingFactory.getWashingStrategy("Soft");
WashingMachine washingMachine2 = new WashingMachine(washingStrategy2);
washingMachine2.washingClothes();
}
}

打印结果:
轻柔流程:
[浸泡] 10 分钟
[漂洗] 2 次,每次 15 分钟
[脱水] 5 分钟
总共耗时:45 分钟

代码中使用 WashingFactory 来封装 4 种策略,使得策略没有对外暴露,我们也了解到设计模式之间具有互补的关系,有些时候并不是单独存在的。

代码:
Strategy Pattern

总结

策略模式是一个很好的封装各种复杂处理的设计模式,让使用者根据自己的选择来选中策略,而不用修改其他代码。当策略太多的时候,可能造成使用方变得复杂、难管理多个策略的问题,利用工厂方法模式可以很好的解决这个难题。这其中也是一个见招拆招的问题,设计模式在真正运用中也是这样子的,遇到问题使用恰当的设计模式去解决问题。

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

公众号之设计模式系列文章

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

公众号

行为型模式:责任链模式

发表于 2019-02-25   |     |   阅读次数

LieBrother原文:
行为型模式:责任链模式

景色

十一大行为型模式之四:责任链模式。

简介

姓名 :责任链模式

英文名 :Chain of Responsibility Pattern

价值观 :责任归我

个人介绍 :
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
(来自《设计模式之禅》)

你要的故事

快要金三银四了,很多同学蠢蠢欲动想要去外面看看世界,而大家有没有提前了解各大企业的面试流程呢?这里我就给大家科普一下大多数互联网企业的面试流程,正好责任链模式用得上。

在互联网企业中,程序员这个岗位的招聘流程大同小异,而一般至少都会有 3 轮面试,分别是 2 轮技术面和 1 轮 HR 面。而这几轮面试都是层层递进的,最开始面试一般是组长面试,通过之后就是部门领导面试,再通过之后就是 HR 面试,HR 面试通过之后就可以成功拿到 Offer 了。

故事从小明参加某公司的面试开始,某公司的招聘流程就是上面说的 3 轮面试。招聘流程的面试官分别是:第一面是组长老刚,第二面是部门经理老孙,第三面也就是终面是 HR 老刘。为什么说这个场景符合责任链模式呢?首先不管是组长还是部门经理还是 HR,他们都作为面试官,面试官赋予他们的权利是去面试来公司面试的同学,而面试的结果是可传递性的,也就是如果面试通过,就会到下一轮面试,最终成为一条传递链。我们用代码模拟这个过程。

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
public class ChainOfResponsibilityTest {

public static void main(String[] args) {
Interviewee interviewee = new Interviewee("小明");
TeamLeader teamLeader = new TeamLeader("老刚");
DepartMentManager departMentManager = new DepartMentManager("老孙");
HR hr = new HR("老刘");
// 设置面试流程
teamLeader.setNextInterviewer(departMentManager);
departMentManager.setNextInterviewer(hr);
// 开始面试
teamLeader.handleInterview(interviewee);
}

}

/**
* 面试者
*/
class Interviewee {

private String name;

private boolean teamLeaderOpinion;
private boolean departMentManagerOpinion;
private boolean hrOpinion;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public boolean isTeamLeaderOpinion() {
return teamLeaderOpinion;
}

public void setTeamLeaderOpinion(boolean teamLeaderOpinion) {
this.teamLeaderOpinion = teamLeaderOpinion;
}

public boolean isDepartMentManagerOpinion() {
return departMentManagerOpinion;
}

public void setDepartMentManagerOpinion(boolean departMentManagerOpinion) {
this.departMentManagerOpinion = departMentManagerOpinion;
}

public boolean isHrOpinion() {
return hrOpinion;
}

public void setHrOpinion(boolean hrOpinion) {
this.hrOpinion = hrOpinion;
}
}

/**
* 面试官
*/
abstract class Interviewer {

protected String name;
protected Interviewer nextInterviewer;

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

public Interviewer setNextInterviewer(Interviewer nextInterviewer) {
this.nextInterviewer = nextInterviewer;
return this.nextInterviewer;
}

public abstract void handleInterview(Interviewee interviewee);

}

/**
* 组长
*/
class TeamLeader extends Interviewer {

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

@Override
public void handleInterview(Interviewee interviewee) {
System.out.println("组长[" + this.name + "]面试[" + interviewee.getName() + "]同学");
interviewee.setTeamLeaderOpinion(new Random().nextBoolean());
if (interviewee.isTeamLeaderOpinion()) {
System.out.println("[" + interviewee.getName() + "]同学组长轮面试通过");
this.nextInterviewer.handleInterview(interviewee);
} else {
System.out.println("[" + interviewee.getName() + "]同学组长轮面试不通过");
}
}
}

/**
* 部门经理
*/
class DepartMentManager extends Interviewer {

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

@Override
public void handleInterview(Interviewee interviewee) {
System.out.println("部门经理[" + this.name + "]面试[" + interviewee.getName() + "]同学");
interviewee.setDepartMentManagerOpinion(new Random().nextBoolean());
if (interviewee.isDepartMentManagerOpinion()) {
System.out.println("[" + interviewee.getName() + "]同学部门经理轮面试通过");
this.nextInterviewer.handleInterview(interviewee);
} else {
System.out.println("[" + interviewee.getName() + "]同学部门经理轮面试不通过");
}
}
}

/**
* HR
*/
class HR extends Interviewer {

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

@Override
public void handleInterview(Interviewee interviewee) {
System.out.println("HR[" + this.name + "]面试[" + interviewee.getName() + "]同学");
interviewee.setHrOpinion(new Random().nextBoolean());
if (interviewee.isHrOpinion()) {
System.out.println("[" + interviewee.getName() + "]同学HR轮面试通过, 恭喜拿到 Offer");
} else {
System.out.println("[" + interviewee.getName() + "]同学HR轮面试不通过");
}
}
}

打印结果:
组长[老刚]面试[小明]同学
[小明]同学组长轮面试通过
部门经理[老孙]面试[小明]同学
[小明]同学部门经理轮面试通过
HR[老刘]面试[小明]同学
[小明]同学HR轮面试通过, 恭喜拿到 Offer

上面代码打印结果是小明通过层层筛选,通过了面试,拿到了 Offer。下面的图来展现这次面试的流程。

流程图

讲解一下代码,Interviewee 是面试者,对于企业来说这个面试者来面试的过程会有 3 个关键标识,就是 3 位面试官对这位面试者的评价,只有都评价好才能通过面试拿到 Offer,所以 Interviewee 类有 3 位面试官的面试结果。Interviewer 是面时官,企业中面试官不是一个职位,而是一个工作,帮企业招到合适的人才,所以它是一个抽象类,有一个抽象方法就是去面试应聘者,具体面试过程又各面试官实现,而因为这个面试会有结果反馈,结果好的会进入下一轮面试,所以会有下一个面试官的对象引用,责任链模式也就在这里体现。TeamLeader、DepartMentManager、HR 则为公司的不同职位,而这些职位当公司需要招聘时,都需要去充当面试官,所以都继承了 Interviewer。这整个过程就构成了责任链模式代码示例,希望在金三银四各位蠢蠢欲动的朋友们都能闯到最后一关拿下 HR 姐姐。

代码:
Chain of Responsibility Pattern

总结

责任链模式很好的把处理的逻辑封装起来,在代码中,我们看到的只是组长面试,但是其实背后隐藏着部门经理和HR的面试。责任链是不是看起来很熟悉?在开发 Java Web 项目的时候是不是有用到过?Filter 过滤器里面就是用责任链模式来实现的。上面代码还用到了另一个模式,不明确指出来了,考验大家学习这些设计模式的效果,知道的同学可以留言说一下。

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

公众号之设计模式系列文章

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

公众号

行为型模式:命令模式

发表于 2019-02-22   |     |   阅读次数

LieBrother原文:
行为型模式:命令模式

景

十一大行为型模式之三:命令模式。

简介

姓名 :命令模式

英文名 :Command Pattern

价值观 :军令如山

个人介绍 :

Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
(来自《设计模式之禅》)

你要的故事

作为一个程序猿,我们每天都在经历着命令模式,技术经理把需求任务分配给工程师开发,有时因为第三方或者其他不可抗拒的因素导致需求停止开发。这种工作模式就是命令模式。好了,开始故事了。小明在 XX 科技公司做一个安静的程序猿,有一天技术经理给他分配了一个任务:新增黑名单,也就是在他们系统的某个模块里面可以手工对电话打黑名单标签的功能。小明接到任务后就立马开发,在开发了 2 天之后,因为战略原因,技术经理大明暂停了这个开发任务,接下来我们通过非命令模式和命令模式 2 种代码实现来体现这个过程。在这个场景中,为了简单,我们假定技术经理大明手下只有小明一个开发人员。

非命令模式

非命令模式也就是不使用命令模式的代码实现。代码中,我们出现了 Developer 开发人,开发同学是接受技术经理传达的任务,技术经理让他开发哪个需求就开发哪个需求,如果项目有问题需要中断,也需要技术经理评估后传达给开发同学,所以 Developer 有 2 个方法,分别是 develop() 开发需求和 suspend() 暂停需求。 Requirement 则为需求类,TechnicalManager1 则为技术经理类,他有一个方法 action(),通过这个方法来指定开发同学开发任务或者暂停任务。

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
public class NoCommandTest {

public static void main(String[] args) {
Developer xiaoMing = new Developer("小明");
Requirement requirement = new Requirement("新增黑名单");
TechnicalManager1 technicalManager2 = new TechnicalManager1("大明");
technicalManager2.setDeveloper(xiaoMing);
technicalManager2.action(requirement, "develop");
System.out.println("开发了 2 天,需求变故,需要暂停。。。");
technicalManager2.action(requirement, "suspend");
}

}

/**
* 开发人员
*/
class Developer {

private String name;

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

public void develop(Requirement requirement) {
System.out.println(this.name + " 开始开发需求:" + requirement.getName());
}

public void suspend(Requirement requirement) {
System.out.println(this.name + " 停止开发需求:" + requirement.getName());
}

public String getName() {
return name;
}

}

/**
* 需求
*/
class Requirement {
private String name;

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

public String getName() {
return name;
}

}

/**
* 技术经理
*/
class TechnicalManager1 {

private String name;

private Developer developer;

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

public void setDeveloper(Developer developer) {
this.developer = developer;
}

public void action(Requirement requirement, String type) {
if ("develop".equals(type)) {
this.developer.develop(requirement);
} else if ("suspend".equals(type)) {
this.developer.suspend(requirement);
}
}

}

打印结果:
小明 开始开发需求:新增黑名单
开发了 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
public class CommandTest {

public static void main(String[] args) {
Developer xiaoMing = new Developer("小明");
Command developCommand = new DevelopCommand(xiaoMing);
Command suspendCommand = new SuspendCommand(xiaoMing);
Requirement requirement = new Requirement("新增黑名单");
TechnicalManager2 technicalManager = new TechnicalManager2("大明");
technicalManager.setCommand(developCommand);
technicalManager.action(requirement);
System.out.println("开发了 2 天,需求变故,需要暂停。。。");
technicalManager.setCommand(suspendCommand);
technicalManager.action(requirement);

}

}

/**
* 命令
*/
abstract class Command {

protected Developer developer;

public Command(Developer developer) {
this.developer = developer;
}

public abstract void execute(Requirement requirement);
}

/**
* 开始开发
*/
class DevelopCommand extends Command {

public DevelopCommand(Developer developer) {
super(developer);
}

@Override
public void execute(Requirement requirement) {
this.developer.develop(requirement);
}
}

/**
* 开发中断
*/
class SuspendCommand extends Command {

public SuspendCommand(Developer developer) {
super(developer);
}

@Override
public void execute(Requirement requirement) {
this.developer.suspend(requirement);
}
}

/**
* 技术经理
*/
class TechnicalManager2 {

private String name;
private Command command;

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

public void action(Requirement requirement) {
this.command.execute(requirement);
}

public void setCommand(Command command) {
this.command = command;
}
}

打印结果:
小明 开始开发需求:新增黑名单
开发了 2 天,需求变故,需要暂停。。。
小明 停止开发需求:新增黑名单

代码中用 Command 来抽象下达任务,而技术经理 TechnicalManager2 并没有和 Developer 有直接的关系,而是 TechnicalManager2 和 Command 建立的联系,Command 和 Developer 建立了联系。这样子把大明和小明的强依赖关系给剥离开,而新增一个下达写周报的任务也很简单,在 Developer 中新增一个处理写周报的方法,新增一个写周报的 Command 子类,就可以了,TechnicalManager2 如上面所愿不用修改。这就是完整的一个命令模式代码。

代码:
Command Pattern

总结

从文章中我们就可以看到,利用命令模式能够进行类的解耦,让调用者和接受者没有任何关系,也通过对行为的抽象,让新增其他行为变得清晰容易,也就是可扩展性大大增加。

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

公众号之设计模式系列文章

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

行为型模式:中介者模式

发表于 2019-02-18   |     |   阅读次数

LieBrother公众号原文:
行为型模式:中介者模式

景

十一大行为型模式之二:中介者模式。

简介

姓名 :中介者模式

英文名 :Mediator Pattern

价值观 :让你体验中介是无所不能的存在

个人介绍 :

Define an object that encapsulates how a set of objects interact.Mediator promotes loose coupling by keeping objects from referring to each other explicitly,and it lets you vary their interaction independently.
用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
(来自《设计模式之禅》)

你要的故事

看了这小伙子的名字,大家会很直观的想到那些拿了我们半个月租的租房中介同学。在这不讲讲房租中介同学,以后可没机会了。大家现在找房子,不管是买还是租,一登录什么安居客、58同城,是不是有 80% 是经纪人房源,说 80% 还是比较保守的,经历了 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
49
50
51
52
53
54
55
56
public class PersonalTest {

public static void main(String[] args) {
Tenant xiaoMing = new Tenant("小明");
xiaoMing.lookAtHouse();
}

}

class Tenant {
private String name;
private XiaoQuFangLandlord xiaoQuFangLandlord2 = new XiaoQuFangLandlord();
private NongMinFangLandlord nongMinFangLandlord2 = new NongMinFangLandlord();

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

public void lookAtHouse() {
System.out.println(this.name +"想看农民房");
nongMinFangLandlord2.supply();
System.out.println(this.name + "想看小区房");
xiaoQuFangLandlord2.supply();
}

}

/**
* 房东
*/
abstract class Landlord {
// 提供房子
public abstract void supply();
}

class XiaoQuFangLandlord extends Landlord {

@Override
public void supply() {
System.out.println("小区房的房东提供一间小区房");
}
}

class NongMinFangLandlord extends Landlord {

@Override
public void supply() {
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
63
64
65
66
67
68
69
public class MediatorTest {

public static void main(String[] args) {
System.out.println("小明想要看小区房和农民房");
Tenant2 xiaoMing = new Tenant2("小明", Arrays.asList("XiaoQuFang", "NongMinFang"));
xiaoMing.lookAtHouse();
}


}

/**
* 租客
*/
class Tenant2 {
private String name;
private List<String> wantTypes;

private RentingMediator rentingMediator = new RentingMediator();

public Tenant2(String name, List<String> wantTypes) {
this.name = name;
this.wantTypes = wantTypes;
}

public void lookAtHouse() {
rentingMediator.supplyHouse(wantTypes);
}

}

/**
* 中介抽象类
*/
abstract class Mediator {
// 看房
public abstract void supplyHouse(List<String> types);
}

/**
* 租房中介
*/
class RentingMediator extends Mediator {

private XiaoQuFangLandlord xiaoQuFangLandlord;
private NongMinFangLandlord nongMinFangLandlord;

public RentingMediator() {
xiaoQuFangLandlord = new XiaoQuFangLandlord();
nongMinFangLandlord = new NongMinFangLandlord();
}

@Override
public void supplyHouse(List<String> types) {
System.out.println("经纪人提供了如下房源");
if (types.contains("XiaoQuFang")) {
xiaoQuFangLandlord.supply();
}
if (types.contains("NongMinFang")) {
nongMinFangLandlord.supply();
}
}
}

打印结果:
小明想要看小区房和农民房
经纪人提供了如下房源
小区房的房东提供一间小区房
农民房的房东提供一间小区房

在代码中,我们可以看到小明和经纪人是一对一关系,经纪人和房东是一对多关系。小明找房经历也轻松多了,只花了一下午就把房子都看了并看中了。这也是中介者模式的优点,减少了不必要的依赖,降低了类间的耦合。

代码:
Mediator Pattern

总结

中介者模式通过在互相依赖的对象中间加了一层,让原本强依赖的对象变成弱依赖。在软件编程中,有一个中介者模式的典型的例子,就是 MVC 框架,也称三层架构,通过 Controller (控制层) 将 Model (业务逻辑层) 和 View (视图层) 的依赖给分离开,协调 Model 和 View 中的数据和界面交互工作。看看你工作中的代码,想想看有没有哪些对象之间的关系特紧密特混乱,考虑是不是可以通过中介者模式来把依赖关系剥离,让代码更清晰。

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

行为型模式:模板方法

公众号之设计模式系列文章

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

1…678…24
LieBrother

LieBrother

当才华撑不起野心时,应该静下心来学习;当能力驾驭不了目标时,应该沉下心来历练。

120 日志
38 分类
138 标签
© 2016 - 2019 LieBrother
由 Hexo 强力驱动
主题 - NexT.Mist
本站访客数人次  |  本站总访问量次