概念

用一个中介者对象来封装一系列对象的交互。中介者使个对象不需要显示的互相引用,从而使其耦合松散,而且可以独立第改变他们之间的交互。

中介者模式是封装了一系列的对象交互的成熟模式,其关键是将对象之间的交互封装在称作中介者的对象中,中介者使每个对象不需要显示的相互引用,这些对象只包括中介者的引用。当系统中某个对象需要和系统中的另一个对象进行交互的时候,只需要将自己的请求通知中介者即可。

结构与使用

  • 中介者(Mediator):中介者是一个接口,该接口定义了用于同事(Colleague)对象之间进行通信的方法。

  • 具体中介者(ConcreteMediator):具体中介者是实现中介者接口的类。具体中介者需要包含所有具体同事的引用,并通过实现中介者接口中的方法来满足具体同事之间的通信请求。

  • 同事(Colleague):一个接口,规定具体同事需要实现的方法。

  • 具体同事(ConcreteColleague):实现同事接口的类。具体同事需要包含具体中介者的引用,一个具体同事需要和其他具体同事交互时,只需将自己的请求通知给它所包含的具体中介者即可。

UML类图

Mediator.jpg

结构的描述

以下通过一个简单的例子来描述中介者模式中所涉及的各个角色。

古代相互交战的 A、B、C 三方,想通过一个中介者调停之间的战火。A、B、C 三方分别需要中介者向其他两方传达信息。

1.同事

本问题中同事接口定义了具体同事,即交战双方需要实现的方法。

