1. Visitor

1.1. Intent

object structure 의 element들에 수행될 operation 을 표현한다. Visitor는 해당 operation이 수행되는 element의 class에 대한 변화 없이 새로운 operation을 정의할 수 있도록 해준다.

1.2. Motivation

컴파일러가 abstact syntax tree로 프로그램을 표현한다고 하자. 컴파일러는 모든 변수들이 정의가 되어있는 지를 검사하는 것과 같은 '정적인 의미' 분석을 위해 abstract syntax tree에 대해 operation을 수행할 필요가 있을 것이다. 컴파일러는 또한 code 변환을 할 필요가 있다. 또한 컴파일러는 type-checking, code optimization, flow analysis 와 해당 변수가 이용되기 전 선언되었는지 등의 여부를 검사하기 위해서 해당 operations들을 수행할 필요가 있다. 더 나아가 우리는 pretty-printing, program restructuring, code instrumentation, 그리고 프로그램의 다양한 기준들에 대한 계산을 하기 위해 abstract syntax tree를 이용할 것이다.

이러한 operations들의 대부분들은 variable들이나 arithmetic expression들을 표현하는 node들과 다르게 assignment statement들을 표현하는 node를 취급할 필요가 있다. 따라서, 각각 assignment statement 를 위한 클래스와, variable 에 접근 하기 위한 클래스, arithmetic expression을 위한 클래스들이 있어야 할 것이다. 이러한 node class들은 컴파일 될 언어에 의존적이며, 또한 주어진 언어를 위해 바뀌지 않는다.


이 다이어그램은 Node class 계층구조의 일부분을 보여준다. 여기서의 문제는 다양한 node class들에 있는 이러한 operation들의 분산은 시스템으로 하여금 이해하기 어렵고, 유지하거나 코드를 바꾸기 힘들게 한다. Node 에 type-checking 코드가 pretty-printing code나 flow analysis code들과 섞여 있는 것은 혼란스럽다. 게다가 새로운 operation을 추가하기 위해서는 일반적으로 이 클래스들을 재컴파일해야 한다. 만일 각각의 새 operation이 독립적으로 추가될 수 있고, 이 node class들이 operation들에 대해 독립적이라면 더욱 좋을 것이다.

우리는 각각의 클래스들로부터 관련된 operation들을 패키징화 하고, traverse 될 (tree 의 각 node들을 이동) abstract syntax tree의 element들에게 인자로 넘겨줄 수 있다. 이를 visitor라고 한다. element가 visitor를 'accepts' 할때 element는 element의 클래스를 인코딩할 visitor에게 request를 보낸다. 이 request 또한 해당 element를 인자로 포함하고 있다. 그러면 visitor는 해당 element에 대한 operation을 수행할 것이다.

예를든다면, visitor를 이용하지 않는 컴파일러는 컴파일러의 abstact syntax tree의 TypeCheck operation을 호출함으로서 type-check 을 수행할 것이다. 각각의 node들은 node들이 가지고 있는 TypeCheck를 호출함으로써 TypeCheck를 구현할 것이다. (앞의 class diagram 참조). 만일 visitor를 이용한다면, TypeCheckingVisior 객체를 만든 뒤, TypeCheckingVisitor 객체를 인자로 넘겨주면서 abstract syntax tree의 Accept operation을 호출할 것이다. 각각의 node들은 visitor를 도로 호출함으로써 Accept를 구현할 것이다 (예를 들어, assignment node의 경우 visitor의 VisitAssignment operation을 호출할 것이고, varible reference는 VisitVaribleReference를 호출할 것이다.) AssignmentNode 클래스의 TypeCheck operation은 이제 TypeCheckingVisitorVisitAssignment operation으로 대체될 것이다.

type-checking 의 기능을 넘어 일반적인 visitor를 만들기 위해서는 abstract syntax tree의 모든 visitor들을 위한 abstract parent class인 NodeVisitor가 필요하다. NodeVisitor는 각 node class들에 있는 operation들을 정의해야 한다. 해당 프로그램의 기준 등을 계산하기 원하는 application은 node class 에 application-specific한 코드를 추가할 필요 없이, 그냥 NodeVisitor에 대한 새로운 subclass를 정의하면 된다. VisitorPattern은 해당 Visitor 와 연관된 부분에서 컴파일된 구문들을 위한 operation들을 캡슐화한다.



VisitorPattern으로, 개발자는 두개의 클래스 계층을 정의한다. 하나는 operation이 수행될 element에 대한 계층이고 (Node hierarchy), 하나는 element에 대한 operation들을 정의하는 visitor들이다. (NodeVisitor hierarchy). 개발자는 visitor hierarchy 에 새로운 subclass를 추가함으로서 새 operation을 만들 수 있다.

1.3. Applicability

VisitorPattern은 다음과 같은경우에 이용한다.


1.4. Structure

1.5. Participants

  • Visitor (NodeVisitor)
    - declares a Visit operations for each class of ConcreteElement in the object structure. The operation's name and signature identifies the class that sends the Visit request to the visitor. That lets the visitor determine the concrete class of the element being visited. Then the visitor can access the element directly through its particular interface.
  • ConcreteVisitor (TypeCheckingVisitor)
    - implements each operation declared by Visitor. Each operation implements a fragment of the algorithm defined for the corresponding class of object in the structure. ConcreteVisitor provides the context for the algorithm and stores its local state. This state often accumulates result during the traversal of the structure.
  • Element (Node)
    - defines an Accept operation that takes a visitor as an argument.
  • ConcreteElement (AssignmentNode, VariableRefNode)
    - implements an Accept operation that takes a visitor as an argument.
  • ObjectStructure (Program)
    - can enumerate its elements.

    - may provide a high-level interface to allow the visitor to visit its elements.

    - may either be a composite (See CompositePattern) or a collection such as a list or a set.


