猫头鹰的深夜翻译:使用组合模式来开关灯

前言

设计模式可能是博客圈最热门的话题之一。但是,用来说明每个设计模式的例子通常不是很形象。为了挽救这一点,这篇博客将用一个简单有效的例子来解释组合模式

我们不会再重复解释这个模型的概念。唯一需要记住的是组合模式通常用在一个树结构的递归操作上。

组合模式例子

假设有一栋建筑,这个建筑有几层楼,每层楼有几个房间。
这个建筑的神奇之处在于,它没有一个总开关。因此,当需要关闭或是开启整栋楼的灯时,就不得不逐层逐个房间按动开关。

为了改变这种情况,我们需要首先定义一个包含开关灯操作的基本接口。这个接口我们命名为Component,对应于组合模式中的Component

1
2
3
4
public interface Component {
void switchLightsOn();
void switchLightsOff();
}

接着,我们定义表示建筑的类Building,Floor和Room。每个类实现该接口,并且实现的逻辑如下:

  • Building的开关灯操作意味着整栋楼都将开灯或关灯
  • Floor的开关灯操作意味着整层楼的开灯或关灯
  • Room的开关灯操作意味着该房间的开灯或关灯

以下是三个类的内容:

Building

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Building extends ArrayList<Floor> implements Component{
@Override
public void switchLightsOn() {
for (Floor floor : this) {
floor.switchLightsOn();
}
}
@Override
public void switchLightsOff() {
for (Floor floor : this) {
floor.switchLightsOff();
}
}
}

Floor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Floor extends ArrayList<Room> implements Component {
private int floorNumber;
public Floor(int floorNumber){
this.floorNumber=floorNumber;
}
@Override
public void switchLightsOn() {
for (Room room : this) {
room.switchLightsOn();
}
}
@Override
public void switchLightsOff() {
for (Room room : this) {
room.switchLightsOff();
}
}
}

Room

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Room implements Component {
private boolean lightsOn = false;
private int roomNumber;
public Room(int roomNumber){
this.roomNumber=roomNumber;
}
@Override
public void switchLightsOn() {
lightsOn = true;
}
@Override
public void switchLightsOff() {
lightsOn = false;
}
public boolean isLightsOn() {
return lightsOn;
}
}

补充说明一下:

  • 出于方便,Building类和Floor类直接继承了ArrayList来获得其容器的功能
  • Building将关灯的操作传递给其下所有的楼层。类似的,每个楼层将关灯的操作传递给每个房间。这种递归传递时组合模式独有的一个特点。
  • 总体的效果是Building上的行为启动了一系列操作并传递给Floors和Rooms。这种多米诺骨牌效应也是组合模式的一个特点。

看一下具体的运行情况。首先,我们需要新建一个Building(默认情况下,灯是关闭状态)。之后,调用建筑的switchLightsOn方法,打开建筑中的所有房间的灯。

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
public class CompositeTest {
private Building building;
@Before
public void createBuilding(){
building = new Building();
//1st floor
Floor floor = new Floor(1);
floor.add(new Room(11));
floor.add(new Room(12));
building.add(floor);
//2nd floor
floor = new Floor(2);
floor.add(new Room(21));
floor.add(new Room(22));
floor.add(new Room(23));
building.add(floor);
//3rd floor
floor = new Floor(3);
floor.add(new Room(31));
floor.add(new Room(32));
floor.add(new Room(33));
building.add(floor);
}
@Test
public void buildingLightsAreOn(){
//checking that all rooms are off
for(Floor floor : building){
for(Room room : floor){
assertEquals(false,room.isLightsOn());
}
}
building.switchLightsOn();
//checking that all rooms are on
for(Floor floor : building){
for(Room room : floor){
assertEquals(true,room.isLightsOn());
}
}
}
}

原文链接: https://dzone.com/articles/using-the-composite-pattern-to-turn-the-lights-on