現在のプロジェクトには、複雑なオブジェクト階層が含まれています。次の構造は、デモンストレーション目的でこの階層を単純化した例です。
- としょうかん
- カテゴリ「フィクション」
- カテゴリ「SF」
- ブック A (各ブックにはページが含まれていますが、ここには表示されていません)
- ブックB
- カテゴリ「犯罪」
- ブックC
- カテゴリ「ノンフィクション」
- (多くのサブカテゴリ)
ここで、データ構造からの情報が必要なときはいつでも、コード全体にネストされたループがないようにしたいと考えています。構造が変更されると、すべてのループを更新する必要があるからです。
そのため、必要な柔軟性が得られるビジター パターンを使用する予定です。次のようになります。
class Library
{
void Accept(ILibraryVisitor visitor)
{
IterateCategories(this.categories, visitor);
}
void IterateCategories(
IEnumerable<Category> categorySequence,
ILibraryVisitor visitor)
{
foreach (var category in categorySequence)
{
visitor.VisitCategory(category.Name);
IterateCategories(category.Subcategories, visitor);
foreach (var book in category.Books)
{
// Could also pass in a book instance, not sure about that yet...
visitor.VisitBook(book.Title, book.Author, book.PublishingDate);
foreach (var page in book.Pages)
{
visitor.VisitPage(page.Number, page.Content);
}
}
}
}
}
interface ILibraryVisitor
{
void VisitCategory(string name);
void VisitBook(string title, string author, DateTime publishingDate);
void VisitPage(int pageNumber, string content);
}
すでに問題が発生している可能性があるので、アドバイスをいただければ幸いです。
質問1
属している (サブ) カテゴリ (例: Fiction » Science Fiction » Book A )を前に付けた本のタイトルのリストを作成したい場合は、単純なビジター実装でうまくいくように見えます。
// LibraryVisitor is a base implementation with no-op methods
class BookListingVisitor : LibraryVisitor
{
private Stack<string> categoryStack = new Stack<string>();
void VisitCategory(string name)
{
this.categoryStack.Push(name);
}
// Other methods
}
ここで、すでに問題に直面しています。カテゴリがいつ終了するかがわからないため、いつスタックをポップするかについての手がかりがありません。以下のように、VisitCategory メソッドを 2 つのメソッドに分割するのは一般的なアプローチですか?
interface ILibraryVisitor
{
void VisitCategoryStart(string name);
void VisitCategoryEnd();
// Other methods
}
それとも、このような構造を扱う他の方法があり、開始と終了の明確な範囲がありますか?
質問2
1982 年に出版された本だけを一覧表示したいとします。decorator ビジターは、フィルタリングを一覧表示ロジックから分離します。
class BooksPublishedIn1982 : LibraryVisitor
{
private ILibraryVisitor visitor;
public BooksPublishedIn1982(ILibraryVisitor visitor)
{
this.visitor = visitor;
}
void VisitBook(string title, string author, DateTime publishingDate)
{
if (publishingDate.Year == 1982)
{
this.visitor.VisitBook(string title, string author, publishingDate);
}
}
// Other methods that simply delegate to this.visitor
}
ここでの問題は、VisitPage が 1982 年に出版されていない本に対して呼び出されることです。そのため、デコレーターは何らかの形で訪問済みオブジェクトと通信する必要があります。
訪問者: 「この本は 1982 年のものではないので、何も言わないでください。」
図書館: 「わかりました。では、そのページは表示しません。」
現在、訪問メソッドは void を返します。サブアイテムにアクセスするかどうかを示すブール値を返すように変更することもできますが、それはちょっと汚い感じがします。特定の項目をスキップする必要があることを訪問者に知らせる一般的な方法はありますか? それとも、別の設計パターンを検討する必要がありますか?
PS これらが 2 つの別個の質問であると思われる場合は、お知らせください。喜んで分割させていただきます。