ビジターパターンは oop メカニズム、特にポリモーフィズムを利用します。このパターンは、多数のトークンを独自に処理する必要があるパーサーの実装に役立ちます。しかし、C などの非 oop 言語の場合、解決策は何ですか? switch-case制御ステートメントの長い文字列は目的のためだと思います。
1 に答える
OOP 言語は、優れた型チェックとシンタックス シュガーを除けば、命令型言語ほど特別なものではありません。それ以外には、基本的な Java や C++ から C を切り離すものは何もありません。多くの OOP に似た機能は、非 OOP 言語で実装できます (実装されています)。たとえば、C では、 とstruct
、これらの を操作する関数を定義するのが一般的ですstruct
(通常、それらを最初の引数として使用します)。仮想メソッドまたは抽象メソッドは、コールバックにすぎません。
ここではウィキペディアから例を盗みます。ビジター パターンを使用して保存する CAD プログラム用の 3D オブジェクトの階層があるとします。オブジェクト階層には、線、円、円弧、および三角形が含まれています。OBJ ファイル用のビジターと 3DS ファイル用のビジターが必要です。
まず、ビジター構造を定義しましょう。
typedef struct _Visitor {
void (*visitLine)(struct _Visitor *, Line *);
void (*visitCircle(struct _Visitor *, Circle *);
void (*visitArc)(struct _Visitor *, Arc *);
void (*visitTriangle)(struct _Visitor *, Triangle *);
} Visitor;
の各コールバック メソッドがどのように最初の引数としてVisitor
aを受け取るかに注目してください。この引数は、呼び出し規約によると、OOP 言語の(または)struct _Visitor
と意味的に同等です。これにより、ファイル形式固有の Visitor の最初のフィールドを作成することで、追加のデータを に保存できます。this
self
Visitor
Visitor
これで、OBJ および 3DS 形式のビジターを定義できます。
typedef struct _OBJVisitor {
struct _Visitor visitor;
int objSpecific1; // This is just an example, these fields can be whatever you want,
... // the key is that the struct _Visitor is the first element,
... // because this lets us freely cast between Visitor and OBJVisitor
} OBJVisitor;
typedef struct __3DSVisitor {
struct _Visitor visitor;
int _3dsSpecific1;
...
} _3DSVisitor; // C names can't start with a number...
ここで、Visitor
これらの構造体の 1 つに固有の を返す、この構造体の 2 つのコンストラクターを定義します。ジェネリックVisitor
といずれかOBJVisitor
またはの間で自由に変換する方法に注意してください_3DSVisitor
。このプラットフォーム間での変換の自由は、C 標準によって保証されています。
Visitor *mkOBJVisitor(int objSpecific1, ...) { // The constructor can take any other arguments as necessary
OBJVisitor* objVisitor = (OBJVisitor *) malloc(sizeof(OBJVisitor));
objVisitor->visitor.visitLine = OBJVisitor_visitLine;
objVisitor->visitor.visitCircle = OBJVisitor_visitCircle;
objVisitor->visitor.visitArc = OBJVisitor_visitArc;
objVisitor->visitor.visitTriangle = OBJVisitor_visitTriangle;
objVisitor->objSpecific1 = objSpecific1;
return (Visitor *) objVisitor;
}
void OBJVisitor_visitLine(struct _Visitor *_this, Line *line)
{
OBJVisitor *this = (OBJVisitor *) this; // This convertibility is guaranteed.
... // implementation of line serialization
}
3DSの機能とそれ以外のOBJVisitor_*
機能は同じなので割愛します。
これで、同じトリックを使用して、Acceptor
各オブジェクトの構造体を作成できます。
typedef struct _Acceptor {
void (*accept)(struct _Acceptor *, Visitor *);
} Acceptor;
typedef struct Circle {
Acceptor acceptor;
double radius;
double x, y;
... // Any other information you want
}
... // Define similar structures for the other shapes
これで、シェイプ コンストラクターで、適切なアクセプター コールバックを設定できます。
Circle *mkCircle(double radius, double x, double y)
{
Circle *circle = (Circle *) malloc(sizeof(Circle));
circle->acceptor.accept = Circle_accept;
circle->radius = radius;
circle->x = x;
circle->y = y;
return circle;
}
このメソッドは、 で適切な関数Circle_accept
を呼び出します。visit*
Visitor
void Circle_accept(struct _Acceptor *_this, Visitor *visitor)
{
visitor->visitCircle(visitor, (Circle *) _this);
}
visitor
への最初の引数として渡す方法に注意してくださいvisitor->visitCircle
。C には OOP のサポートが組み込まれていないため、visitCircle
メソッドを呼び出している「オブジェクト」を自動的に渡すことはありません。しかし、これは大した問題ではありません。いつでも自分でできるからです。
ここで、これらの形状を子として含み、それ自体がアクセプターであるグループ オブジェクトがあるとします。
typedef struct _Group {
Acceptor acceptor;
int childCount;
Acceptor **children;
} Group;
Group *mkGroup(int childCount, Acceptor** children)
{
Group *group = (Group *) malloc(sizeof(Group));
group->acceptor.accept = Group_accept;
... // Omitting code for clarity
}
これで、シェイプを階層に配置し、最上位レベルで accept を呼び出すだけで、すべてのシェイプにアクセスできますGroup
。
void Group_accept(struct _Acceptor *_this, Visitor *visitor)
{
Group *this = (Group *) this;
int i;
for(i = 0; i < this->childCount; ++i) {
this->children[i]->accept(this->children[i], visitor); // Invoke the visitor on each of our children, including any group nodes.
}
}
ご覧のとおり、OOP は命令型プログラミングの単なる拡張です。上記では、インターフェイス、継承、仮想 (または抽象) メソッド、およびコンストラクターをプレーンな古い C で実装しました。これらのアイデアを拡張して、オブジェクト システム全体を作成できます。そこから、ビジター パターンを作成するのは簡単です。唯一の問題は、明確に定義されたコーディングと命名規則に固執しない限り、コールバックやメモリ リークなどで簡単に混乱してしまうことです。
楽しく (そして慎重に) コーディングしてください!