1、意图
Represent an operation to be performed on the elements of an objectstructure. Visitor lets you define a new operation without changing theclasses of the elements on which it operates.
2、适应性
Use the Visitor pattern when
• an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes.
• many distinct and unrelated operations need to be performed on objectsin an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operationstogether by defining them in one class. When the object structure isshared by many applications, use Visitor to put operations in just hose applications thatneed them.
• the classes defining the object structure rarely change, but you oftenwant to define new operations over the structure. Changing the objectstructure classes requires redefining the interface to all visitors,which is potentially costly. If the object structure classes changeoften, then it's probably better to define the operations in those classes.
3、结构
Here again is the Equipment class from Composite (183). We've augmented it with anAccept operation to let it work with a visitor.
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;
};
The abstract class for all visitors of equipment has a virtualfunction for each subclass of equipment, as shown next. All of thevirtual functions do nothing by default.
class EquipmentVisitor {
public:
virtual ~EquipmentVisitor();
virtual void VisitFloppyDisk(FloppyDisk*);
virtual void VisitCard(Card*);
virtual void VisitChassis(Chassis*);
virtual void VisitBus(Bus*);
// and so on for other concrete subclasses of Equipment
protected:
EquipmentVisitor();
};
Equipment subclasses define Accept inbasically the same way: It calls theEquipmentVisitor operation that corresponds to the classthat received the Accept request, like this:
void FloppyDisk::Accept (EquipmentVisitor& visitor) {
visitor.VisitFloppyDisk(this);
}
// Chassis::Accept could traverseall the parts in the chassis as follows:
void Chassis::Accept (EquipmentVisitor& visitor) {
for (
ListIterator i(_parts);
!i.IsDone();
i.Next()
) {
i.CurrentItem()->Accept(visitor);
}
visitor.VisitChassis(this);
}
Subclasses of EquipmentVisitor define particular algorithmsover the equipment structure.
// PricingVisitor will compute the total cost of all nodes in theequipment structure.
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->NetPrice();
}
void PricingVisitor::VisitChassis (Chassis* e) {
_total += e->DiscountPrice();
}
// The InventoryVisitor accumulates the totals for each type ofequipment in the object structure.
class InventoryVisitor : 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);
}
Here's how we can use an InventoryVisitor on anequipment structure:
Equipment* component;
InventoryVisitor visitor;
component->Accept(visitor);
cout << "Inventory "
<< component->Name()
<< visitor.GetInventory();