本文是学习刘伟技术博客和《设计模式-可复用面向对象软件的基础》笔记,博客链接:http://blog.csdn.net/lovelion/article/details/17517213
主要是对博客和书本做提炼和记录,更多是对设计模式的基础框架学习,细节将略去,侧重对每个设计模式框架的理解。
我应该理解和掌握的:
1)能够画出这个设计模式的架构框图;
2)能够根据架构框图写出对应的伪代码;
3)这个模式的应用场景,主要优缺点。
1.解释器模式
假设我们有一个可以通过输入规定格式文本去控制的机器人,比如输入:down run 10 and left move 20,机器人便执行向下快速移动10个单位再向左移动20个单位;那么如何让机器人正确的识别这条指令呢,这便是我们要讨论的解释器模式。
进而继续讨论“down run 10 and left move 20”这条语句,用形式化的语言表示该语言的简单语法如下:
expressioin::=direction action distance | composite //表达式,一个完整的句子,非终结符,可以继续拆分
composite::= expression 'and' expression //符合表达式
direction::= 'up' | 'down' | 'left' | 'right' //移动方向,终结符,是语言的最小组成单位,不能进行拆分
action::= 'move' | 'run' //移动方式
distance::= an interger //移动距离
根据这写规则,我们可以组成很多不同的语句,只要遵守语法,就能够被我们的解释器翻译。
(1)定义
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
1)解释器模式结构图
2)参与者
a) AbstractExpression(抽象表达式):声明一个抽象的解释操作,它是所有终结表达式和非终结表达式的公共父类。
b) TerminalExpression(终结符表达式):实现文法中终结符相关联的解释操作;在句子中的每一个终结符都是该类的一个实例。
c) NonterminalExpression(非终结符表达式):非终结表达式符也是抽象表达式的子类,它实现文法中非终结表达式符的解释操作。它一般包括非终结符和终结符,因此一般通过递归的方式来完成解释操作。
d) Context(环境类,上下文):它存储解释器外的一些全局信息,通常它临时存储了需要解释的语句。
e)每一种终结符和非终结符都有一个具体类与之对应,正因为使用类来表示每一条文法规则,所以系统具有较好的灵活性和扩张性。
3)抽象语法树
在解释器模式中还可以通过一种称之为抽象语法树的图形来直观的表示每一句语法文本的构成,每个文法规则的语言实例都可以表示为一个抽象语法树。在图中,终结符作为树的叶子节点,而非终结符表达式的实例作为非叶子节点。
down run 10 and left move 20 对应的抽象语法树如下:
这个实例的基本构造图如下:
4)看图写代码
#include<iostream>
#include<vector>
using namespace std;
#define MAX_SIZE 256
#define SAFE_DELETE(p) if(p){delete p; p = NULL;}
const char* const DOWN = "down";
const char* const UP = "up";
const char* const LEFT = "left";
const char* const RIGHT = "right";
const char *const MOVE = "move";
const char *const WALK = "walk";
//抽象表达式,所有终结表达式和非终结表达式的公共父类
class AbstractNode
{
public:
virtual char *interpret() = 0;
};
//and非终结表达式
class AndNode : public AbstractNode
{
public:
AndNode(AbstractNode *left, AbstractNode *right):p_left(left),p_right(right){}
char* interpret()
{
char* p_result = new char[MAX_SIZE];
memset(p_result, 0, MAX_SIZE * sizeof(char));
char* left = p_left->interpret();
char* right = p_right->interpret();
strcat(p_result, left);
strcat(p_result, right);
SAFE_DELETE(left);
SAFE_DELETE(right);
return p_result;
}
private:
//and左右的两个表达式
AbstractNode* p_left;
AbstractNode* p_right;
};
//非终结表达式
class SentenceNode : public AbstractNode
{
public:
SentenceNode(AbstractNode* d, AbstractNode* a, AbstractNode* dis):p_direction(d),p_action(a),p_distance(dis){}
char* interpret()
{
char* p_result = new char[MAX_SIZE];
memset(p_result, 0, MAX_SIZE * sizeof(char));
char *p_direction_result = p_direction->interpret();
char *p_distance_result = p_distance->interpret();
char *p_action_result = p_action->interpret();
strcat(p_result, p_direction_result);
strcat(p_result, p_action_result);
strcat(p_result, p_distance_result);
SAFE_DELETE(p_distance_result);
SAFE_DELETE(p_direction_result);
SAFE_DELETE(p_action_result);
return p_result;
}
private:
//包含三个终结表达式的引用
AbstractNode* p_direction;
AbstractNode* p_action;
AbstractNode* p_distance;
};
//方向终结表达式
class DirectionNode : public AbstractNode
{
public:
DirectionNode(char* p):p_direction(p){}
char* interpret()
{
char* p_result = new char[MAX_SIZE];
memset(p_result, 0 , MAX_SIZE * sizeof(char));
if(!strcmp(p_direction, DOWN))
{
strcat(p_result, "向下");
}
else if(!strcmp(p_direction, UP))
{
strcat(p_result, "向上");
}
else if(!strcmp(p_direction, LEFT))
{
strcat(p_result, "向左");
}
else if(!strcmp(p_direction, RIGHT))
{
strcat(p_result, "向右");
}
else
{
strcat(p_result, "无效指令");
}
return p_result;
}
private:
char* p_direction;
};
//行动终结表达式
class ActionNode : public AbstractNode
{
public:
ActionNode(char* p) : p_action(p){}
char* interpret()
{
char* p_result = new char[MAX_SIZE];
memset(p_result, 0, MAX_SIZE * sizeof(char));
if(!strcmp(p_action, MOVE))
{
strcat(p_result, "移动");
}
else if(!strcmp(p_action, WALK))
{
strcat(p_result, "走动");
}
else
{
strcat(p_result, "无效指令");
}
return p_result;
}
private:
char* p_action;
};
//距离终结表达式
class DistanceNode : public AbstractNode
{
public:
DistanceNode(char* p):p_distance(p){}
char* interpret()
{
char* p_result = new char[MAX_SIZE];
memset(p_result, 0, MAX_SIZE*sizeof(char));
strcat(p_result, p_distance);
SAFE_DELETE(p_distance);
return p_result;
}
private:
char* p_distance;
};
//输入与输出处理
class InstructionHandler
{
public:
InstructionHandler(const char* in):p_instruction(in){}
void handle();
void output();
private:
void split(char **&instruction, int &size);
const char* p_instruction;//p_instruction is a pointer to const char;
AbstractNode *m_pTree;
};
void InstructionHandler::handle()
{
AbstractNode* l;
AbstractNode* r;
AbstractNode* d;
AbstractNode* a;
AbstractNode* dis;
vector<AbstractNode*> node;
char** insArray = NULL;
int size;
split(insArray, size);
for(int i= 0; i < size; i++)
{
if(!strcmp(insArray[i], "and"))
{
char* pd = insArray[++i];
d = new DirectionNode(pd);
char* pa = insArray[++i];
a = new ActionNode(pa);
char* pdis = insArray[++i];
dis = new DistanceNode(pdis);
r = new SentenceNode(d, a, dis);
node.push_back(new AndNode(l, r));
}
else
{
char *pDirectionStr = insArray[i];
d = new DirectionNode(pDirectionStr);
char *pActionStr = insArray[++i];
a = new ActionNode(pActionStr);
char *pDistanceStr = insArray[++i];
dis = new DistanceNode(pDistanceStr);
l = new SentenceNode(d, a, dis);
//node.push_back(l);
}
}
m_pTree = node[node.size() - 1];
}
void InstructionHandler::output()
{
char* p_result = m_pTree->interpret();
cout<<p_result<<endl;
SAFE_DELETE(p_result);
}
void InstructionHandler::split(char **&instruction, int &size)
{
instruction = new char*[10];
memset(instruction, 0, 10*sizeof(char));
for(int i = 0; i < 10; i++)
{
instruction[i] = new char[10];
memset(instruction[i], 0, 10 * sizeof(char));
}
size = 0;
int n = 0;
while(*p_instruction != '\0')
{
if(*p_instruction == ' ')
{
size++;
p_instruction++;
n = 0;
continue;
}
instruction[size][n++] = *p_instruction++;
}
size++;
}
(2)总结
1)优点
a) 易于改变和扩展文法。该模式使用类来表示文法,你可以使用继承来改变或扩展该文法。
b) 也易于实现文法。抽象语法树中节点的类实现方式大体类似,这些类易于直接编写。
c) 增加新的表达式较为方便,无需更改原有代码,符合开闭。
2)缺点
a) 对于复杂的文法难以维护。
b) 执行效率低。由于解释器模式中存在大量的循环和递归调用,因此在解释较为复杂的句子时,其速度很慢,代码调试过程也麻烦。
(3)适用场景
a) 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
b) 一些重复出现的问题可以用一种简单的语言来进行表达。
c) 一个语言的文法较为简单。
d) 执行效率不是问题。