装饰图案中的装饰顺序
Order of decorations in Decorator Pattern
你们大多数人都知道装饰器模式的pizza/cofeee示例。
Pizza* pizza1 = BigPizzaDecorator(MushromDecorator(SimplePizza()));
Pizza* pizza2 = MushromDecorator(BigPizzaDecorator(SimplePizza()));
这两个对象的行为方式相似,但并不完全相同,特别是如果你有非交换操作,例如:
BigPizzaDecorator::price() { return 10 + PizzaDecorator::price(); } // this is commutative
BigPizzaDecorator::name() { return "big " + PizzaDecorator::name(); } // this is not commutative
因此,pizza1
和pizza2
的价格是相同的,但名称不同,例如第一个应该是"Big mushroom pizza"
,第二个"Mushroom big pizza"
。第一个是正确的英语(最好是"蘑菇大披萨",但它并不那么重要(。
《头优先》一书用Coffee的例子指出了这个问题:
当您需要查看装饰器链中的多个层时开始将装饰器推向超出其真正意图的地步。
然而,这样的事情是可能的。想象一个调味品PrettyPrint装饰师,解析最后的描述,并可以打印"Mocha,Whip,Mocha"作为"Whip,Double Mocha">
最好的方法是什么?(operator<
?(
我从未知道在使用装饰器时需要这种东西。我认为,如果你需要这样做,那么你就不应该使用装饰器,尤其是当你故意"把装饰器推到它的意图之外"时。
我尝试过这样做,代码如下。基本上,我在SimplePizza
对象周围创建了一个薄层,它了解decorator需要什么,然后decorator对其进行装饰。
这里的主要问题是,为了维护输出中的顺序,您必须维护装饰器之间的关系——这很快就会成为维护噩梦。
#include <iostream>
#include <queue>
#include <sstream>
struct name_part
{
std::string mName;
int mPriority;
name_part(const std::string& name, int priority)
: mName(name)
, mPriority(priority)
{
}
};
bool operator<(const name_part& a, const name_part& b)
{
return (a.mPriority < b.mPriority);
}
std::string priority_queueToString(const std::priority_queue<name_part>& orig)
{
std::ostringstream oss;
std::priority_queue<name_part> q(orig);
while (!q.empty())
{
oss << q.top().mName << " ";
q.pop();
}
return oss.str();
}
struct SimplePizza
{
virtual std::string name()
{
return "pizza";
}
};
struct SimplePizzaImplementer : SimplePizza
{
SimplePizza *mDecorated;
SimplePizzaImplementer()
: mDecorated(0)
{
}
SimplePizzaImplementer(SimplePizza *decorated)
: mDecorated(decorated)
{
}
virtual std::string name()
{
return priority_queueToString(nameParts());
}
virtual std::priority_queue<name_part> nameParts()
{
std::priority_queue<name_part> q;
if (mDecorated)
{
q.push(name_part(mDecorated->name(), 0));
}
return q;
}
};
struct MushroomDecorator : SimplePizzaImplementer
{
SimplePizzaImplementer *mDecorated;
MushroomDecorator(SimplePizzaImplementer *decorated)
: mDecorated(decorated)
{
}
virtual std::string name()
{
return priority_queueToString(nameParts());
}
virtual std::priority_queue<name_part> nameParts()
{
std::priority_queue<name_part> q = mDecorated->nameParts();
q.push(name_part("mushroom", 1));
return q;
}
};
struct BigDecorator : SimplePizzaImplementer
{
SimplePizzaImplementer *mDecorated;
BigDecorator(SimplePizzaImplementer *decorated)
: mDecorated(decorated)
{
}
virtual std::string name()
{
return priority_queueToString(nameParts());
}
virtual std::priority_queue<name_part> nameParts()
{
std::priority_queue<name_part> q = mDecorated->nameParts();
q.push(name_part("big", 2));
return q;
}
};
int main()
{
SimplePizzaImplementer *impl = new SimplePizzaImplementer(new SimplePizza());
SimplePizza *pizza1 = new MushroomDecorator(new BigDecorator(impl));
SimplePizza *pizza2 = new BigDecorator(new MushroomDecorator(impl));
std::cout << pizza1->name() << std::endl;
std::cout << pizza2->name() << std::endl;
}
关于将这些代码放在哪里,请使用重载运算符<lt;是可行的。
我觉得"把装饰师推到它的意图之外"在这里真的需要强调。
你真的会构建一个功能依赖于解析的严肃应用程序吗
"Mocha, Whip, Mocha"
以及制定
"Whip, Double Mocha"
从概念上讲,您是在从一个未出于此目的发布的接口推断语义。结果将是非常脆弱的,装饰器实现中的微小更改:"Yummy super mocha special"将破坏解析器。添加新的装饰器将需要未知级别的更改。
- CMake-按正确顺序将项目与C运行时对象文件链接
- 函数调用中参数的顺序重要吗
- 为什么不;名字在地图上是按顺序排列的吗
- 将Integer转换为4字节的unsined字符矢量(按大端字节顺序)
- 数到第n个楼梯的路(顺序无关紧要)
- 优先顺序:智能指针和类析构函数
- 在循环中按顺序遍历成员变量
- 独立读取-修改-写入顺序
- QML按钮点击功能执行顺序
- C++中数据类型修饰符的顺序
- 当比特(而不是字节)的顺序至关重要时的持久性
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 通过选项卡的文本设置QTabWidget顺序
- c++11评估顺序(未定义的行为)
- 如何在C++中递归地按相反顺序打印集合
- 给定顺序中的事件处理
- 具有包含其他对象的类的对象创建顺序
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- 遍历顺序由 std::文件系统directory_iterator给出
- 检查 2 棵树是否具有相同的顺序