首页 > 代码库 > 设计模式(二)组合模式Composite(表达式求值)
设计模式(二)组合模式Composite(表达式求值)
组合模式目标:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。
透露一下:这个例子可以说是组合模式的典型应用,据说(作者说)某个编译器开发团队用了两个半月的时间实现了表达式求值,被作者用十几行代码就这样实现了。
需求:表达式求值,是编译器的重要组件,本例你能找到的实际代码应该不多,因为是本人根据《C++沉思录》里面的例子亲自敲出来的(当然都是作者的功劳)。目的在于支持各种一元运算,二元运算甚至更多的运算都加入到表达式求值中,程序方便扩展,使用简单。
代码展示:说实话这个例子还是不太好理解的,尤其对初学者,因为比较抽象,但是如果先给出他怎么使用这个结果,就比较容易看清楚为什么要这么组织了。每次过段时间就会忘记作者怎么实现的,所以这里集中展示,方便以后回忆。
首先是main函数
从main函数你可以看出作者打算创造一个什么样的东西,这个东西至少要完成哪些事情,之后你可以猜测要完成这件事你要做哪些准备
#include "ExpressNode.h" #include "ValueNode.h" #include "UnaryNode.h" #include "BinaryNode.h" #include "Express.h" void main() { Express t=Express(3); t=Express('+',t,12); cout<<t<<" = "<<t.eval()<<endl; Express y=Express('-',4); cout<<y<<" = "<<y.eval()<<endl; Express t1=Express('*',Express('-',5),Express('+',3,4)); cout<<t1<<" = "<<t1.eval()<<endl; t=Express('*',t1,t1); Express t2=Express('*',t,t); cout<<t2<<" = "<<t2.eval()<<endl; }
运行结果:
看到了吧,上图就是表达式求值,看起来是不是很方便,这个东西可以打印你的表达式,还可以直接求出表达式的值,这个自然的需求蕴含着一个事实:表达式有各种各样的,一元操作符,二元操作符,单个数值也是表达式,所有类型的表达式都支持输出,都支持计算结果。当然还可以添加三元操作符表达式。
这样一来就至少有一个表达式类是客户第一要接触的:Express,这个类的接口如你想象应该是下面的样子
Express类
Express.h
#pragma once #include "ExpressNode.h" #include <iostream> using namespace std; class Express { public: Express(int);//ValueNode(int) Express(3) Express(char,const Express);//UnaryNode(char,int) Express('+',t,12) Express(char,const Express,const Express);//BinaryNode(char,int,int) Express('+',3,4) Express(const Express&); Express& operator=(const Express&); ~Express(void); friend ostream& operator<<(ostream& os, const Express& e) { os<<*(e.p); return os; } int eval() const; private: class ExpressNode* p;//具体的功能由这个类实现,这个类派生了各种各样的表达式 };
Express.cpp
#include "Express.h" #include "ValueNode.h" #include "UnaryNode.h" #include "BinaryNode.h" Express::Express(int a) { p=new ValueNode(a); } Express::Express(char c, const Express e) { p=new UnaryNode(c,e); } Express::Express(char c,const Express el,const Express er)//BinaryNode(char,int,int) { p=new BinaryNode(c,el,er); } Express::Express(const Express& e1) { p=e1.p; p->setUse(p->getUse()+1); } Express& Express::operator=(const Express& e1) { (e1.p)->setUse((e1.p)->getUse()+1); p->setUse(p->getUse()-1); if(p->getUse()==0) delete p; p=e1.p; return *this; } Express::~Express(void) { p->setUse(p->getUse()-1); if(p->getUse()==0) delete p; } int Express::eval() const { return p->eval(); }
从Express的接口可以看出,Express创建对象的时候交给了具体的表达式类,而用基类指针实现多态,来达到统一计算,统一输出表达式的目的。
下面就是各种表达式的基类:ExpressNode,这个类是所有表达式的一般形式,这个类的接口要求各种表达式都要实现。
ExpressNode.h
#pragma once #include<iostream> using namespace std; class ExpressNode { public: friend class Express; int getUse(void) const; void setUse(int); friend ostream& operator<<(ostream& os,const ExpressNode& ExprNode)//(1)输出表达式自身 { ExprNode.print(os); return os; } ExpressNode(void):use(1){} virtual ~ExpressNode(void); protected: virtual void print(ostream& os) const=0; virtual int eval() const=0;//(2)计算表达式的值 private: int use; };
ExpressNode.cpp
#include "ExpressNode.h" ExpressNode::~ExpressNode(void) { } int ExpressNode::getUse() const { return use; } void ExpressNode::setUse(int use1) { use=use1; }这个类看起来什么都没做,只是提供一个一致的界面让子类去实现,唯一的一个整形变量来保存引用计数,使得表达式不会大量复制拷贝
下面就是各种表达式子类的实现了:数值表达式ValueNode(表示数值常量)、一元表达式UnaryNode(正、负数运算)、二元表达式BinaryNode(两个表达式相运算+ - * /加、减、乘、除)
ValueNode.h
#pragma once #include "ExpressNode.h" #include "Express.h" class ValueNode : public ExpressNode { public: friend class Express; ValueNode(void); ValueNode(int value1); ~ValueNode(void); private: void print(ostream& os) const; int eval() const {return value;} int value; };
对数值表达式求值就是自己保存的值
ValueNode.cpp
#include "ValueNode.h" ValueNode::ValueNode(void) { } ValueNode::ValueNode(int value1):value(value1) { } ValueNode::~ValueNode(void) { } void ValueNode::print(std::ostream& os) const { os<<value; }输出一个数值表达式就是输出自己保存的值
UnaryNode.h
#pragma once #include "Express.h" #include "ExpressNode.h" class UnaryNode : public ExpressNode { public: friend class Express; UnaryNode(void); UnaryNode(char c,class Express left1); ~UnaryNode(void); private: void print(ostream& os) const; int eval() const ; char opend; class Express left; };从这里可以看出,正负运算的基础是一个一般意义上的表达式,任何复杂的表达式都可以加上正负运算。
UnaryNode.cpp
#include "Express.h" #include "UnaryNode.h" UnaryNode::UnaryNode(char c,class Express left1):opend(c),left(left1) { } UnaryNode::~UnaryNode(void) { } void UnaryNode::print(std::ostream &os) const { os<<"("<<opend<<left<<")"; } int UnaryNode::eval() const { if(opend=='-') return (-1)*left.eval(); throw "error, bad op int UnaryNode"; }
BinaryNode.h
#pragma once #include "ExpressNode.h" #include "Express.h" class BinaryNode : public ExpressNode { public: friend class Express; BinaryNode(void); BinaryNode(char,class Express,class Express); ~BinaryNode(void); private: void print(ostream&) const; int eval() const; char opend; class Express left; class Express right; };从这里可以看出,二元表达式就是一个操作符,两个一般意义上的表达式,具体的工作还是往下分派,自己只做操作符要做的事。输出也是往下委派。
BinaryNode.cpp
#include "BinaryNode.h" BinaryNode::BinaryNode(char c,class Express left1,class Express right1) :opend(c),left(left1),right(right1) { } BinaryNode::~BinaryNode(void) { } void BinaryNode::print(ostream& os) const { os<<"("<<left<<opend<<right<<")"; } int BinaryNode::eval() const { int op1=left.eval(); int op2=right.eval(); if(opend=='+') return op1+op2; if(opend=='-') return op1-op2; if(opend=='*') return op1*op2; if(opend=='/'&& op2!=0) return op1/op2; throw "error, bad operation in BinaryNode"; }
最终所有的求值运算都会落到一个数值表达式的头上,输出的过程就是一个在树上自底向上输出最终汇总的过程。
这个例子可以说是组合模式的典型应用,据说(作者说)某个编译器开发团队用了两个半月的时间实现了表达式求值,被作者用十几行代码就这样实现了。
设计模式(二)组合模式Composite(表达式求值)