Javaにおける非循環ビジターパターン:オブジェクト間の相互作用の合理化
非循環ビジターデザインパターンの意図
Javaにおける非循環ビジターパターンは、操作をオブジェクト階層から切り離し、さまざまなアプリケーションに対して柔軟な設計を提供します。
現実世界の例を用いた非循環ビジターパターンの詳細な説明
現実世界の例
Javaにおける非循環ビジターパターンの現実世界の類似例として、博物館のガイドシステムがあります。これは、このデザインパターンの実践的な応用を示しています。絵画、彫刻、歴史的遺物など、さまざまな展示物がある博物館を想像してみてください。博物館には、それぞれの展示物に関する情報を提供するさまざまな種類のガイド(音声ガイド、人間によるガイド、バーチャルリアリティガイド)があります。新しいガイドの種類が導入されるたびに展示物を変更する代わりに、各ガイドは異なる展示の種類にアクセスするためのインターフェースを実装します。このように、博物館は既存の展示物を変更することなく新しい種類のガイドを追加でき、システムは依存関係の循環を形成することなく、拡張可能かつ保守可能な状態を維持します。
簡単に言うと
非循環ビジターを使用すると、階層を変更せずに既存のクラス階層に機能を追加できます。
WikiWikiWebによると
非循環ビジターパターンでは、GangOfFourビジターパターンに固有の依存関係の循環を作成することなく、既存のクラス階層に影響を与えることなく、既存のクラス階層に新しい機能を追加できます。
Javaにおける非循環ビジターのプログラミング例
このJavaの例では、非循環ビジターパターンを示すモデムクラスの階層があります。この階層のモデムは、フィルタリング基準(UnixまたはDOS互換のモデムかどうか)に基づいた外部アルゴリズムによってアクセスされる必要があります。
こちらがModem
階層です。
public abstract class Modem {
public abstract void accept(ModemVisitor modemVisitor);
}
public class Zoom extends Modem {
// Other properties and methods...
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof ZoomVisitor) {
((ZoomVisitor) modemVisitor).visit(this);
} else {
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
}
}
}
public class Hayes extends Modem {
// Other properties and methods...
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof HayesVisitor) {
((HayesVisitor) modemVisitor).visit(this);
} else {
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
}
}
}
次に、ModemVisitor
階層を紹介します。
public interface ModemVisitor {
}
public interface HayesVisitor extends ModemVisitor {
void visit(Hayes hayes);
}
public interface ZoomVisitor extends ModemVisitor {
void visit(Zoom zoom);
}
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
}
public class ConfigureForDosVisitor implements AllModemVisitor {
// Other properties and methods...
@Override
public void visit(Hayes hayes) {
LOGGER.info(hayes + " used with Dos configurator.");
}
@Override
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Dos configurator.");
}
}
public class ConfigureForUnixVisitor implements ZoomVisitor {
// Other properties and methods...
@Override
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Unix configurator.");
}
}
最後に、実際にビジターを使用する例を示します。
public static void main(String[] args) {
var conUnix = new ConfigureForUnixVisitor();
var conDos = new ConfigureForDosVisitor();
var zoom = new Zoom();
var hayes = new Hayes();
hayes.accept(conDos); // Hayes modem with Dos configurator
zoom.accept(conDos); // Zoom modem with Dos configurator
hayes.accept(conUnix); // Hayes modem with Unix configurator
zoom.accept(conUnix); // Zoom modem with Unix configurator
}
プログラム出力
09:15:11.125 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Hayes modem used with Dos configurator.
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Zoom modem used with Dos configurator.
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.Hayes -- Only HayesVisitor is allowed to visit Hayes modem
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
非循環ビジターパターンのクラス図

Javaで非循環ビジターパターンを使用する場面
このパターンは、以下の場合に使用できます。
- 階層を変更または影響を与えることなく、既存の階層に新しい機能を追加する必要がある場合。
- 階層に対して動作するが、階層自体には属さない機能がある場合。例:ConfigureForDOS/ConfigureForUnix/ConfigureForXの問題。
- オブジェクトの種類に応じて、非常に異なる操作を実行する必要がある場合。
- Elementクラスの新しい派生体が頻繁に追加される場合。
- Elementの派生体の再コンパイル、再リンク、再テスト、再配布が非常に高価な場合。
非循環ビジターパターンのJavaチュートリアル
非循環ビジターパターンの利点とトレードオフ
利点
- 拡張性:オブジェクト構造を変更せずに、簡単に新しい操作を追加できます。
- デカップリング:オブジェクトとそのオブジェクトに対する操作間の結合を減らします。
- 依存関係の循環なし:非循環的な依存関係を保証し、保守性を向上させ、複雑さを軽減します。
トレードオフ
- 複雑性の増加:複数のビジターインターフェースが必要になるため、追加の複雑さが発生する可能性があります。
- 保守オーバーヘッド:オブジェクト階層の変更には、すべてのビジターの更新が必要です。
関連するJavaデザインパターン
- コンポジット:個々のオブジェクトと合成を均一に扱うことを可能にするために、非循環ビジターと併用されることが多いです。
- デコレータ:オブジェクトに動的に責任を追加するために併用できます。
- ビジター:非循環ビジターパターンは、循環依存を回避するビジターパターンのバリエーションです。