12 design pattern _ visitor pattern _ C language implementation

Visitor mode

1 Simulation scene

Suppose we want to develop a network port driver. There are two types of network ports: low-speed network ports and high-speed network ports. Each type of network port supports the operation of configuration rate and configuration mode. Then our driver looks like this:

Insert picture description here

Assuming that the above procedure has been implemented, now due to business needs, we need to add the query rate operation.

Then, we need to modify the implemented Mac class and its subclasses, and add GetSpeed() to the Mac class and its subclasses. The modified driver is as follows:

Insert picture description here

This modification obviously violates the "opening and closing principle."

Is there a way to add GetSpeed() to the Mac class and its subclasses without modifying them?

The visitor model provides such a method.

Of course, there must be a prerequisite for using the visitor mode, that is, when designing the Mac class, we need to anticipate the possibility of adding operations to the Mac class in the future, and reserve a common interface for dynamically adding operations at runtime.

Let's start with the visitor pattern from the beginning.

2 Introduction to Visitor Mode

The intent of the visitor pattern given by GoF is: to represent an operation that acts on each element in an object structure. It is that you can define new operations that act on these elements without changing the class of each element.

Specific to the above scenario: each element is the Mac class and its subclasses. The new operation is GetSpeed().

We define the new operation as Visitor, and add a subclass of Visitor every time a new operation is added.

When we define the Mac class, we need to reserve a general interface Accept (Visitor) for dynamically adding operations at runtime. Accept can receive new operations. In this way, no matter how many new operations are added later, the new operations can be applied to the Mac through Accept.

Let's look at the specific implementation of the visitor pattern.

3 Use visitor mode to implement network port driver

Participant

  1. Visitor

Declare which elements the visitor can access, and declare a Visitor operation for each element that can be accessed.

In the above scenario, there are two accessible elements: HighSpeedMac and LowSpeedMac. Therefore, it is necessary to declare the Visitor operations VisitLowSpeedMac() and VisitHignSpeedMac() for HighSpeedMac and LowSpeedMac respectively.

  1. ConcreteVisitor: GetSpeedVisitor

Implement the operations declared by the Visitor. Every time an operation is added, a ConcreteVisitor needs to be added.

Let's take the addition of GetSpeed() as an example. If we need to add GetMode() later, we only need to define GetModeVisitor again.

  1. Element: Mac

Need to reserve a general interface Accept for dynamically adding operations at runtime

  1. ConcreteElement: LowSpeedMac、HighSpeedMac

Realize the method and Accept of ConcreteElement itself.

  1. ObjectStruct

Provide an interface to enumerate Element. In this example, we will only define two Elements as examples, so we will list the Elements directly in the main function, without defining a special ObjectStruct.

UML

Insert picture description here

Visitor sample code

visitor.h

#ifndef VISITOR_H
#define VISITOR_H

#include "mac.h"

struct Visitor {
    void (*VisitLowSpeedMac)(struct Visitor *this, struct Mac *lowSpeedMac);
    void (*VisitHighSpeedMac)(struct Visitor *this, struct Mac *highSpeedMac);
};

#endif

GetSpeedVisitor sample code

get_speed_visitor.h

#ifndef GET_SPEED_VISITOR_H
#define GET_SPEED_VISITOR_H

#include "visitor.h"

// 构造函数
void GetSpeedVisitor(struct Visitor *this);

// 析构函数
void _GetSpeedVisitor(struct Visitor *this);

#endif

get_speed_visitor.c

#include "get_speed_visitor.h"
#include <stddef.h>
#include <stdio.h>

static void VisitLowSpeedMac(struct Visitor *this, struct Mac *lowSpeedMac)
{
    printf("  get speed for low speed mac, speed is \"%s\"\n", lowSpeedMac->speed);
}

static void VisitHighSpeedMac(struct Visitor *this, struct Mac *highSpeedMac)
{
    printf("  get speed for high speed mac, speed is \"%s\"\n", highSpeedMac->speed);
}

// 构造函数
void GetSpeedVisitor(struct Visitor *this)
{
    this->VisitLowSpeedMac = VisitLowSpeedMac;
    this->VisitHighSpeedMac = VisitHighSpeedMac;
}

// 析构函数
void _GetSpeedVisitor(struct Visitor *this)
{
    this->VisitLowSpeedMac = NULL;
    this->VisitHighSpeedMac = NULL;
}

Mac sample code

mac.h

#ifndef MAC_H
#define MAC_H

#include "visitor.h"

struct Mac {
    char mode[50];
    char speed[50];
    // 编译时已实现的操作
    void (*SetMode)(struct Mac *this, char *mode);
    void (*SetSpeed)(struct Mac *this, char *speed);
    // 用于在运行时扩展操作
    void (*Accept)(struct Mac *this, struct Visitor *visitor);
};

#endif

LowSpeedMac sample code

low_speed_mac.h

#ifndef LOW_SPEED_MAC_H
#define LOW_SPEED_MAC_H

#include "mac.h"

// 构造函数
void LowSpeedMac(struct Mac *this);

// 析构函数
void _LowSpeedMac(struct Mac *this);

#endif

low_speed_mac.c

#include "low_speed_mac.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h> 