1.6. Collaborations

1.7. Consequences

template <class Item>
class Iterator {
	// ...
	Item CurrentItem () const;
};

class Visitor {
public:
	// ...
	void VisitMyType (MyType*);
	void VisitYourType (YourType*);
};

1.8. Implementation

class Visitor {
public:
	virtual void VisitElementA (ElementA*);
	virtual void VisitElementB (ElementB*);

	// and so on for other concrete elements
protected:
	Visitor ();
};

class Element {
public:
	virtual ~Element ();
	virtual void Accept (Visitor&) = 0;
protected:
	Element ();
};

class ElementA : public Element {
public:
	ElementA ();
	virtual void Accept (Visitor& v) { v.VisitElementA (this); }
};

class ElementB : public Element {
public:
	ElementB ();
	virtual void Accept (Visitor& v) { v.VisitElementB (this); }
};

class CompositeElement : public Element {
public:
	virtual void Accept (Visitor&);
private:
	List<Element*>* _children;
};

void CompositeElement::Accept (Visitor& v) {
	ListIterator<Element*> i (_children);

	for (i.First (); !i.IsDone(); i.Next ()) {
		i.CurrentItem ()->Accept (v);
	}
	v.VisitCompositeElement (this);
}

1.9. Sample Code


class Equipment {
public:
	virtual ~Equipment ();

	const char* Name () { return _name; } 
	
	virtual Watt Power ();
	virtual Currency NetPrice ();
	virtual Currency DiscountPrice ();

	virtual void Accept (EquipmentVisitor&);

protected:
	Equipment (const char*);
private:
	const char* _name;
};

class EquipmentVisitor {
public:
	virtual ~EquipmentVisitor ();

	virtual void VisitFloppyDisk (FloppyDisk*);
	virtual void VisitCard (Card*);
	virtual void VisitrChassis (Chassis*);
	virtual void VisitBus (Bus*);

	// and so on for other concrete subclasses of Equipment
protected:
	EquipmentVisitor ();
};

void FloppyDisk::Accept (EquipmentVisitor& visitor) {
	visitor.VisitFloppyDisk (this);
}

void Chassis::Accept (EquipmentVisitor& visitor) {
	for (ListIterator<Equipment*> i(_parts); !i.IsDone (); i.Next ()) {
		i.CurrentItem ()->Accept (visitor);
	}
	visitor.VisitChassis (this);
}

class PricingVisitor : public EquipmentVisitor {
public:
	PricingVisitor ();

	Currency& GetTotalPrice ();

	virtual void VisitFloppyDisk (FloppyDisk*);
	virtual void VisitCard (Card*);
	virtual void VisitChassis (Chassis*);
	virtual void VisitBus (Bus*);
	// ...
private:
	Currency _total;
};

void PricingVisitor::VisitFloppyDisk (FloppyDisk* e) {
	_total += e->NextPrice ();
}

void PricingVisitor::VisitChassis (Chassis* e) {
	_total += e->DiscountPrice ();
}

class InventoryVisitoy : public EquipmentVisitor {
public:
	InventoryVisitor ();

	Inventory& GetInventory ();

	virtual void VisitFloppyDisk (FloppyDisk*);
	virtual void VisitCard (Card*);
	virtual void VisitChassis (Chassis*);
	virtual void VisitBus (Bus*);
	// ...

private:
	Inventory _inventory;
};


void InventoryVisitor::VisitFloppyDisk (FloppyDisk* e) {
	_inventory.Accumulate (e);
}

void InventoryVisitor::VisitChassis (Chassis* e) {
	_inventory.Accumulate (e);
}

Equipment* component;
InventoryVisitor visitor;

component->Accept (visitor);
cout << "Inventory "
	<< componet->Name ()
	<< visitor.GetInventory ();


accept: aVisitor
	^ aVisitor visitSequence : self

visitSequence: sequenceExp
	inputState := sequenceExp expression1 accept: self.
	^ sequenceExp expression2 accept: self.

visitRepeat : repeatExp
	| finalState |
	finalState := inputState copy.
	[inputState isEmpty]
		whileFalse:
		[inputState := repeatExp repetition accept: self.
		finalState addAll: inputState].
	^ finalState

visitAlternation: alternateExp
	| finalState originalState |
	originalState := inputState.
	finalState := alternateExp alternative1 accept: self.
	inputState := originalState.
	finalState addAll: (alternateExp alternative2 accept: self).
	^ finalState

visitLiteral: literalExp
	| finalState tStream |
	finalState := Set new.
	inputState
		do:
			[:stream | tStream := stream copy.
				(tStream nextAvailable:
					literalExp components size
				) = literalExp components
					ifTrue: [finalState add: tStream]
			].
	^ finalState

1.10. Known Uses


1.11. Related Patterns

Retrieved from http://wiki.zeropage.org/wiki.php/Gof/Visitor
last modified 2021-02-07 05:23:19