
typedef struct Engine Engine;
typedef struct Wheel Wheel;
typedef struct Car Car;
//Visitor: C has no overloading, so each element
//has its own function in the table
typedef struct {
void (*visitEngine)(const Engine *engine);
void (*visitWheel)(const Wheel *wheel);
void (*visitCar)(const Car *car);
} CarVisitor;
//Element: the base struct is the first field of each element
typedef struct Element {
void (*accept)(const struct Element *self, const CarVisitor *v);
} Element;
//ConcreteElement
struct Engine {
Element base;
};
//the second dispatch: the element picks the function of the visitor
void engineAccept(const Element *self, const CarVisitor *v) {
v->visitEngine((const Engine *)self);
}
//ConcreteElement
struct Wheel {
Element base;
int number;
};
void wheelAccept(const Element *self, const CarVisitor *v) {
v->visitWheel((const Wheel *)self);
}
//ConcreteElement: the car keeps its parts inside, no malloc
struct Car {
Element base;
Engine engine;
Wheel wheels[4];
};
void carAccept(const Element *self, const CarVisitor *v) {
const Car *car = (const Car *)self;
const Element *items[] = {
&car->engine.base,
&car->wheels[0].base, &car->wheels[1].base,
&car->wheels[2].base, &car->wheels[3].base};
for (size_t i = 0; i < sizeof items / sizeof items[0]; i++) {
items[i]->accept(items[i], v);
}
v->visitCar(car);
}
Car carMake(void) {
Car car = {.base = {carAccept}, .engine = {{engineAccept}}};
for (int i = 0; i < 4; i++) {
car.wheels[i] = (Wheel){{wheelAccept}, i + 1};
}
return car;
}
//ConcreteVisitor
//C23: an unused parameter may stay without a name
void testEngine(const Engine *) {
printf("test engine\n");
}
void testWheel(const Wheel *wheel) {
printf("test wheel #%d\n", wheel->number);
}
void testCar(const Car *) {
printf("test car\n");
}
//ConcreteVisitor
void repairEngine(const Engine *) {
printf("repair engine\n");
}
void repairWheel(const Wheel *wheel) {
printf("repair wheel #%d\n", wheel->number);
}
void repairCar(const Car *) {
printf("repair car\n");
}
//Client: a ConcreteVisitor is a table of its own functions
Car car = carMake();
CarVisitor v1 = {testEngine, testWheel, testCar};
CarVisitor v2 = {repairEngine, repairWheel, repairCar};
//the first dispatch: the element's own accept
car.base.accept(&car.base, &v1);
//printed: test engine
// test wheel #1
// test wheel #2
// test wheel #3
// test wheel #4
// test car
car.base.accept(&car.base, &v2);
//printed: repair engine
// repair wheel #1
// repair wheel #2
// repair wheel #3
// repair wheel #4
// repair car
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.Read moreThe problem. A document is made of paragraphs, images and tables. The program must export it, count its words and check its spelling. If each operation is added as a method to every kind of part, the part classes grow with every new operation. The code of one operation is also spread over many classes. The idea. Each operation becomes a separate object, called a visitor. A visitor has one method for each kind of element. Each element has one method that takes a visitor and calls the visitor's method for its own kind. So the right code runs for each pair of element and operation, without checks of the type. To add a new operation, you write a new visitor; the element classes do not change. How to read the example. The elements are of several kinds. Each concrete visitor is one operation, with a separate method for each kind of element. The client code gives a visitor to the elements, and each element sends it to the method for its own kind. In most examples one element holds the others and gives the visitor to each of its parts. Two visitors then pass over the same parts, each with its own operation. In some languages the elements are plain data with a type tag, and a visitor is one function with a version for each kind. When to use it. Use Visitor when the set of element classes is stable, but new operations on them appear often. Do not use it when new kinds of element are added often: each new kind needs a new method in every visitor. A common mistake. Checking the type of the element with conditions inside one general visitor method. A new kind of element is then missed without any warning. Give each kind its own method. |