static void SetMode(struct Mac *this, char *mode)
{
    strcpy(this->mode, mode);
    printf("  set mode for low speed mac, mode is \"%s\"\n", mode);
}

static void SetSpeed(struct Mac *this, char *speed)
{
    strcpy(this->speed, speed);
    printf("  set speed for low speed mac, speed is \"%s\"\n", speed);
}

static void Accept(struct Mac *this, struct Visitor *visitor)
{
    visitor->VisitLowSpeedMac(visitor, this);
}

// 构造函数
void LowSpeedMac(struct Mac *this)
{
    this->mode[0] = 0;
    this->speed[0] = 0;
    this->SetMode = SetMode;
    this->SetSpeed = SetSpeed;
    this->Accept = Accept;
}

// 析构函数
void _LowSpeedMac(struct Mac *this)
{
    this->mode[0] = 0;
    this->speed[0] = 0;
    this->SetMode = NULL;
    this->SetSpeed = NULL;
    this->Accept = NULL;
}

HighSpeedMac sample code

high_speed_mac.h

#ifndef HIGH_SPEED_MAC_H
#define HIGH_SPEED_MAC_H

#include "mac.h"

// 构造函数
void HighSpeedMac(struct Mac *this);

// 析构函数
void _HighSpeedMac(struct Mac *this);

#endif

high_speed_mac.c

#include "high_speed_mac.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h> 

static void SetMode(struct Mac *this, char *mode)
{
    strcpy(this->mode, mode);
    printf("  set mode for high speed mac, mode is \"%s\"\n", mode);
}

static void SetSpeed(struct Mac *this, char *speed)
{
    strcpy(this->speed, speed);
    printf("  set speed for high speed mac, speed is \"%s\"\n", speed);
}

static void Accept(struct Mac *this, struct Visitor *visitor)
{
    visitor->VisitHighSpeedMac(visitor, this);
}

// 构造函数
void HighSpeedMac(struct Mac *this)
{
    this->mode[0] = 0;
    this->speed[0] = 0;
    this->SetMode = SetMode;
    this->SetSpeed = SetSpeed;
    this->Accept = Accept;
}

// 析构函数
void _HighSpeedMac(struct Mac *this)
{
    this->mode[0] = 0;
    this->speed[0] = 0;
    this->SetMode = NULL;
    this->SetSpeed = NULL;
    this->Accept = NULL;
}

Client code example

#include "low_speed_mac.h"
#include "high_speed_mac.h"
#include "get_speed_visitor.h"
#include <stdio.h>

void main()
{
    struct Mac lowSpeedMac;
    LowSpeedMac(&lowSpeedMac);
    struct Mac highSpeedMac;
    HighSpeedMac(&highSpeedMac);

    printf("调用低速网口编译时已实现的操作:\n");
    lowSpeedMac.SetSpeed(&lowSpeedMac, "1G");
    lowSpeedMac.SetMode(&lowSpeedMac, "GMII");
    printf("\n");
    printf("调用高速网口编译时已实现的操作:\n");
    highSpeedMac.SetSpeed(&highSpeedMac, "50G");
    highSpeedMac.SetMode(&highSpeedMac, "SGMII");
    printf("\n");
   
    printf("--扩展getSpeed操作--\n");
    printf("\n");
    struct Visitor getSpeedVisitor;
    GetSpeedVisitor(&getSpeedVisitor);
    printf("调用低速网口运行时扩展的操作:\n");
    lowSpeedMac.Accept(&lowSpeedMac, &getSpeedVisitor);
    printf("\n");
    printf("调用高速网口运行时扩展的操作:\n");
    highSpeedMac.Accept(&highSpeedMac, &getSpeedVisitor);
    printf("\n");
}

Client display example

-bash-4.2# ./test
调用低速网口编译时已实现的操作:
  set speed for low speed mac, speed is "1G"
  set mode for low speed mac, mode is "GMII"

调用高速网口编译时已实现的操作:
  set speed for high speed mac, speed is "50G"
  set mode for high speed mac, mode is "SGMII"

--扩展getSpeed操作--

调用低速网口运行时扩展的操作:
  get speed for low speed mac, speed is "1G"

调用高速网口运行时扩展的操作:
  get speed for high speed mac, speed is "50G"

4 Restrictions on using visitor mode

Limit one:

We have mentioned one of the limitations in the previous description: when designing the Element, we anticipated that operations might be added to the Element in the future, and reserved a general interface for dynamically adding operations at runtime, namely: Accept().

Limit two:

There is also a stricter restriction: how many subclasses of Element must be stable.

Because in the Visitor needs to declare a Visitor operation for each element (ie: each subclass of Element). So when adding a subclass of Element, you need to modify the definition of Visitor to add a new Visitor operation. This obviously violates the "opening and closing principle."

So, please note: The scenario in this article actually assumes that Mac has only two subcategories, LowSpeedMac and HighSpeedMac. This is a stricter restriction. In reality, the sub-category of Mac is not stable, because it is very likely that SuperHighSpeedMac will need to be added in the future. In this case, the visitor model is not applicable.

Guess you like

Origin blog.csdn.net/weixin_46826913/article/details/107751534