Design patterns 什么时候应该使用访问者设计模式?
我一直在博客中看到对访问者模式的引用,但我必须承认,我就是不明白。我读了这本书,我理解它的原理,但我仍然不知道什么时候用它Design patterns 什么时候应该使用访问者设计模式?,design-patterns,visitor-pattern,Design Patterns,Visitor Pattern,我一直在博客中看到对访问者模式的引用,但我必须承认,我就是不明白。我读了这本书,我理解它的原理,但我仍然不知道什么时候用它 作为一个最近才真正得到装饰图案的人,现在看到它在任何地方都有应用,我希望能够直观地理解这个看似方便的图案。你困惑的原因可能是访客是一个致命的误称。许多(杰出的)程序员都在这个问题上遇到了困难。它实际上是用本机不支持它的语言实现的(大多数语言都不支持) 1)我最喜欢的例子是“有效C++”的作者Scott Meyers,他把这个叫做“有效的C++”。 < P>一个方法是,访
作为一个最近才真正得到装饰图案的人,现在看到它在任何地方都有应用,我希望能够直观地理解这个看似方便的图案。你困惑的原因可能是访客是一个致命的误称。许多(杰出的)程序员都在这个问题上遇到了困难。它实际上是用本机不支持它的语言实现的(大多数语言都不支持)
1)我最喜欢的例子是“有效C++”的作者Scott Meyers,他把这个叫做“有效的C++”。
< P>一个方法是,访问者模式是让你的客户在特定的类层次中添加你的类的额外方法。 当您有一个相当稳定的类层次结构,但您对该层次结构的需求不断变化时,它非常有用 典型的例子适用于编译器等。抽象语法树(Abstract Syntax Tree,AST)可以准确地定义编程语言的结构,但您可能希望在AST上执行的操作将随着项目的进展而改变:代码生成器、漂亮的打印机、调试器、复杂性度量分析 如果没有访问者模式,每次开发人员想要添加新功能时,他们都需要将该方法添加到基类中的每个功能中。当基类出现在单独的库中或由单独的团队生成时,这一点尤其困难(我听说有人认为访问者模式与良好的OO实践相冲突,因为它将数据的操作从数据中移开。访问者模式在正常OO实践失败的情况下非常有用。)我对访问者模式不太熟悉。让我们看看我是否做对了。假设你有一个动物等级
类动物{};
犬类:公共动物{};
猫类:公共动物{};
(假设它是一个具有完善接口的复杂层次结构。)
现在我们想在层次结构中添加一个新的操作,即我们希望每个动物都发出声音。只要层次结构如此简单,您就可以直接使用多态性:
类动物
{public:virtual void makeSound()=0;};
犬类:公共动物
{public:void makeSound();};
void Dog::makeSound()
{std::cout hereiscat(这个);}
最后,我们在不修改Cat和Dog的情况下实现了实际操作:
class声音:公共操作
{
公众:
无效的狗(狗*d);
此处无效(c类);
};
虚空之声::hereIsADog(Dog*d)
{std::coutVisitor设计模式非常适合于“递归”结构,如目录树、XML结构或文档大纲
访问者对象访问递归结构中的每个节点:每个目录、每个XML标记,等等。访问者对象不会在结构中循环。相反,访问者方法应用于结构的每个节点
这是一个典型的递归节点结构。可以是目录或XML标记。
[如果您是Java用户,想象一下有很多额外的方法来构建和维护子列表。]
class TreeNode( object ):
def __init__( self, name, *children ):
self.name= name
self.children= children
def visit( self, someVisitor ):
someVisitor.arrivedAt( self )
someVisitor.down()
for c in self.children:
c.visit( someVisitor )
someVisitor.up()
visit
方法将访问者对象应用于结构中的每个节点。在这种情况下,它是一个自顶向下的访问者。您可以更改visit
方法的结构以进行自底向上或其他排序
这是访问者的超类。visit
方法使用它。它“到达”结构中的每个节点。由于visit
方法调用up
和down
,访问者可以跟踪深度
class Visitor( object ):
def __init__( self ):
self.depth= 0
def down( self ):
self.depth += 1
def up( self ):
self.depth -= 1
def arrivedAt( self, aTreeNode ):
print self.depth, aTreeNode.name
子类可以做一些事情,比如在每一级计算节点数,并累积节点列表,生成一个很好的路径层次分区号
这是一个应用程序。它构建了一个树结构,someTree
。它创建了一个Visitor
,dumpNodes
然后它将dumpNodes
应用于树。dumpNode
对象将“访问”树中的每个节点
someTree= TreeNode( "Top", TreeNode("c1"), TreeNode("c2"), TreeNode("c3") )
dumpNodes= Visitor()
someTree.visit( dumpNodes )
TreeNodevisit
算法将确保每个TreeNode都用作访问者的arrivedAt
方法的参数。这里的每个人都是正确的,但我认为它无法解决“何时”。首先,从设计模式:
Visitor允许您定义一个新的
操作而不更改类
它所操作的元素的名称
现在,让我们考虑一个简单的类层次结构。我有类1、2、3和4以及方法a、B、C和D。将它们像电子表格一样排列:类是线,方法是列
现在,面向对象的设计假定您更可能增长新类而不是新方法,因此可以说,添加更多行更容易。您只需添加一个新类,指定该类中的不同之处,然后继承其余部分
尽管有时类是相对静态的,但您需要经常添加更多的方法——添加列。OO设计中的标准方法是将此类方法添加到所有类中,这可能会很昂贵。Visitor模式使这变得很容易
顺便说一句,这是Scala模式匹配想要解决的问题。在我看来,使用访问者模式或直接修改每个元素结构来添加新操作的工作量或多或少是相同的。此外,如果我要添加新的元素类,比如Cow
,操作界面将受影响,这会传播到所有现有的元素类,因此需要重新编译所有元素类。那么这有什么意义呢?访问者模式作为
class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};
class ExplodingAsteroid : public Asteroid {
public:
virtual void CollideWith(SpaceShip&) {
cout << "ExplodingAsteroid hit a SpaceShip" << endl;
}
virtual void CollideWith(ApolloSpacecraft&) {
cout << "ExplodingAsteroid hit an ApolloSpacecraft" << endl;
}
}
namespace DesignPatterns
{
public class BlisterPack
{
// Pairs so x2
public int TabletPairs { get; set; }
}
public class Bottle
{
// Unsigned
public uint Items { get; set; }
}
public class Jar
{
// Signed
public int Pieces { get; set; }
}
}
foreach (var item in packageList)
{
if (item.GetType() == typeof (BlisterPack))
{
pillCount += ((BlisterPack) item).TabletPairs * 2;
}
else if (item.GetType() == typeof (Bottle))
{
pillCount += (int) ((Bottle) item).Items;
}
else if (item.GetType() == typeof (Jar))
{
pillCount += ((Jar) item).Pieces;
}
}
public class PillCountVisitor : IVisitor
{
public int Count { get; private set; }
#region IVisitor Members
public void Visit(BlisterPack blisterPack)
{
Count += blisterPack.TabletPairs * 2;
}
public void Visit(Bottle bottle)
{
Count += (int)bottle.Items;
}
public void Visit(Jar jar)
{
Count += jar.Pieces;
}
#endregion
}
public class BlisterPack : IAcceptor
{
public int TabletPairs { get; set; }
#region IAcceptor Members
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
#endregion
}
var visitor = new PillCountVisitor();
foreach (IAcceptor item in packageList)
{
item.Accept(visitor);
}
public class HourlyEmployee extends Employee {
public String reportQtdHoursAndPay() {
//generate the line for this hourly employee
}
}
import java.util.HashMap;
interface Visitable{
void accept(Visitor visitor);
}
interface Visitor{
void logGameStatistics(Chess chess);
void logGameStatistics(Checkers checkers);
void logGameStatistics(Ludo ludo);
}
class GameVisitor implements Visitor{
public void logGameStatistics(Chess chess){
System.out.println("Logging Chess statistics: Game Completion duration, number of moves etc..");
}
public void logGameStatistics(Checkers checkers){
System.out.println("Logging Checkers statistics: Game Completion duration, remaining coins of loser");
}
public void logGameStatistics(Ludo ludo){
System.out.println("Logging Ludo statistics: Game Completion duration, remaining coins of loser");
}
}
abstract class Game{
// Add game related attributes and methods here
public Game(){
}
public void getNextMove(){};
public void makeNextMove(){}
public abstract String getName();
}
class Chess extends Game implements Visitable{
public String getName(){
return Chess.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
class Checkers extends Game implements Visitable{
public String getName(){
return Checkers.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
class Ludo extends Game implements Visitable{
public String getName(){
return Ludo.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
public class VisitorPattern{
public static void main(String args[]){
Visitor visitor = new GameVisitor();
Visitable games[] = { new Chess(),new Checkers(), new Ludo()};
for (Visitable v : games){
v.accept(visitor);
}
}
}
Logging Chess statistics: Game Completion duration, number of moves etc..
Logging Checkers statistics: Game Completion duration, remaining coins of loser
Logging Ludo statistics: Game Completion duration, remaining coins of loser
public interface IAnimal
{
void DoSound();
}
public class Dog : IAnimal
{
public void DoSound()
{
Console.WriteLine("Woof");
}
}
public class Cat : IAnimal
{
public void DoSound(IOperation o)
{
Console.WriteLine("Meaw");
}
}
using System;
using System.Collections.Generic;
namespace VisitorPattern
{
class Program
{
static void Main(string[] args)
{
var animals = new List<IAnimal>
{
new Cat(), new Cat(), new Dog(), new Cat(),
new Dog(), new Dog(), new Cat(), new Dog()
};
foreach (var animal in animals)
{
animal.DoOperation(new Walk());
animal.DoOperation(new Sound());
}
Console.ReadLine();
}
}
public interface IOperation
{
void PerformOperation(Dog dog);
void PerformOperation(Cat cat);
}
public class Walk : IOperation
{
public void PerformOperation(Dog dog)
{
Console.WriteLine("Dog walking");
}
public void PerformOperation(Cat cat)
{
Console.WriteLine("Cat Walking");
}
}
public class Sound : IOperation
{
public void PerformOperation(Dog dog)
{
Console.WriteLine("Woof");
}
public void PerformOperation(Cat cat)
{
Console.WriteLine("Meaw");
}
}
public interface IAnimal
{
void DoOperation(IOperation o);
}
public class Dog : IAnimal
{
public void DoOperation(IOperation o)
{
o.PerformOperation(this);
}
}
public class Cat : IAnimal
{
public void DoOperation(IOperation o)
{
o.PerformOperation(this);
}
}
}
void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)
public interface Piece{
boolean checkMoveValidity(Coordinates coord);
void performMove(Coordinates coord);
Piece computeIfKingCheck();
}
public class Pawn implements Piece{
@Override
public boolean checkMoveValidity(Coordinates coord) {
...
}
@Override
public void performMove(Coordinates coord) {
...
}
@Override
public Piece computeIfKingCheck() {
...
}
}
public interface PieceMovingVisitor {
void visitPawn(Pawn pawn);
void visitKing(King king);
void visitQueen(Queen queen);
void visitKnight(Knight knight);
void visitRook(Rook rook);
void visitBishop(Bishop bishop);
}
public interface Piece {
void accept(PieceMovingVisitor pieceVisitor);
Coordinates getCoordinates();
void setCoordinates(Coordinates coordinates);
}
void accept(PieceMovingVisitor pieceVisitor);
public class Bishop implements Piece {
private Coordinates coord;
public Bishop(Coordinates coord) {
super(coord);
}
@Override
public void accept(PieceMovingVisitor pieceVisitor) {
pieceVisitor.visitBishop(this);
}
@Override
public Coordinates getCoordinates() {
return coordinates;
}
@Override
public void setCoordinates(Coordinates coordinates) {
this.coordinates = coordinates;
}
}
// 1. Player requests a move for a specific piece
Piece piece = selectPiece();
Coordinates coord = selectCoordinates();
// 2. We check with MoveCheckingVisitor that the request is valid
final MoveCheckingVisitor moveCheckingVisitor = new MoveCheckingVisitor(coord);
piece.accept(moveCheckingVisitor);
// 3. If the move is valid, MovePerformingVisitor performs the move
if (moveCheckingVisitor.isValid()) {
piece.accept(new MovePerformingVisitor(coord));
}
void setCoordinates(Coordinates coordinates);
call the visited (piece) -> that calls the visitor (pieceMovingVisitor)
call the visited (piece) -> that calls the visitor (pieceMovingVisitor) -> that calls the visited (piece)
import static java.lang.System.out;
public class Visitor_2 {
public static void main(String...args) {
Hearen hearen = new Hearen();
FoodImpl food = new FoodImpl();
hearen.showTheHobby(food);
Katherine katherine = new Katherine();
katherine.presentHobby(food);
}
}
interface Hobby {
void insert(Hearen hearen);
void embed(Katherine katherine);
}
class Hearen {
String name = "Hearen";
void showTheHobby(Hobby hobby) {
hobby.insert(this);
}
}
class Katherine {
String name = "Katherine";
void presentHobby(Hobby hobby) {
hobby.embed(this);
}
}
class FoodImpl implements Hobby {
public void insert(Hearen hearen) {
out.println(hearen.name + " start to eat bread");
}
public void embed(Katherine katherine) {
out.println(katherine.name + " start to eat mango");
}
}
import static java.lang.System.out;
public class Visitor_2 {
public static void main(String...args) {
Hearen hearen = new Hearen();
FoodImpl food = new FoodImpl();
hearen.showHobby(food);
Katherine katherine = new Katherine();
katherine.showHobby(food);
}
}
interface Hobby {
void insert(Hearen hearen);
void insert(Katherine katherine);
}
abstract class Person {
String name;
protected Person(String n) {
this.name = n;
}
abstract void showHobby(Hobby hobby);
}
class Hearen extends Person {
public Hearen() {
super("Hearen");
}
@Override
void showHobby(Hobby hobby) {
hobby.insert(this);
}
}
class Katherine extends Person {
public Katherine() {
super("Katherine");
}
@Override
void showHobby(Hobby hobby) {
hobby.insert(this);
}
}
class FoodImpl implements Hobby {
public void insert(Hearen hearen) {
out.println(hearen.name + " start to eat bread");
}
public void insert(Katherine katherine) {
out.println(katherine.name + " start to eat mango");
}
}
//psuedo code
if(payPal)
do paypal checkout
if(stripe)
do strip stuff checkout
if(payoneer)
do payoneer checkout
new PaymentCheckoutVistor(paymentType).visit()
public class Employee
{
}
public class SalariedEmployee : Employee
{
}
public class HourlyEmployee : Employee
{
}
public class QtdHoursAndPayReport
{
public void PrintReport()
{
var employees = new List<Employee>
{
new SalariedEmployee(),
new HourlyEmployee()
};
foreach (Employee e in employees)
{
if (e is HourlyEmployee he)
PrintReportLine(he);
if (e is SalariedEmployee se)
PrintReportLine(se);
}
}
public void PrintReportLine(HourlyEmployee he)
{
System.Diagnostics.Debug.WriteLine("hours");
}
public void PrintReportLine(SalariedEmployee se)
{
System.Diagnostics.Debug.WriteLine("fix");
}
}
class Program
{
static void Main(string[] args)
{
new QtdHoursAndPayReport().PrintReport();
}
}
public abstract class Employee
{
public abstract void Accept(EmployeeVisitor v);
}
public class SalariedEmployee : Employee
{
public override void Accept(EmployeeVisitor v)
{
v.Visit(this);
}
}
public class HourlyEmployee:Employee
{
public override void Accept(EmployeeVisitor v)
{
v.Visit(this);
}
}
public interface EmployeeVisitor
{
void Visit(HourlyEmployee he);
void Visit(SalariedEmployee se);
}
public class QtdHoursAndPayReport : EmployeeVisitor
{
public void Visit(HourlyEmployee he)
{
System.Diagnostics.Debug.WriteLine("hourly");
// generate the line of the report.
}
public void Visit(SalariedEmployee se)
{
System.Diagnostics.Debug.WriteLine("fix");
} // do nothing
public void PrintReport()
{
var employees = new List<Employee>
{
new SalariedEmployee(),
new HourlyEmployee()
};
QtdHoursAndPayReport v = new QtdHoursAndPayReport();
foreach (var emp in employees)
{
emp.Accept(v);
}
}
}
class Program
{
public static void Main(string[] args)
{
new QtdHoursAndPayReport().PrintReport();
}
}
}