大话设计模式C语言之访问者模式

访问者模式

访问者模式(Visitor Pattern)是一种行为设计模式,它能将算法与其所作用的对象隔离开来,一个作用于某对象结构中的各元素的操作,他使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

案例代码

下面例子实现对设备系统中的不同硬件组件支持不同访问操作:诊断访问者(DiagnosticVisitor)、功耗统计访问者(PowerVisitor)、温度统计访问者(TemperatureVisitor)等等。

访问者

访问者(Visitor)接口声明了一系列以对象结构的具体元素为参数的访问者方法。如果编程语言支持重载,这些方法的名称可以是相同的,但是其参数一定是不同的。

访问者外部抽象接口 visitor.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef VISITOR_H
#define VISITOR_H

typedef struct Visitor Visitor;
typedef struct CPU CPU;
typedef struct Memory Memory;
typedef struct Device Device;

void visitor_visit_cpu(Visitor *, CPU *);
void visitor_visit_memory(Visitor *, Memory *);
void visitor_visit_device(Visitor *, Device *);

void visitor_destroy(Visitor *);

#endif

访问者内部抽象结构 visitor_internal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef VISITOR_INTERNAL_H
#define VISITOR_INTERNAL_H

#include "visitor.h"

struct Visitor {

void (*visit_cpu)(Visitor *, CPU *);

void (*visit_memory)(Visitor *, Memory *);

void (*visit_device)(Visitor *, Device *);

void (*destroy)(Visitor *);
};

#endif

访问者公共接口实现 visitor.c
1
2
3
4
5
6
7
8
#include "visitor.h"
#include "visitor_internal.h"

void visitor_destroy(Visitor *v)
{
v->destroy(v);
}

具体访问者

具体访问者(Concrete Visitor)会为不同的具体元素类实现相同行为的几个不同版本。

诊断访问者(DiagnosticVisitor)
diagnostic_visitor.h
1
2
3
4
5
6
7
8
9
#ifndef DIAGNOSTIC_VISITOR_H
#define DIAGNOSTIC_VISITOR_H

#include "visitor.h"

Visitor *diagnostic_visitor_create(void);

#endif

diagnostic_visitor.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
#include <stdio.h>
#include <stdlib.h>
#include "diagnostic_visitor.h"
#include "visitor_internal.h"
#include "cpu.h"
#include "memory.h"
#include "device.h"

typedef struct {

Visitor base;

} DiagnosticVisitor;

static void visit_cpu(Visitor *v, CPU *cpu)
{
printf("[Diagnostic] CPU cores=%d freq=%d MHz\n",
cpu_get_cores(cpu),
cpu_get_frequency(cpu));
}

static void visit_memory(Visitor *v, Memory *mem)
{
printf("[Diagnostic] Memory size=%d MB\n",
memory_get_size(mem));
}

static void visit_device(Visitor *v, Device *dev)
{
printf("[Diagnostic] Device id=%d OK\n",
device_get_id(dev));
}

static void destroy(Visitor *v)
{
free(v);
}

Visitor *diagnostic_visitor_create(void)
{
DiagnosticVisitor *dv = malloc(sizeof(DiagnosticVisitor));

dv->base.visit_cpu = visit_cpu;
dv->base.visit_memory = visit_memory;
dv->base.visit_device = visit_device;
dv->base.destroy = destroy;

return (Visitor *)dv;
}

功耗统计访问者(PowerVisitor)
power_visitor.h
1
2
3
4
5
6
7
8
9
#ifndef POWER_VISITOR_H
#define POWER_VISITOR_H

#include "visitor.h"

Visitor *power_visitor_create(void);

#endif

power_visitor.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
#include <stdio.h>
#include <stdlib.h>
#include "power_visitor.h"
#include "visitor_internal.h"
#include "cpu.h"
#include "memory.h"
#include "device.h"

typedef struct {

Visitor base;

int total_power;

} PowerVisitor;

static void visit_cpu(Visitor *v, CPU *cpu)
{
PowerVisitor *pv = (PowerVisitor *)v;

int power = cpu_get_cores(cpu) * cpu_get_frequency(cpu) / 100;

pv->total_power += power;

printf("[Power] CPU power=%d W\n", power);
}

static void visit_memory(Visitor *v, Memory *mem)
{
PowerVisitor *pv = (PowerVisitor *)v;

int power = memory_get_size(mem) / 10;

pv->total_power += power;

printf("[Power] Memory power=%d W\n", power);
}

static void visit_device(Visitor *v, Device *dev)
{
PowerVisitor *pv = (PowerVisitor *)v;

int power = device_get_id(dev);

pv->total_power += power;

printf("[Power] Device power=%d W\n", power);
}

static void destroy(Visitor *v)
{
PowerVisitor *pv = (PowerVisitor *)v;

printf("[Power] Total power=%d W\n", pv->total_power);

free(v);
}

Visitor *power_visitor_create(void)
{
PowerVisitor *pv = malloc(sizeof(PowerVisitor));

pv->base.visit_cpu = visit_cpu;
pv->base.visit_memory = visit_memory;
pv->base.visit_device = visit_device;
pv->base.destroy = destroy;

pv->total_power = 0;

return (Visitor *)pv;
}

元素

元素(Element)接口声明了一个方法来 “接收” 访问者。 该方法必须有一个参数被声明为访问者接口类型。

