设计模式(二)组合模式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";
}

最终所有的求值运算都会落到一个数值表达式的头上,输出的过程就是一个在树上自底向上输出最终汇总的过程。

这个例子可以说是组合模式的典型应用,据说(作者说)某个编译器开发团队用了两个半月的时间实现了表达式求值,被作者用十几行代码就这样实现了。

时间: 2024-12-09 18:32:42

设计模式(二)组合模式Composite(表达式求值)的相关文章

设计模式之组合模式(Composite)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式之组合模式---Composite Pattern

模式的定义 组合模式(Composite Pattern)定义如下: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性.

[设计模式] 7 组合模式Composite

DP书上给出的定义:将对象组合成树形结构以表示“部分-整体”的层次结构.组合使得用户对单个对象和组合对象的使用具有一致性.注意两个字“树形”.这种树形结构在现实生活中随处可见,比如一个集团公司,它有一个母公司,下设很多家子公司.不管是母公司还是子公司,都有各自直属的财务部.人力资源部.销售部等.对于母公司来说,不论是子公司,还是直属的财务部.人力资源部,都是它的部门.整个公司的部门拓扑图就是一个树形结构. 下面给出组合模式的UML图.从图中可以看到,FinanceDepartment.HRDep

【设计模式】—— 组合模式Composite

前言:[模式总览]——————————by xingoo 模式意图 使对象组合成树形的结构.使用户对单个对象和组合对象的使用具有一致性. 应用场景 1 表示对象的 部分-整体 层次结构 2 忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象. 模式结构 [安全的组合模式] 这种组合模式,叶子节点,也就是单个对象不具有对象的控制功能.仅仅有简单的业务操作. 1 package com.xingoo.composite.safe; 2 3 import java.util.ArrayLis

设计模式 笔记 组合模式 Composite

//---------------------------15/04/16---------------------------- //Composite 组合模式----对象结构型模式 /* 1:意图 将对象组合成树形结构以表示"部分-整体"的层次结构.Composite使得用户对单个对象和 组合对象的使用具有一致性. 2:动机: 3:适用性: 1>你想表示对象的部分-整体层次结构. 2>你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象. 4:

设计模式之组合模式 Composite

代码实现 //Component(抽象构件):抽象构件中定义了叶子和容器构件的共同点.比如,有公共的添加删除叶子功能,有显示节点功能. public abstract class Component { protected String name; public Component(String name) { super(); this.name = name; } public abstract void add(Component c); public abstract void remo

C#设计模式:组合模式(Composite Pattern)

一,C#设计模式:组合模式(Composite Pattern) using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _9.组合模式 { //组合模式主要用来处理一类具有"容器特征"的对象--即它们在充当对象的同时,又可以作为容器包含其他多个对象. //组合模式,将对象组合成树形结构以表示

C#设计模式(10)——组合模式(Composite Pattern)

一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,然而作为客户,它们希望能够始终一致地对待简单对象和复合对象.然而组合模式就是解决这样的问题.下面让我们看看组合模式是怎样解决这个问题的. 二.组合模式的详细介绍 2.1 组合模式的定义 组合模式

设计模式 - 组合模式(composite pattern) 详解

组合模式(composite pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 组合模式: 允许你将对象组合成树形结构来表现"整体/部分"层次结构. 组合能让客户以一致的方法处理个别对象以及组合对象. 建立组件类(Component), 组合类(composite)和叶子类(leaf)继承组件类, 客户类(client)直接调用最顶层的组合类(composite)即可. 具体方法: 1. 组件类(component), 包含组合