訪問者モード
1シミュレーションシーン
ネットワークポートドライバーを開発するとします。ネットワークポートには、低速ネットワークポートと高速ネットワークポートの2種類があります。各タイプのネットワークポートは、構成レートと構成モードの操作をサポートしています。すると、ドライバーは次のようになります。
上記の手順が実装されていると仮定すると、今はビジネスニーズのため、クエリレート操作を追加する必要があります。
次に、実装されたMacクラスとそのサブクラスを変更し、MacクラスとそのサブクラスにGetSpeed()を追加する必要があります。変更されたドライバーは次のとおりです。
この変更は明らかに「開閉の原則」に違反しています。
GetSpeed()をMacクラスとそのサブクラスに変更せずに追加する方法はありますか?
ビジターモデルはそのような方法を提供します。
もちろん、ビジターモデルを使用するための前提条件が必要です。つまり、Macクラスを設計するとき、将来的にMacクラスに操作を追加する可能性があることを予測し、実行時に操作を動的に追加するための共通インターフェイスを予約する必要があります。
最初からビジターパターンから始めましょう。
2訪問者モードの概要
GoFによって提供されるビジターパターンの目的は、オブジェクト構造の各要素に作用する操作を表すことです。つまり、各要素のクラスを変更せずに、これらの要素に作用する新しい操作を定義できます。
上記のシナリオに固有:各要素はMacクラスとそのサブクラスです。新しい操作はGetSpeed()です。
新しいオペレーションをVisitorとして定義し、新しいオペレーションが追加されるたびにVisitorのサブクラスを追加します。
Macクラスを定義するとき、実行時に動的に操作を追加するための一般的なインターフェイスAccept(Visitor)を予約する必要があります。Acceptは新しいオペレーションを受け取ることができます。このようにして、後で追加される新しい操作の数に関係なく、新しい操作をAcceptを介してMacに適用できます。
ビジターパターンの特定の実装を見てみましょう。
3ビジターモードを使用してネットワークポートドライバーを実装する
参加者
- ビジター
訪問者がアクセスできる要素を宣言し、アクセス可能な要素ごとに訪問者操作を宣言します。
上記のシナリオでは、HighSpeedMacとLowSpeedMacの2つのアクセス可能な要素があります。したがって、HighSpeedMacおよびLowSpeedMacに対して、Visitor操作VisitLowSpeedMac()およびVisitHignSpeedMac()をそれぞれ宣言する必要があります。
- ConcreteVisitor:GetSpeedVisitor
訪問者が宣言した操作を実装します。操作が追加されるたびに、ConcreteVisitorを追加する必要があります。
例としてGetSpeed()の追加を見てみましょう。後でGetMode()を追加する必要がある場合は、もう一度GetModeVisitorを定義するだけで済みます。
- 要素:Mac
実行時に動的に操作を追加するための一般的なインターフェースAcceptを予約する必要があります
- ConcreteElement:LowSpeedMac、HighSpeedMac
ConcreteElement自体のメソッドとAcceptを実現します。
- ObjectStruct
Elementを列挙するためのインターフェースを提供します。この例では、例として2つの要素のみを定義するため、特別なObjectStructを定義せずに、メイン関数に直接要素をリストします。
UML
訪問者サンプルコード
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サンプルコード
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サンプルコード
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サンプルコード
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サンプルコード
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;
}
クライアントコードの例
#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");
}
クライアントの表示例
-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ビジターモードの使用に関する制限
制限:
前の説明で制限の1つを述べました。要素を設計するときに、将来的に要素に操作が追加される可能性があることを想定し、実行時に動的に操作を追加するための一般的なインターフェイス、つまりAccept()を予約しました。
制限2:
また、より厳しい制限もあります。Elementのサブクラスの数は、安定している必要があります。
ビジターでは、各要素(つまり、Elementの各サブクラス)に対してビジターオペレーションを宣言する必要があるためです。したがって、Elementのサブクラスを追加するときは、Visitorの定義を変更して新しいVisitor操作を追加する必要があります。これは明らかに「開閉の原則」に違反しています。
したがって、注意してください:この記事のシナリオでは、MacにはLowSpeedMacとHighSpeedMacの2つのサブカテゴリしかないと想定しています。これはより厳しい制限です。実際には、Macのサブカテゴリは安定していません。これは、将来SuperHighSpeedMacを追加する必要がある可能性が非常に高いためです。この場合、ビジターモデルは適用されません。