シナリオ
「ショッピングカートに複数の商品があり、それぞれの商品に対して異なる割引計算を行う」というシナリオで考える。各商品には異なるタイプがあり(例えば、食品や家電など)、割引の計算は商品タイプごとに異なる。
コード例
using System;
using System.Collections.Generic;
// Visitor interface - 割引を適用するためのインターフェース
interface IDiscountVisitor
{
void Visit(FoodItem item);
void Visit(ElectronicsItem item);
}
// ConcreteVisitor - 具体的な割引計算を行うクラス
class DiscountVisitor : IDiscountVisitor
{
public void Visit(FoodItem item)
{
// 食品に10%の割引を適用
Console.WriteLine($"Applying 10% discount to food item: {item.Name}");
item.Price *= 0.9;
}
public void Visit(ElectronicsItem item)
{
// 家電に15%の割引を適用
Console.WriteLine($"Applying 15% discount to electronics item: {item.Name}");
item.Price *= 0.85;
}
}
// Element interface - 商品のインターフェース
interface ICartItem
{
void Accept(IDiscountVisitor visitor);
string Name { get; }
double Price { get; set; }
}
// ConcreteElementA - 食品の商品クラス
class FoodItem : ICartItem
{
public string Name { get; private set; }
public double Price { get; set; }
public FoodItem(string name, double price)
{
Name = name;
Price = price;
}
public void Accept(IDiscountVisitor visitor)
{
visitor.Visit(this);
}
}
// ConcreteElementB - 家電の商品クラス
class ElectronicsItem : ICartItem
{
public string Name { get; private set; }
public double Price { get; set; }
public ElectronicsItem(string name, double price)
{
Name = name;
Price = price;
}
public void Accept(IDiscountVisitor visitor)
{
visitor.Visit(this);
}
}
// Client code
class Program
{
static void Main(string[] args)
{
List<ICartItem> cartItems = new List<ICartItem>
{
new FoodItem("Apple", 100),
new ElectronicsItem("Laptop", 1000)
};
IDiscountVisitor discountVisitor = new DiscountVisitor();
foreach (var item in cartItems)
{
item.Accept(discountVisitor);
Console.WriteLine($"{item.Name} after discount: {item.Price:C}");
}
}
}
クラス図
説明
この具体例では、Visitorパターンを使うことで、商品ごとの異なる割引ロジックをクラスに変更を加えることなく柔軟に追加できている。
どんなケースで有効か
ビジターパターンは、データ構造が比較的安定しており、操作が頻繁に追加・変更される場合に特に有効。この場合、ビジターパターンを利用することで、データ構造クラスに手を加えることなく新しいビジターを追加するだけで、異なる操作を実装できる。これにより、クラスの保守性が向上する。
コンパイラやインタプリタの開発
ケース: 抽象構文木(AST)を使ってプログラムを解析、最適化、コード生成する。
ビジターパターンの利点: 各解析フェーズ(型チェック、コード生成など)を別々のビジターとして実装できるため、新しい解析処理を追加しやすい。
グラフィック描画
ケース: 異なるグラフィック要素(線、円、矩形など)を複数の描画エンジンや出力形式(画面表示、印刷、SVGなど)に対応させる。
ビジターパターンの利点: 各描画エンジンや出力形式ごとにビジターを作成することで、描画方法を柔軟に拡張できる。
他記事
Visitorパターンで型によるswitchやif判定を消す
https://qiita.com/toRisouP/items/d96a09fab827af17fb37
【C#】データ構造と処理を分離するVisitorパターンを学ぶ
https://www.hanachiru-blog.com/entry/2021/02/18/120000