设计模式【八】:组合模式
伪代码
伪代码引自Erich Gamma 等《Design Patterns》.
class Equipment{
public:
virtual ~Equipment();
const char* Name(){return _name;}
virtual Watt Power();
virtual Currency NetPrice();
virtual Currency DiscountPrice();
virtual void Add(Equipment*);
virtual void Remove(Equipment*);
virtual Iterator<Equipment*>* CreateIterator();
protected:
Equipment(const char*);
private:
const char* _name;
};
class FloppyDisk: public Equiment{
public:
FloppyDisk(const char*);
virtual ~FloppyDisk();
virtual Watt Power();
virtual Currency NetPrice();
virtual Currency DiscountPrice();
};
class CompositeEquipment: public Equipment{
public:
virtual ~CompositeEquipment();
virtual Watt Power();
virtual Currency NetPrice();
virtual Currency DiscountPrice();
virtual void Add(Equipment*);
virtual void Remove(Equipment*);
virtual Iterator<Equipment*>* CreateIterator();
protected:
CompositeEquipment(const char*);
private:
List<Equipment*> _equipment;
};
Currency CompositeEquipment::NetPrice(){
Iterator<Equipment*>* i = CreateIterator();
Currency total = 0;
for(i->First();!i->IsDone();i->Next()){
total += i->CurrentItem->NetPrice();
}
delete i;
return total;
}
class Chassis:public CompositeEquipment{
};
class Cabinet:public CompositeEquipment{
};
//...
Cabinet* cabinet = new Cabinet("PC Cabinet");
Chassis* chassis = new Chassis("PC Chassis");
cabinet->Add(chassis);
Bus* bus = new Bus("Bus");
bus->Add(new Card("16mb card"));
chassis->Add(bus);
chassis->Add(new FloppyDisk("3.5in disk"));
cout<<"The total price is "<<chassis->NetPrice()<<endl;
优缺点
优点
- 定义元对象和组合对象,并使对象可以递归地被组织起来。
- 简化使用者的操作。使用者可以对组合对象和元对象一致对待。比如伪代码中计算总价格的函数在组合对象内部递归实现。
- 易于添加组合对象的组件。
缺点
- 增大对组合对象的管理难度。因为在组合模式下对象的组件可以动态增删,所以不应依赖类名来判断组合对象的具体构成,确定对象具体构成需要在运行时动态检查。
何时使用
- 需要组织组合对象(往往是树形结构)。
- 需要使用者忽略组合对象与元对象的区别。
实现时技巧
- 可构造子组件对父组件的隐式引用,比如在子组件的构造函数中添加一个指向父组件的指针。这样可以方便在对象树上的遍历。
- 慎用共享的子组件。复用组件当然可以简化程序,但这会导致子组件可能有多个父组件。如果在子组件内维护一个指针链表可以管理这种情况,但这样反而使程序复杂。
- 对象接口应当设计为各组件的并集。我们可以将叶子组件(元组件)视为不会返回子组件的一般组件,这样有助于简化接口设计。当然,这是灵活性与安全性的trade off,如果我们在叶子组件里声明全部的操作(包括对其子组件的操作,只不过返回null),其实是牺牲安全性来换取灵活性。
- 可能需要对子组件排序。
- 如果需要频繁遍历组件树,可缓存最近遍历的若干子组件等等。
- 如果实现语言没有垃圾回收机制,需要考虑释放内存。一般父组件delete时需要delete它所有的子组件。
- 总之,由于其树型组织结构,其注意事项有很多是与数据结构课上学的树有所对应。