1
2
3
4
5
6
public interface Colleague {
public void giveMess(String [] mess);
public void receiverMess(String mess);
public void setName(String name);
public String getName();
}

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
public class ConcreteMediator {
ColleagueA colleagueA;
ColleagueB colleagueB;
ColleagueC colleagueC;
public void registerColleagueA(ColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void registerColleagueB(ColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
public void registerColleagueC(ColleagueC colleagueC) {
this.colleagueC = colleagueC;
}
public void deliverMess(Colleague colleague, String[] mess) {
if (colleague == colleagueA) {
colleagueB.receiverMess(colleague.getName() + mess[0]);
colleagueC.receiverMess(colleague.getName() + mess[1]);
} else if (colleague == colleagueB) {
colleagueA.receiverMess(colleague.getName() + mess[0]);
colleagueC.receiverMess(colleague.getName() + mess[1]);
} else if (colleague == colleagueC) {
colleagueA.receiverMess(colleague.getName() + mess[0]);
colleagueB.receiverMess(colleague.getName() + mess[1]);
}
}
}

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
// ColleagueA.java
public class ColleagueA implements Colleague {
ConcreteMediator mediator;
String name;
public ColleagueA(ConcreteMediator mediator) {
this.mediator = mediator;
mediator.registerColleagueA(this);
}
@Override
public void giveMess(String[] mess) {
mediator.deliverMess(this, mess);
}
@Override
public void receiverMess(String mess) {
System.out.println(name+"收到的信息:");
System.out.println("\t" + mess);
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}

// ColleagueB.java
public class ColleagueB implements Colleague {
ConcreteMediator mediator;
String name;
public ColleagueB(ConcreteMediator mediator) {
this.mediator = mediator;
mediator.registerColleagueB(this);
}
@Override
public void giveMess(String[] mess) {
mediator.deliverMess(this, mess);
}
@Override
public void receiverMess(String mess) {
System.out.println(name+"收到的信息:");
System.out.println("\t" + mess);
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}

// ColleagueC.java
public class ColleagueC implements Colleague {
ConcreteMediator mediator;
String name;
public ColleagueC(ConcreteMediator mediator) {
this.mediator = mediator;
mediator.registerColleagueC(this);
}
@Override
public void giveMess(String[] mess) {
mediator.deliverMess(this, mess);
}
@Override
public void receiverMess(String mess) {
System.out.println(name+"收到的信息:");
System.out.println("\t" + mess);
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}

4.模式的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String args[]){
ConcreteMediator mediator = new ConcreteMediator();
ColleagueA colleagueA = new ColleagueA(mediator);
ColleagueB colleagueB = new ColleagueB(mediator);
ColleagueC colleagueC = new ColleagueC(mediator);
colleagueA.setName("A国");
colleagueB.setName("B国");
colleagueC.setName("C国");
String[] messA = {"要求归还曾抢夺的100斤土豆", "要求归还曾抢夺的10头牛"};
colleagueA.giveMess(messA);
String[] messB = {"要求归还曾抢夺的10只公鸡", "要求归还曾抢夺的15匹马"};
colleagueB.giveMess(messB);
String[] messC = {"要求归还曾抢夺的300斤小麦", "要求归还曾抢夺的50头驴"};
colleagueC.giveMess(messC);
}

优点

  • 可以避免许多的对象为了之间的相互通信而相互的显示引用。

  • 可以通过中介者将原本分布于多个对象之间的交互行为集中在一起。当这些对象之间需要改变之间的通信行为时,只需使用一个具体中介者即可,不必修改具体同事的代码,即这些同事可被重用。

  • 具体中介者使得各个具体同事完全解耦,修改任何一个具体同事的代码不会影响到其他同事。

  • 具体中介者集中了同事之间的交互细节,使用户比较清楚地知道整个系统中的同事是如何交互的。

注: 由于具体中介者集中了同事之间如何交互的细节,可能使具体中介者变得复杂,增加了维护难度。

cat :由第一行开始显示文件内容。
tac :从最后一行开始显示,可以看出tac是cat的倒写形式。
nl :显示的时候顺便输出行号。
more :一页一页地显示文件内容。
less :与more类似,但比more更好的是,它可以往前翻页。
head :只看头几行。
tail :只看结尾几行。
od :以二进制的方式读取文件内容。

一、直接查看文件内容

cat [-AbEnTv] 文件

: -A :相当于-vET的整合参数。可以列出一些特殊字符,而不是空白;
-b :列出行号,仅针对非空白行做行号显示;
-E :将结尾的断行字符显示出来;
-n :打印行号,连同空白行也有行号;
-T :将 Tab 按键以 ^I 显示出来;
-v :列出一些看不出来的特殊字符。

tac 文件

tac与cat相反,它是由最后一行到第一行反向在屏幕上显示出来。

nl [-bnw] 文件

-b :指定行号指定的方式,主要有两种:

-b a :表示不论是否有空行,也同样列出行号(类似cat -n);
-b t :如果有空行,空的那一行不列出行号(默认值)。

-n :列出行号表示的方法,主要有三种:

-n ln :行号在屏幕最左方显示;
-n rn :行号在自己字段的最右方显示,且不加 0;
-n rz :行号在自己字段的最右方显示,且加 0;

–w:行号占用的位数。

[root@localhost ~]# nl -n rz -w 3 anaconda-ks.cfg 
001	#version=RHEL7
002	# System authorization information
003	auth --enableshadow --passalgo=sha512
......

二、可翻页查看

more 文件

如果more后面接的文件内容行数大于屏幕输出的行数时,最后一行会显示出目前显示的百分比,而且还可以在最后一行输入一些命令。

空格键 :代表向下翻页;
Enter :向下滚动一行;
/字符串 :向下查询“字符串”这个关键字;
:f :立刻显示出文件名以及目前显示的行数;
q :立刻离开more
b或ctrl-b :往回翻页,这个操作只对文件有用,对管道无用

less 文件

less的用法比more更有弹性。

空格键 :向下翻动一页
PageDown :向下翻动一页
/字符串 :向下查询“字符串”这个关键字;
?字符串 :向上查询“字符串”这个关键字;
n :重复前一个查询(与 / 或 ? 有关)
N :反向重复前一个查询(与 / 或 ? 有关)
q :立刻离开less

三、数据选取

head [-n number] 文件

取出前面几行。
-n :后面接数字,代表显示几行的意思,默认显示前10行

后面100行数据都不打印,只打印前面几行

head -n -100 /tmp/test.log

tail [-n number] 文件

取出后面几行。
-n :后面接数字,代表显示几行的意思
-f :表示持续检测后面所接的文件名,直到按下 Ctrl+c 才会结束检测。

如果不知道文件有几行只想列出100行以后的数据:

tail -n +100 /tmp/test.log

假如我要显示文件的第11行到20行:

head -n 20 /tmp/test.log | tail -n 10

一、目录的相关操作

特殊的目录

. 代表当前目录
.. 代表上一层目录
- 代表前一个工作目录
~ 代表“目前用户身份”所在的主文件
~account 代表account这个用户的主文件夹(account是用户名)

下面我们就来谈一谈几个常见的处理目录的命令:

cd :切换目录(change direcotry)
pwd :显示当前目录(print working directory)
mkdir :新建一个新的目录(make directory)
rmdir :删除一个空目录

pwd [-P]
-P :显示出当前路径,而非使用连接(link)路径。
mkdir [-mp] 目录名称
-m :配置文件的权限,直接设置,不需要看默认权限(umask)
-p :帮助你直接将所需要的目录(包含上层目录)递归创建。

[root@localhost tmp]# mkdir -p test1/test2/test3
[root@localhost tmp]# mkdir-m 777 test
[root@localhost tmp]# ls -l
drwxrwxrwx. 2 root root 4096 7月 28 16:51 test
drwxr-xr-x. 3 root root 4096 7月 28 16:51 test1

rmdir [-p] 目录名称
-p :连同上层空的目录也一起删除

[root@localhost tmp]# rmdir test
[root@localhost tmp]# rmdir test1
rmdir: 删除 “test1” 失败: 目录非空
[root@localhost tmp]# rmdir -p test1/test2/test3

二、复制、删除与移动:cp,rm,mv

1、cp 复制文件或目录

cp [-adfilprsu] 源文件 目标文件
cp [options] source1 source2 source3 … directory

-a :相当于-pdr的意思(常用)
-d :若源文件为连接文件的属性(link file),复制连接文件的属性而非文件本身;
-f :为强制(force)的意思,若目标文件已经存在且无法开启,则删除后再尝试一次;
-i :若目标文件已经存在,在覆盖时会进询问(常用)
-l :进行硬连接(hard link)的连接文件创建,而非复制文件本身;
-p :连同文件的属性一起复制过去,而非使用默认属性(备份常用)
-r :递归持续复制,,用于目录的复制(常用)
-s :复制成符号连接文件(symbolic link),即“快捷方式”文件;
-u :若destination比source旧才更新destination。
【注意:如果源文件有两个以上,则最后一个目的文件一定要是目录才行

在不加任何参数的情况下,文件的某些属性、权限会改变,连文件的建立时间也不一样了。如果你想把文件的所有属性都一起复制过来,可以加上 -a。在默认的条件中,cp的源文件与目的文件的权限是不同的,目的文件的所有者通常是命令操作者本身。

[root@localhost tmp]# cp /var/log/wtmp . //复制到当前目录,最后的“.”不要忘了
[root@localhost tmp]# ls -l /var/log/wtmp wtmp //注意文件属性的变化
-rw-rw-r–. 1 root utmp 50688 7月 28 16:49 /var/log/wtmp
-rw-r–r–. 1 root root 50688 7月 28 20:22 wtmp

如果是目录则不能直接复制,要加上-r 参数。 -r 是可以复制目录,但是,文件与目录的权限可能会被改变, 所以,也可以用“cp -a /etc/ /tmp” 来执行命令,尤其是在备份的情况下。

[root@localhost tmp]# cp -r /etc/ /tmp

创建连接文件

[root@localhost tmp]# ll bashrc //先看一下文件的情况
-rw-r–r–. 1 root root 176 7月 28 20:46 bashrc
[root@localhost tmp]# cp -s bashrc bashrc_slink
[root@localhost tmp]# cp -l bashrc bashrc_hlink
[root@localhost tmp]# ll bashrc*
-rw-r–r–. 2 root root 176 7月 28 20:46 bashrc
-rw-r–r–. 2 root root 176 7月 28 20:46 bashrc_hlink
lrwxrwxrwx. 1 root root 6 7月 28 20:47 bashrc_slink -> bashrc

若 ~/.bashrc 比 /tmp/bashrc 新才复制过来

[root@localhost tmp]# cp -u ~/.bashrc /tmp/.bashrc
这个 -u 的特性是在目标文件与源文件有差异时才会复制的,所以,比较常用于备份中。

将上面生成的bashrc_slink复制成bashrc_slink1与bashrc_slink2

[root@localhost tmp]# cp bashrc_slink bashrc_slink1
[root@localhost tmp]# cp -d bashrc_slink bashrc_slink2
[root@localhost tmp]# ll bashrc bashrc_slink*
-rw-r–r–. 2 root root 176 7月 28 20:46 bashrc
lrwxrwxrwx. 1 root root 6 7月 28 20:47 bashrc_slink -> bashrc
-rw-r–r–. 1 root root 176 7月 29 14:48 bashrc_slink1 //与源文件相同
lrwxrwxrwx. 1 root root 6 7月 29 14:48 bashrc_slink2 -> bashrc //是连接文件
原本复制的是连接文件,但却将连接文件的实际文件复制过来了,也就是说,如果没有加上任何参数时,cp复制的是源文件,而非连接文件的属性。

能否使用corly的身份,完整复制/var/log/wtmp 文件到 /tmp 下面,并更名为corly_wtmp呢?

[corly@localhost tmp]$ cp -a /var/log/wtmp /tmp/corly_wtmp
[corly@localhost tmp]$ ll /var/log/wtmp /tmp/corly_wtmp
-rw-rw-r–. 1 corly corly 53376 7月 29 09:26 /tmp/corly_wtmp
-rw-rw-r–. 1 root utmp 53376 7月 29 09:26 /var/log/wtmp

由于corly的身份并不能随意修改文件的所有者与用户组,因此虽然能够复制wtmp的相关权限与时间等属性,但与所有者、用户组相关的,corly的身份是无法进行操作的,即使加上**-a**参数,也是无法完整复制权限。

2、rm 移出文件或目录

rm [-fir] 文件或目录
-f :就是force的意思,忽略不存在的文件,不会出现警告信息;
-i :互动模式在删除前会询问用户是否操作;
-r :递归删除。

3、mv 移动文件与目录或更名

mv [-fiu] source destination
mv [options] source1 source2 source3 … directory
-f :force强制,如果目标文件已经存在,不会询问而直接覆盖;
-i :若目标文件已经存在,就会询问覆盖;
-u :若目标文件已经存在,且source比较新,才会更新。

概述

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

责任链模式是使用多个对象处理用户请求的一种成熟模式,责任链模式的关键是将用户的请求分派给多个对象,这些对象被组织成一个责任链,即每个对象都含有后继对象的引用,并要求责任链上的每一个对象,如果能处理用户的请求,就做出处理,不再将用户的请求传递给责任链上的下一个对象;如果不能处理用户的请求,就必须将用户的请求传递给责任链上的下一个对象。

结构与使用

责任链模式的结构中包括两种角色。

  • 处理者(Handler):处理者是一个接口,负责规定具体处理者处理用户请求的方法以及具体处理者设置后继的对象的方法

  • 具体处理者(ConcreteHandler):具体处理者是实现处理者接口的类的实例。具体处理者通过调用处理者接口规定的方法处理用户的请求,即在接到用户的请求后处理者将调用接口规定的方法,在执行该方法的过程中,如果发现能处理用户的请求,就处理用户的数据,否则就返回无法处理信息给用户,然后将用户的请求传递给自己的后继对象。

UML类图

responsibility.jpg

结构的描述

1、处理者(Handler)

1
2
3
4
public interface Handler {
public abstract void handlerRequest(String number);
public abstract void setNextHandler(Handler handler);
}

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
// Beijing.java
public class Beijing implements Handler {
private Handler handler;
private ArrayList<String> numberList;
public Beijing() {
numberList = new ArrayList<String>();
// 这里模拟证件号码
numberList.add("11129857");
numberList.add("11129858");
numberList.add("11129859");
numberList.add("11129856");
}
@Override
public void handlerRequest(String number) {
if (numberList.contains(number)) {
System.out.println("该人住在北京");
} else {
System.out.println("该人不在北京居住");
if (handler != null) {
handler.handlerRequest(number);
}
}
}
@Override
public void setNextHandler(Handler handler) {
this.handler = handler;
}
}


// ShangHai.java
public class ShangHai implements Handler {
private Handler handler;
private ArrayList<String> numberList;
public ShangHai() {
numberList = new ArrayList<String>();
// 这里模拟证件号码
numberList.add("21129857");
numberList.add("21129858");
numberList.add("21129859");
numberList.add("21129856");
}
@Override
public void handlerRequest(String number) {
if (numberList.contains(number)) {
System.out.println("该人住在上海");
} else {
System.out.println("该人不在上海居住");
if (handler != null) {
handler.handlerRequest(number);
}
}
}
@Override
public void setNextHandler(Handler handler) {
this.handler = handler;
}

}

3、测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Application {
private Handler beijing,shanghai;
//创建责任链
public void createChain() {
beijing = new Beijing();
shanghai = new ShangHai();
beijing.setNextHandler(shanghai);
}
//响应用户请求
public void responseClient(String number) {
beijing.handlerRequest(number);
}
public static void main(String[] args) {
Application application = new Application();
application.createChain();
application.responseClient("21129859");
}
}

优点

  • 责任链中的对象只和自己的后继是低耦合关系,和其他对象毫无关联,这使得编写处理者对象以及创建责任链变得非常容易。

  • 在处理者中分配职责时,责任链给应用程序更多的灵活性。

  • 应用程序可以动态地增加、删除处理者或重新指定处理者的职责。

  • 应用程序可以动态地改变处理者之间的先后顺序。

  • 应用程序可以动态地改变处理者之间的先后顺序。

  • 使用责任链的用户不必知道处理者的信息,用户不会知道到底是哪个对象处理了请求。

概念

动态地给对象添加一些额外的职责。就功能来说装饰模式比生成子类更为灵活。

在许多设计中,可能需要改进类的某个对象的功能,而不是该类创建的全部对象。例如,麻雀类的实例能连续飞行100米,如果用麻雀类创建5只麻雀,那么这5只麻雀都能连续飞行100米。假如想让其中一只麻雀能连续飞行150米,那我们应当怎么做呢?我们不想通过修改麻雀类的代码(也可能根本不允许修改)使得麻雀类创建的麻雀能够连续飞行150米。一种比较好的方法就是给麻雀装上智能电子翅膀。智能电子翅膀可以使得麻雀不使用自己的翅膀就能飞行50米。

装饰模式是动态地扩展一个对象的功能,而不需要改变原始类代码的一种成熟的设计模式。在装饰模式中,“具体组件”类和“具体装饰”类是该模式中最重要的两个角色。“具体组件”类的实例称作“被装饰者”,“具体装饰”类的实例称作“装饰者”。“具体装饰”类需要包含有“具体组件”类的一个实例的引用,以便装饰“被装饰者”。

例如,前面所述的麻雀类就是“具体组件”类,而一只麻雀就是“具体组件”类的一个实例,即是一个“被装饰者”,而安装了电子翅膀的麻雀就是“具体装饰”类的一个实例,即安装了电子翅膀的麻雀是麻雀的“装饰者”。

结构与使用

装饰模式的结构中包含四种角色。

  • 抽象组件(Component):抽象组件是一个抽象类。抽象组件定义了“被装饰者”需要进行“装饰”的方法。

  • 具体组件(ConcreteCommponent):具体组件是抽象组件的一个子类,具体组件的实例称作“被装饰者”。

  • 装饰(Decorator):装饰也是抽象组件的一个子类,但装饰还包括抽象组件申明的变量以保存“被装饰者”的引用。装饰可以使抽象类也可以是非抽象类,如果是非抽象类,它的实例称作“装饰者”。

  • 具体装饰(ConcreteDecorator):具体装饰是装饰的一个非抽象子类,具体装饰的实例称作“装饰者”。

UML类图

Decorator.jpg

结构的描述

1.抽象组件

本问题中,抽象组件规定了具体组件需要实现的方法。

1
2
3
public abstract class Bird {
public abstract int fly();
}

2.具体组件

具体组件是Sparrow类。

1
2
3
4
5
6
7
public class Sparrow extends Bird {
public final int DISTANCE = 100;
@Override
public int fly() {
return DISTANCE;
}
}

3.装饰

1
2
3
4
5
6
7
public abstract class Decorator extends Bird{
protected Bird bird;
public Decorator() {}
public Decorator(Bird bird){
this.bird = bird;
}
}

4.具体装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SparrowDecorator extends Decorator {
public final int DISTANCE = 50;
public SparrowDecorator(Bird bird) {
super(bird);
}
@Override
public int fly() {
//委托被装饰者调用fly方法
return bird.fly() + eleFly();
}
//装饰者新添的方法
private int eleFly() {
return DISTANCE;
}
}

5.测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Application {
public void printFly(Bird bird){
System.out.println("这只鸟能飞行"+bird.fly()+"米。");
}
public static void main(String[] args) {
Application client = new Application();
//sparrow 只能飞行100米
Bird sparrow = new Sparrow();
//sparrowDecorator1 能飞150米
Bird sparrowDecorator1 = new SparrowDecorator(sparrow);
//sparrowDecorator2 能飞200米
Bird sparrowDecorator2 = new SparrowDecorator(sparrowDecorator1);
client.printFly(sparrowDecorator1);
client.printFly(sparrowDecorator2);
}
}

使用多个装饰者

由于装饰是抽象组件的一个子类,因此“装饰者”本身也可作为一个“被装饰者”,这意味着可以使用多个具体装饰类来装饰具体组件的实例。

比如,对于上面的小问题,假如我们不仅需要飞行150米、200米的鸟,而且需要飞行120米、170米、220米的鸟,这是我们只需再添加一个具体装饰即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SparrowDecoratorTwo extends Decorator{
public final int DISTANCE = 20;
public SparrowDecoratorTwo(Bird bird) {
super(bird);
}
@Override
public int fly() {
return bird.fly()+eleFly();
}
private int eleFly() {
return DISTANCE;
}
}

优点

  • 被装饰者和装饰者是松耦合关系,由于装饰(Decorator)仅仅依赖于抽象的组件(Component),因此具体装饰只知道他要装饰的对象是抽象组件某一个子类的实例,但不需要知道是哪一个具体子类。

  • 装饰模式满足“开-闭原则”。不必修改具体组件,就可以增加新的针对该具体组件的装饰。

  • 可以使用多个具体装饰来装饰具体组件的实例。

设计要求

  1. 设计一个窗口,该窗口中有三个文本区。
  2. 三个文本区域中的其中一个可供用户编辑文本,另外两个用户不可编辑。
  3. 当用户在可编辑的文本中进行编辑时,另外两个不可编辑的文本区中的一个将显示用户所编辑文本中出现的单词,另一个将显示用户所编辑文本中包含的数字。

设计实现

用户所编辑文本应当视作观察者模式中的一个具体主题所维护的数据,而另外两个文本区应该视作是两个具体观察者中的成员。

1.主题

本问题中,使用java.util包中的Observable类作为主题。

2.观察者

观察者是java.util包中的Observer接口。

3.具体主题

InputTextSubject是Observable类的子类,负责创建具体主题。InputTextSubject类包含一个可编辑的JTextArea文本区。

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
public class InputTextSubject extends Observable {
String content;
JTextArea text;
public InputTextSubject() {
text = new JTextArea(10, 15);
text.setLineWrap(true);
text.setWrapStyleWord(true);
(text.getDocument()).addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
changedUpdate(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
changedUpdate(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
content = text.getText();
setChanged();
notifyObservers(content);
}
});
}
public String getContent() {
return content;
}
public JTextArea getText() {
return text;
}
}

4.具体观察者

负责创建具体观察者的类是ShowWord和ShowDigit。

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
// ShowWord.java
public class ShowWord extends JPanel implements Observer {
Observable subject;
JTextArea text;
TreeSet<String> worldList;
public ShowWord(Observable observable) {
this.subject = observable;
subject.addObserver(this);
text = new JTextArea(10, 15);
text.setLineWrap(true);
text.setWrapStyleWord(true);
text.setEditable(false);
add(new JScrollPane(text));
worldList = new TreeSet<String>();
}
@Override
public void update(Observable subject, Object object) {
text.setText(null);
text.append("出现的单词有(按字典顺序):\n");
worldList.clear();
String content = object.toString();
String regx = "[\\s\\d\\p{Punct}]+";
String words[] = content.split(regx);
for (int i = 0; i < words.length; i++) {
worldList.add(words[i]);
}
Iterator<String> te = worldList.iterator();
while (te.hasNext()) {
text.append(te.next()+" ");
}
}
}

// ShowDigit.java
public class ShowDigit extends JPanel implements Observer {
Observable subject;
JTextArea text;
Vector<String> vector;
public ShowDigit(Observable subject) {
this.subject = subject;
subject.addObserver(this);
text = new JTextArea(10, 15);
text.setLineWrap(true);
text.setWrapStyleWord(true);
text.setEditable(false);
add(new JScrollPane(text));
vector = new Vector<String>();
}
@Override
public void update(Observable subject, Object object) {
text.setText(null);
text.append("出现的数字有:\n");
vector.removeAllElements();
String content = object.toString();
String regex = "\\D+"; //非数字字符构成的正则表达式
String digitWords[] = content.split(regex);
for (int i = 0; i < digitWords.length; i++) {
if (! vector.contains(digitWords[i])) {
vector.add(digitWords[i]);
}
}
for (int i = 0; i < vector.size(); i++) {
text.append(vector.elementAt(i)+" ");
}
}
}

5.测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Application extends JFrame {
public Application() {
InputTextSubject textSubject = new InputTextSubject(); //具体主题
ShowWord observerOne = new ShowWord(textSubject); //具体观察者
ShowDigit observerTwo = new ShowDigit(textSubject); //具体观察者
setLayout(new FlowLayout());
add(new JScrollPane(textSubject.getText()) );
add(observerOne);
add(observerTwo);
setBounds(20, 20, 400, 300);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String args[]){
new Application();
}
}

用户在可编辑文本区域输入文本时,另外连个文本区分别显示了用户输入文本中包含的单词以及所包含的数字字符序列,运行效果如图所示。

程序运行效果

文件属性

文件属性

  • 第一列代表这个文件的类型与权限(permission)

    这一列共有10个字符代表这个文件是“目录、文件或链接文件等”。

    • 若是‘d’则是目录。
    • 若是‘-’则是文件。
    • 若是‘l’则表示为连接文件(linkfile)。
    • 若是‘b’则表示设备文件中可供存储的接口设备。
    • 若是‘c’则表示设备文件里面的串行端口设备,例如键盘、鼠标(一次性读取设备)。

接下来的字符中,以3个一组,切均为“rwx”的3个组合参数。其中‘r’代表可读(read),‘w’代表可写(write),‘x’代表可以执行(execute)。要注意的是,这3个权限的位置不会改变,如果没有权限,就会出现减号‘-’而已。

  • 第一组为“文件所有者权限”
  • 第二组为“同用户组的权限”
  • 第三组为“其它非本用户组的权限”
  • 第二列表示有多少文件连接到此节点(i-node)

    每个文件都会将它的权限与属性记录到文件系统的i-node中,不过我们使用的目录树却是使用文件名来记录,因此每个文件名就会连接到一个i-node。这个属性记录的就是有多少不同的文件名连接到相同的一个i-node号码。

  • 第三列和第四列表示这个文件(或目)的“所有者账号”和“所属用户组”。

  • 第五列表示这个文件的容量大小,默认单位为B。

  • 第六列为这个文件的创建日期或者是最近的修改日期。

修改文件属性与权限

常见的几个用于修改用户组、所有者、各种身份的权限的命令:

chgrp:改变文件所属用户组。
chown: 改变文件所有者。
chmod: 改变文件的权限。

改变所属用户组:chgrp

这个命令是change group的简称。不过,请记得,要被改变的组名必须要在/etc/group文件内存在才行,否者就会显示错误。

chgrp [-R] dirname/filename

  • -R:进行递归(recursive)的持续更改,即连同子目录下的所有文件。

例子:

[root@localhost corlymeng]# chgrp corly test.log
[root@localhost corlymeng]# ls -l
-rw-r–r–. 1 root corly 0 7月 24 16:40 test.log

改变文件所有者:chown

这个命令是change owner的简称。要注意的是,用户必须是已经存在于系统中的账号,也就是在/etc/passwd这个文件中有记录的用户名称才能改变。chown还可以修改文件的用户组。

chown [-R] 账号名称[:组名] 文件或目录

  • -R:同上面一样

例子:

//将test.log所有者改为bin这个账号
chown bin test.log
//将test.log的所有者和用户组改回为root
chown root:root test.log

事实上,chown也可以使用“chown user.group file”,亦即在拥有者与群组间加上小数点也行! 不过很多朋友设定账号时,喜欢在账号当中加入小数点(例如corly.meng这样的账号格式),这就会造成系统的误判了! 所以我们比较建议使用冒号“:”来隔开拥有者与群组,此外,chown也能单纯的修改所属用户组。例如“chown .sshd install.log”就是修改用户组,就是那个小数点的用途。

改变文件权限:chmod

文件权限的改变是用chmod这个命令,但是权限的设置有两种,分别可以使用数字或者是符号来进行权限的更改。

  • 数字更改文件权限

Linux文件的基本权限就有9个,分别是user、group、others三种身份各有自己的read、write、execute权限。其中我们可以用数字来代表各个权限,各权限的数字对照如下:

  • r:4
  • w:2
  • x:1

每种身份(user、group、others)各自的三个权限分数是要累加的,例如当前权限“**-rwxrwx—**”,分数是:

  • user= rwx = 4+2+1 = 7
  • group = rwx = 4+2+1 = 7
  • others = — = 0+0+0 = 0

所以等我们设置权限更改时,该文件的权限数字就是770。更改权限的命令chmod的语法如下:

chmod [-R] xyz 文件或目录

  • xyz:刚刚提到的数字类型的权限,为rwx属性数值的相加。
  • -R:递归更改。

举例来说,如果要将test.log文件的所有权限都设置启用,那么就执行:

chmod 777 test.log

  • 符号类型改变文件权限

还有一个更改文件权限的方法。从前面的介绍我们可以发现,基本上就9个权限,分别是user、group、others三种身份。那么我们就通过u,g,o来代表三种身份的权限。此外a代表all,也即全部身份。

chmod u
g
o
a
+(加入)
-(除去)
=(设置)
r
w
x
文件或目录

例如我们要设置一个文件权限为“**-rwxr-xr-x**”时。

chmod u=rwx,go=rx test.log
#注意:那个u=rwx,go=rx是连在一起的,中间没有任何空格。

如果是“**-rwxr-xr–**”权限呢?可以用“chmod u=rwx,g=rx,o=r test.log”来设置。如果我不知道原先文件的属性,而我只想增加test.log这个文件每个人均可写入的权限,可以使用如下命令:

chmod a+w test.log

如果要去掉所有人可执行权限,则:

chmod a-x test.log

目录权限

文件是存放实际数据的所在,目录主要的内容是记录文件名列表,文件名与目录有强烈的关联。所以如果针对目录,那个r、w、x有什么意义呢?

r (read contents in directory)
表示具有读取目录结构列表的权限,表示你可以查询该目录下的文件名数据。
w (modify contents of directory)
: 新建已存在的文件与目录;
: 删除已存在的文件与目录(不论该文件的权限为何);
: 将已存在的文件或目录进行重命名;
转义该目录内的文件、目录位置。
x (access directory)
表示用户能够进入该目录成为工作目录的用途,所谓的工作目录(work directory)就是你目前所在的目录。
  • 例 1:

有一个目录的权限如下:

drwxr–r– 3 root root 1024 Jun 25 08:22 test

系统中有一个账号名称为corly,这个账号并没有支持root用户组,那么corly对这个目录有何权限?

corly对此目录有r权限,因此可以查询目录下的文件名列表。因为corly不具有x的权限,所以corly并不能切换到此目录内。

上面的例子中因为corly具有r权限,一看之下好想就具有可以进入此目录的权限,其实是错误的。能不能进一个目录,只与该目录的x权限有关。此外,工作目录对于命令的执行非常重要,如果你在某目录下没有x的权限,那你就没有办法切换到该目录下,也就无法执行该目录下的任何命令,即使你具有该目录的r权限。

  • 例 2:

假设账号corly,他的主文件夹在/home/corly/,corly对此目录具有rwx权限。若在此目录下有一个名为test.log的文件,该文件的权限如下:

-rwx—— 1 root root 1024 Sep 20 04:09 test.log

请问corly对此文件的权限是什么?可否删除此文件?

由于corly对此文件来说是“others”的身份,因此这个文件他无法读、编辑和执行,也就是说他无法变动这个文件的内容。
但是由于这个文件在他的主文件夹下,他在此目录具有rwx的完整权限,因此对于test.log这个文件来说,他是能够删除的。

一个观察者可以依赖于多个具体主题,当所依赖的任何具体主题的数据发生变化时,该观察者都能得到通知。多主题所涉及的问题是观察者如何处理主题中变化后的数据,因为,不同的具体主题所含有数据的机构可能有很大的不同。

在处理多主题时,观察者接口可以将更新数据方法的参数类型设置为主题接口类型,比如update(Subject subject),即具体主题数据发生变化时将自己的引用传递给具体的观察者,然后具体的观察者让这个具体的主题调用有关的方法返回该具体主题中的数据。

以下通过简单的问题说明多主题的设计。

李先生希望及时知道气象站所维护的每日的天气数据,比如最高气温和最低气温等,同时希望及时知道旅行社每日的旅游信息。

按着观察者的模式,李先生就是一个具体的观察者,而气象站和旅行社是他依赖的两个具体主题,根据观察者模式的结构,给出的设计如下。

1.主题

1
2
3
4
5
public interface Subject {
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void notifyObservers();
}

2.观察者

1
2
3
public interface Observer {
public void update(Subject subject);
}

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
// WeatherStation.java
public class WeatherStation implements Subject {
String forecastTime, forecastMess;
int maxTemperature, minTemperature;
ArrayList<Observer> personList;
public WeatherStation() {
personList = new ArrayList<Observer>();
}
@Override
public void addObserver(Observer o) {
if (o == null) {
return;
}
if (! personList.contains(o) ) {
personList.add(o);
}
}
@Override
public void deleteObserver(Observer o) {
if (personList.contains(o)) {
personList.remove(o);
}
}
@Override
public void notifyObservers() {
for (int i = 0; i < personList.size(); i++) {
personList.get(i).update(this);
}
}
public void doForecast(String t, String mess, int max, int min) {
forecastTime = t;
forecastMess = mess;
maxTemperature = max;
minTemperature = min;
notifyObservers();
}
public String getForecastTime() {
return forecastTime;
}
public String getForecastMess() {
return forecastMess;
}
public int getMaxTemperature() {
return maxTemperature;
}
public int getMinTemperature() {
return minTemperature;
}
}

// TraverAgency.java
public class TraverAgency implements Subject {
String tourStartTime;
String tourMess;
ArrayList<Observer> personList;
public TraverAgency() {
personList = new ArrayList<Observer>();
}
@Override
public void addObserver(Observer o) {
if (o == null) {
return;
}
if (! personList.contains(o)) {
personList.add(o);
}
}
@Override
public void deleteObserver(Observer o) {
if (personList.contains(o)) {
personList.remove(o);
}
}
@Override
public void notifyObservers() {
for (int i = 0; i < personList.size(); i++) {
personList.get(i).update(this);
}
}
public void giveMess(String time, String mess) {
tourStartTime = time;
tourMess = mess;
notifyObservers();
}
public String getTourStartTime() {
return tourStartTime;
}
public String getTourMess() {
return tourMess;
}
}

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
public class Person implements Observer {
Subject subjectOne, subjectTwo;
String forecastTime, forecastMess;
String tourStartTime, tourMess;
int maxTemperature, minTemperature;
public Person(Subject subjectOne, Subject subjectTwo) {
this.subjectOne = subjectOne;
this.subjectTwo = subjectTwo;
subjectOne.addObserver(this);
subjectTwo.addObserver(this);
}
@Override
public void update(Subject subject) {
if (subject instanceof WeatherStation) {
WeatherStation ws = (WeatherStation) subject;
forecastTime = ws.getForecastTime();
forecastMess = ws.getForecastMess();
maxTemperature = ws.getMaxTemperature();
minTemperature = ws.getMinTemperature();
System.out.print("预报日期:"+ forecastTime +",");
System.out.print("天气情况:"+ forecastMess +",");
System.out.print("最高温度:"+ maxTemperature +",");
System.out.println("最低温度:"+ minTemperature +",");
} else if (subject instanceof TraverAgency) {
TraverAgency ta = (TraverAgency) subject;
tourStartTime = ta.getTourStartTime();
tourMess = ta.getTourMess();
System.out.print("旅游开始日期:"+ tourStartTime +",");
System.out.println("旅游信息:"+tourMess+"。");
}
}
}

5.测试程序

1
2
3
4
5
6
7
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
TraverAgency traverAgency = new TraverAgency();
Person meng = new Person(weatherStation, traverAgency);
weatherStation.doForecast("10 日", "阴有小雨", 28, 20);
traverAgency.giveMess("10 日", "黄山两日游");
}

概念

定义对象间的一种一对多的关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并自动更新。

概述

在许多设计中,经常涉及多个对象都对一个对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化。例如,某些寻找工作的人对求职中心的职业需求信息的变化非常关心,这时求职者可以登记到求职中心的“求职者”列表中,求职中心就会及时通知他所需要的最新的职业需求信息。

观察者模式是关于多个对象想知道一个对象中数据变化情况的一种成熟的模式。观察者模式中有―个称作“主题”的对象和若干称作“观察者”的对象,“主题”和“观察者”之间是一种一对多的依赖关系,当主题的状态发生变化时,所有观察者都得到通知。前面所述的“求职中心”相当于观察者模式的“主题”;每个“求职者”相当于观察者模式的一个具体“观察者”。

结构与使用

观察者模式的结构中包括四种角色。

  • 主题(Suject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及观察者更新数据的方法。

  • 观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。

  • 具体主题(ConcreteSuject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。

  • 具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。

UML 类图

observerPattern.jpg

结构描述

1.主题

主题接口规定了具体主题需要实现的添加、删除观察者以及通过观察者更新数据的方法。

1
2
3
4
5
public interface Subject {
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void notifyObservers();
}

2.观察者

观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。对于本问题具体观察者都通过实现hearTelephone()方法来更新数据。

1
2
3
public interface Observer {
public void hearTelephone(String heardMess);
}

3.具体主题

主题接口规定了具体主题需要实现的通知观察者更新数据的notifyObserver()方法,具体主题通过实现该方法来通知具体观察者。

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
public class SeekJobCenter implements Subject {
String mess;
boolean changed;
ArrayList<Observer> personList; //存放观察者引用的数组线性表
public SeekJobCenter() {
personList = new ArrayList<Observer>();
mess = "";
changed = false;
}
@Override
public void addObserver(Observer o) {
if (!personList.contains(o)) {
personList.add(o);
}
}

@Override
public void deleteObserver(Observer o) {
if (personList.contains(o)) {
personList.remove(o);
}
}

@Override
public void notifyObservers() {
if (changed) { //通知所有观察者
for (int i = 0; i < personList.size(); i++) {
personList.get(i).hearTelephone(mess);
}
changed = false;
}
}
public void giveNewMess(String str) {
if (str.equals(mess)) {
changed = false;
} else {
mess = str;
changed = true;
}
}
}

4.具体观察者

本问题中。实现观察者接口Observer的类有两个:一个是UnivercityStudent类,另一个是HaiGui。 UniversityStudent类的实例调用hearTelephone(String heardMess)方法时,会将参数引用的字符串保存到一个文件中。HaiGui类的实例调用hearTelephone(String heardMess)方法时,如果参数引用的字符串中包含有“java”或“软件”,就将信息保存到一个文件中。UniversityStudent 和HaiGui类的代码如下:

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
//UnivercityStudent.java
public class UniversityStudent implements Observer {
Subject subject;
File myFile;
public UniversityStudent(Subject subject, String fileName) {
this.subject = subject;
subject.addObserver(this);
myFile = new File(fileName);
}
@Override
public void hearTelephone(String heardMess) {
try {
RandomAccessFile out = new RandomAccessFile(myFile, "rw");
out.seek(out.length());
byte[] b = heardMess.getBytes();
out.write(b);
System.out.print("我是一个大学生,");
System.out.println("我向文件"+myFile.getName()+"写入如下内容:");
System.out.println(heardMess);
} catch (Exception e) {
System.out.println(e.toString());
}
}

}

//HaiGui.java
public class HaiGui implements Observer {
Subject subject;
File myFile;
public HaiGui(Subject subject, String fileName) {
this.subject = subject;
subject.addObserver(this);
myFile = new File(fileName);
}
@Override
public void hearTelephone(String heardMess) {
try {
boolean boo = heardMess.contains("java") || heardMess.contains("软件");
if (boo) {
RandomAccessFile out = new RandomAccessFile(myFile, "rw");
out.seek(out.length());
byte[] b = heardMess.getBytes();
out.write(b);
System.out.print("我是一个海归,");
System.out.println("我向文件"+myFile.getName()+"写入如下内容:");
System.out.println(heardMess);
} else {
System.out.println("我是海归,这次信息中没有我需要的信息。");
}
} catch (IOException e) {
System.out.println(e.toString());
}
}
}

模式的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
SeekJobCenter center = new SeekJobCenter(); //具体主题center
UniversityStudent zhang = new UniversityStudent(center, "A.txt");
HaiGui wang = new HaiGui(center, "B.txt");
center.giveNewMess("腾辉公司需8个java程序员");
center.notifyObservers();
center.giveNewMess("仁和公司需要9个动画设计师");
center.notifyObservers();
center.giveNewMess("xx公司需要4个电工");
center.notifyObservers();
center.giveNewMess("xx公司需要4个电工"); //信息不是最新的
center.notifyObservers(); //观察者不会执行更新操作
}

优点

  • 具体主题和具体观察者是松耦合关系。由于具体主题(Subject)接口仅仅依赖于观察者(Observer)接口,因此具体主题只是知道它的观察者是实现观察者(Observer)接口的某个类的实例,但不需要知道具体是哪个类,同样,由于观察者仅仅依赖于主题(Subject)接口,因此具体观察者只是知道它依赖的主题是实现主题(Subject)接口的某个类的实例,但不需要知道具体是哪个类。

  • 观察模式满足“开闭原则”。主题(Subject)接口仅仅依赖于观察者(Observer)接口,这样,就可以让创建具体主题的类也仅仅是依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不必修改创建具体主题的类的代码,同样,创建具体观察者的类仅仅依赖于主题(Subject)接口,如果增加新的实现主题(Subject)接口的类,也不必修改创建具体观察者类的代码。

使用场景

  • 当以一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合。

  • 当一个对象的数据更新时,这个对象也要让其他对象也各自更新自己的数据,但这个对象不知道具体有多少对象需要更新数据。

举例

  1. 观察者与多主题
  2. 统计文本中的单词和数字

我们用命令模式模拟一个带控制开关的小电器。该小电器上有四个开关,两个一组,其中一组负责打开、关闭照明灯,另一组负责打开、关闭小电器上的摄像头。

设计要求

  1. 设计Cammera类(模拟摄像头)和Light类(模拟照明灯)。
  2. 设计Machine类(模拟小电器)。
  3. 要求Machine类创建的对象中含有打开、关闭摄像头以及打开、关闭照明灯的按钮。

设计实现

设计类图如图:

命令模式举例-模拟小电器

1.接收者

Cammera类和Light类的实例是命令模式的接收者。

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
//Cammera.java
import javax.swing.*;
public class Cammera extends JPanel {
String name;
Icon imageIcon;
JLabel label;
public Cammera() {
label = new JLabel("I am Cammera");
add(label);
}
public void on() {
label.setText("Cammera open");
}
public void off() {
label.setText("Cammera off");
}
}

//Light.java
import javax.swing.*;
public class Light extends JPanel{
String name;
JLabel label;
public Light() {
label = new JLabel("I am Light");
add(label);
}
public void on() {
label.setText("Light open");
}
public void off() {
label.setText("Light off");
}
}

命令接口

1
2
3
4
public interface Command {
public abstract void execute();
public abstract String 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
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
// OnCammeraCommand.java
public class OnCammeraCommand implements Command {
Cammera cammera;
public OnCammeraCommand(Cammera cammera){
this.cammera = cammera;
}
public void execute() {
cammera.on();
}
public String getName() {
return "打开摄像头";
}
}

// OffCammeraCommand.java
public class OffCammeraCommand implements Command {
Cammera cammera;
public OffCammeraCommand(Cammera cammera) {
this.cammera = cammera;
}
public void execute() {
cammera.off();
}
public String getName() {
return "关闭摄像头";
}
}

// OnLightCommand.java
public class OnLightCommand implements Command {
Light light;
public OnLightCommand(Light light){
this.light = light;
}
public void execute() {
light.on();
}
public String getName() {
return "打开照明灯";
}
}

// OffLightCommand.java
public class OffLightCommand implements Command {
Light light;
public OffLightCommand(Light light) {
this.light = light;
}
public void execute() {
light.off();
}
public String getName() {
return "关闭照明灯";
}
}

请求者

Invoke类的实例含有程序所需要的按钮。

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
import java.awt.*;
import javax.swing.*;
public class Invoke {
JButton button;
Command command;
public Invoke() {
button = new JButton();
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
executeCommand();
}

});
}
private void executeCommand() {
System.out.println(command.getName());
command.execute();
}
public void setCommand(Command command) {
this.command = command;
button.setText(command.getName());
}
public JButton getButton() {
return button;
}

}

小电器

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
import java.awt.*;
import javax.swing.*;
public class Machine extends JFrame {
Invoke requestOnCammera,requestOffCammera,requestOnLight,requestOffLight;
Cammera cammera;
Light light;
public Machine() {
setTitle("小电器");
requestOnCammera = new Invoke();
requestOffCammera = new Invoke();
cammera = new Cammera();
light = new Light();
requestOnCammera.setCommand(new OnCammeraCommand(cammera));
requestOffCammera.setCommand(new OffCammeraCommand(cammera));
requestOnLight = new Invoke();
requestOffLight = new Invoke();
requestOnLight.setCommand(new OnLightCommand(light));
requestOffLight.setCommand(new OffLightCommand(light));
initPosition();
setSize(500,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
private void initPosition() {
JPanel ps = new JPanel();
ps.add(requestOnCammera.getButton());
ps.add(requestOffCammera.getButton());
ps.add(requestOnLight.getButton());
ps.add(requestOffLight.getButton());
add(ps, BorderLayout.SOUTH);
add(light, BorderLayout.WEST);
add(cammera, BorderLayout.EAST);
}
public static void main(String[] args) {
Machine machine = new Machine();
}
}