설명
여러 요소들을 하나씩 방문해서 계산해야 하는데 계산이 복잡한 경우 하나로 뺌.
이때 Accept 과 Visit 의 함수의 관계가 중요한데
Visit 은 각 Element 종류별로 인자를 갖는 함수를 만들고,
Accept 은 인자로 받은 Visitor 의 Visit 함수를 자신을 인자로 넣어 호출함으로서
필요한 요소들을 Visit 에 넣는 것임.
이때 Accept 함수에 클래스 계층이 다른 경우도 된다는 점에 주목.
Iterator 패턴과 다른 점이 바로 이것임.
자료형의 완전히 달라도 Visitor 패턴을 사용할 수 있음.
문제는 자료형을 일일히 Visitor 에 등록해야하기 때문에
새로운 Element 클래스를 추가하는게 어려움.
또한 Element 의 내부 자료가 Visitor 에게 노출이 되야하기 때문에 캡슐화 원칙을 깸
예제 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | typedef float Currency; // Leaf Node Interface class Equipment { public: virtual ~Equipment() = default; const char* Name() { return _name; } virtual Currency NetPrice() = 0; virtual void Accept(class EquipmentVisitor* visitor) = 0; protected: Equipment(const char* c) : _name(c) {} private: const char* _name; }; // LeafNode 구현 class FloppyDisk : public Equipment { public: FloppyDisk(const char* c) : Equipment(c) {} virtual ~FloppyDisk() = default; virtual Currency NetPrice() override { return m_price; } virtual void Accept(class EquipmentVisitor* visitor) override; private: Currency m_price = 10; }; // Composite Interface class CompositeEquipment : public Equipment { public: virtual ~CompositeEquipment() = default; virtual Currency NetPrice() = 0; virtual void AddComponent(class Equipment* ptr){ _equipments.push_back(ptr); } protected: CompositeEquipment(const char* c) : Equipment(c) {} std::vector<Equipment*> _equipments; }; // Composite 구현 class Chassis : public CompositeEquipment { public: Chassis(const char* c) : CompositeEquipment(c) {} virtual ~Chassis() = default; virtual Currency NetPrice(); virtual void Accept(class EquipmentVisitor* visitor) override; virtual Currency GetPriceAlone() { return m_price; } private: Currency m_price = 5; }; | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class EquipmentVisitor { public: virtual ~EquipmentVisitor() = default; virtual void VisitFloppyDisk(FloppyDisk*) = 0; virtual void VisitChassis(Chassis*) = 0; protected: EquipmentVisitor() = default; }; class PricingVisitor : public EquipmentVisitor { public: PricingVisitor() = default; Currency& GetTotalPrice() { return _total; } virtual void VisitFloppyDisk(FloppyDisk*); virtual void VisitChassis(Chassis*); private: Currency _total = 0; }; | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include "Visitor.h" void Chassis::Accept(EquipmentVisitor* visitor) { for (auto& e : _equipments) e->Accept(visitor); } void FloppyDisk::Accept(EquipmentVisitor* visitor) { visitor->VisitFloppyDisk(this); } void PricingVisitor::VisitFloppyDisk(FloppyDisk* v) { _total += v->NetPrice() * 123; } void PricingVisitor::VisitChassis(Chassis* v) { _total += v->NetPrice() * 3.23f; } Currency Chassis::NetPrice() { PricingVisitor visitor; Accept(&visitor); return visitor.GetTotalPrice(); } | cs |
1 2 3 4 5 6 7 8 9 10 | void main() { Chassis* c = new Chassis("sadf"); c->AddComponent(new FloppyDisk("disk1")); c->AddComponent(new FloppyDisk("disk2")); c->AddComponent(new FloppyDisk("disk3")); c->AddComponent(new FloppyDisk("disk4")); std::cout << c->NetPrice() << std::endl; } | cs |
CompositEquipment 인 Chassis 는 Equipment 인 FloppyDisk 로 이루어짐.
Chassis 의 가격을 계산하려면 따라서 FloppyDisk 의 가격도 모두 계산해야함.
그럼 Chassis 의 NetPrice() 함수를 보면 자기를 이루는 요소들을 모두 Accept(() 을 호출함.
그렇게 Accept 이 호출이 되면 그때마다 Visitor 가 Price 를 계산함.
그렇게 Visitor 내에서 계산이 끝나면
그 값을 들고와 return 하는 구조로 NetPrice() 가 되어 있음.
이렇게 Visitor 라는 새로운 클래스로 알고리즘 책임을 양도하는 패턴임.
추가 설명
Composite 패턴과 함께 쓰이는 경우가 많음.
댓글 없음:
댓글 쓰기