元素外部抽象接口 element.h
1
2
3
4
5
6
7
8
9
10
11
#ifndef ELEMENT_H
#define ELEMENT_H

typedef struct Element Element;
typedef struct Visitor Visitor;

void element_accept(Element *element, Visitor *visitor);
void element_destroy(Element *element);

#endif

元素内部抽象结构 element_internal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef ELEMENT_INTERNAL_H
#define ELEMENT_INTERNAL_H

#include "element.h"

struct Element {

void (*accept)(Element *, Visitor *);

void (*destroy)(Element *);
};

#endif

元素公共接口实现 element.c
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "element.h"
#include "element_internal.h"

void element_accept(Element *element, Visitor *visitor)
{
element->accept(element, visitor);
}

void element_destroy(Element *element)
{
element->destroy(element);
}

具体元素

具体元素 (Concrete Element)必须实现接收方法。该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。即使元素基类实现了该方法,所有子类都必须对其进行重写并调用访问者对象中的合适方法。

CPU元素
cpu.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef CPU_H
#define CPU_H

#include "element.h"

typedef struct CPU CPU;

CPU *cpu_create(int cores, int frequency);

int cpu_get_cores(CPU *);
int cpu_get_frequency(CPU *);

#endif

cpu.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
#include <stdlib.h>
#include "cpu.h"
#include "element_internal.h"
#include "visitor_internal.h"

struct CPU {

Element base;

int cores;
int frequency;
};

static void cpu_accept(Element *element, Visitor *visitor)
{
visitor->visit_cpu(visitor, (CPU *)element);
}

static void cpu_destroy(Element *element)
{
free(element);
}

CPU *cpu_create(int cores, int frequency)
{
CPU *cpu = malloc(sizeof(CPU));

cpu->base.accept = cpu_accept;
cpu->base.destroy = cpu_destroy;

cpu->cores = cores;
cpu->frequency = frequency;

return cpu;
}

int cpu_get_cores(CPU *cpu)
{
return cpu->cores;
}

int cpu_get_frequency(CPU *cpu)
{
return cpu->frequency;
}

Memory元素
memory.h
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef MEMORY_H
#define MEMORY_H

#include "element.h"

typedef struct Memory Memory;

Memory *memory_create(int size);

int memory_get_size(Memory *);

#endif

memory.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
#include <stdlib.h>
#include "memory.h"
#include "element_internal.h"
#include "visitor_internal.h"

struct Memory {

Element base;

int size;
};

static void memory_accept(Element *element, Visitor *visitor)
{
visitor->visit_memory(visitor, (Memory *)element);
}

static void memory_destroy(Element *element)
{
free(element);
}

Memory *memory_create(int size)
{
Memory *mem = malloc(sizeof(Memory));

mem->base.accept = memory_accept;
mem->base.destroy = memory_destroy;

mem->size = size;

return mem;
}

int memory_get_size(Memory *mem)
{
return mem->size;
}

Device元素
device.h
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef DEVICE_H
#define DEVICE_H

#include "element.h"

typedef struct Device Device;

Device *device_create(int id);

int device_get_id(Device *);

#endif

device.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
#include <stdlib.h>
#include "device.h"
#include "element_internal.h"
#include "visitor_internal.h"

struct Device {

Element base;

int id;
};

static void device_accept(Element *element, Visitor *visitor)
{
visitor->visit_device(visitor, (Device *)element);
}

static void device_destroy(Element *element)
{
free(element);
}

Device *device_create(int id)
{
Device *dev = malloc(sizeof(Device));

dev->base.accept = device_accept;
dev->base.destroy = device_destroy;

dev->id = id;

return dev;
}

int device_get_id(Device *dev)
{
return dev->id;
}

测试客户端代码 main.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
#include "cpu.h"
#include "memory.h"
#include "device.h"
#include "diagnostic_visitor.h"
#include "power_visitor.h"
#include "element.h"
#include "visitor.h"

int main(void)
{

Element *elements[3];

elements[0] = (Element *)cpu_create(4, 2400);
elements[1] = (Element *)memory_create(8192);
elements[2] = (Element *)device_create(5);

Visitor *diag = diagnostic_visitor_create();

Visitor *power = power_visitor_create();

for (int i = 0; i < 3; i++)
{
element_accept(elements[i], diag);
}

for (int i = 0; i < 3; i++)
{
element_accept(elements[i], power);
}

visitor_destroy(diag);
visitor_destroy(power);

for (int i = 0; i < 3; i++)
{
element_destroy(elements[i]);
}

while (1);
return 0;
}

总结

   访问者模式的目就是要把处理从数据结构分离出来,比如按算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法使用访问者模式就比较适合。因为访问者模式使得算法操作的增加变得容易。但同时访问者模式需要系统的数据结构相对稳定,因为新增数据结构需要在每个状态类和它下属所有类中新增方法,这违背了开闭原则。

   就像这个例子中如果要新增TemperatureVisitor无需修改设备类十分容易,但是新增Element设备类就需要修改所有访问者类,但由于设备硬件生产出来后不会轻易改变,所以设备类数据结构相对稳定,所以使用访问者模式比较合适。

大多数时候你并不需要访问者模式,但是当你一旦需要使用它时,那就是时候使用它了。