23種設計模式C++實現——解釋器模式
在做面向對象的軟件開發時我們往往想達到更高的代碼可複用性和更合理的軟件顆粒度。
根據《設計模式——可複用面向對象軟件的基礎》所說:“你必須找到相關的對象,以適當的顆粒度將他們迴歸類,再定義類的接口和繼承層次,建立對象之間的基本關係。你的設計應該對手頭的問題有針對性,同時對將來的問題和需求也要有足夠的通用性。”
內行的設計者知道:不是解決任何問題都要從頭做起。他們更願意複用以前使用的解決方案。這些重複的模式方案解決特定的問題,使得面向對象的設計更靈活、優雅,最終複用性更好。它們幫助設計者將新的設計建立在以往的工作基礎上,複用以往的成功設計方案。一個熟悉這些設計模式的設計者不需要再去發現它們,而能夠立即將他們應用於設計問題中。
本系列文章主要參考文獻爲——設計模式,可複用面向對象軟件的基礎(Design Patterns Elements of Reusable Object-Oriented SoftWare Erich.),內部代碼基本用C++語言編寫。
彙總鏈接:23種設計模式C++實現——概要(索引彙總)
文章目錄
摘要
本章主要說明解釋器模式,該設計模式主要意圖是:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的例子。
具體實現下邊我們就通過以下的栗子,解釋一個簡單控制小車移動的例子來說明什麼是解釋器模式。
該例子中的文法相對比較簡單,語法如下所示:
/******************************************************************
** expression ::= direction action distance | composite //表達式
** composite ::= expression 'and' expression //複合表達式
** direction ::= 'up' | 'down' | 'left' | 'right' //移動方向
** action ::= 'move' //移動方式
** distance ::= an integer //移動距離
******************************************************************/
主要參與者
該設計模式的參與者有4個,分別是:
- AbstractExpression 抽象表達式
- TerminalExpression(SentenceExpression)終結符表達式
- NoneTerminalExpression(DirectionExpression ActionExpression DistanceExpression ) 非終結符表達式
- Context(Context)上下文,包含解釋器外的一些信息
- Client 用戶
- Singleton 單例類,用於保存一些全局數據(這個是爲了方便額外添加的)
優點
- 易於改變和擴展文法,一般通過繼承來改變或擴展該文法
- 易於實現文法,定義抽象語法樹中各個結點的類的實現大體類似
缺點
- 複雜的文法難以維護
具體實現代碼
單例類
class Singleton
{
private:
Singleton()
{
m_pos.setX(abs( qrand() % 10));
m_pos.setY(abs( qrand() % 10));
QString str = "Init Pos x = " + QString::number(m_pos.x()) + " y = " + QString::number(m_pos.y());
qDebug()<<qPrintable(str);
}
static Singleton* m_pSingleton;
QPoint m_pos;
public:
static Singleton* instance()
{
return m_pSingleton;
}
QPoint getPos(){return m_pos;}
void changePos(int x, int y)
{
m_pos.rx() += x;
m_pos.ry() += y;
}
};
Singleton* Singleton::m_pSingleton = new Singleton();
抽象表達式(AbstractExpression)
class AbstractExpression
{
public:
virtual QString interpreter() = 0;
};
終結符表達式
SentenceExpression
class SentenceExpression : public AbstractExpression
{
public:
SentenceExpression(AbstractExpression *direction, AbstractExpression *action, AbstractExpression *distance)
:m_pDirection(direction), m_pAction(action), m_pDistance(distance){}
QString interpreter()
{
int x = 0, y = 0;
QString p_strDirection = m_pDirection->interpreter();
if (p_strDirection.contains("wrong", Qt::CaseInsensitive))
{
return "wrong";
}
else
{
QStringList strList = p_strDirection.split(",");
if(strList.at(0) == "x")
{
x = strList.at(1).toInt();
}
else if(strList.at(0) == "y")
{
y = strList.at(1).toInt();
}
}
QString p_strAction = m_pAction->interpreter();
QString p_strDistance = m_pDistance->interpreter();
x = x * p_strDistance.toInt();
y = y * p_strDistance.toInt();
Singleton::instance()->changePos(x, y);
QString strResult = p_strDirection + " " + p_strAction + " " + p_strDistance + " ";
return strResult;
}
private:
AbstractExpression *m_pDirection;
AbstractExpression *m_pAction;
AbstractExpression *m_pDistance;
};
非終結符表達式
AndNode
class AndNode : public AbstractExpression
{
public:
AndNode(AbstractExpression *left, AbstractExpression * right):m_left(left),m_right(right) {}
QString interpreter()
{
QString p_strLeft = m_left->interpreter();
QString p_strRight = m_right->interpreter();
QString p_strResult = p_strLeft + p_strRight;
return p_strResult;
}
private:
AbstractExpression *m_left;
AbstractExpression *m_right;
};
DirectionExpression
class DirectionExpression : public AbstractExpression
{
public:
DirectionExpression(QString direction) : m_strDirection(direction){}
QString interpreter()
{
QString strResult;
if (0 == m_strDirection.compare(QString("up"), Qt::CaseInsensitive))
{
strResult = "y,1";
}
else if (0 == m_strDirection.compare(QString("down"), Qt::CaseInsensitive))
{
strResult = "y,-1";
}
else if (0 == m_strDirection.compare(QString("right"), Qt::CaseInsensitive))
{
strResult = "x,1";
}
else if (0 == m_strDirection.compare(QString("left"), Qt::CaseInsensitive))
{
strResult = "x,-1";
}
else
{
strResult = "wrong";
}
return strResult;
}
private:
QString m_strDirection;
};
ActionExpression
class ActionExpression : public AbstractExpression
{
public:
ActionExpression(QString action) : m_strAction(action){}
QString interpreter()
{
QString strResult;
if (0 == m_strAction.compare(QString("move"), Qt::CaseInsensitive))
{
strResult = "move";
}
else
{
strResult = "wrong";
}
return strResult;
}
private:
QString m_strAction;
};
DistanceExpression
class DistanceExpression : public AbstractExpression
{
public:
DistanceExpression(QString distance) : m_strDistance(distance){}
QString interpreter()
{
return m_strDistance;
}
private:
QString m_strDistance;
};
上下文(Context)
class Context
{
public:
Context(QString instruction) : m_strInstruction(instruction), m_pTree(NULL){}
void Handle();
void Output();
private:
QString m_strInstruction;
AbstractExpression *m_pTree;
};
void Context::Handle()
{
AbstractExpression *pLeft = NULL;
AbstractExpression *pRight = NULL;
AbstractExpression *pDirection = NULL;
AbstractExpression *pAction = NULL;
AbstractExpression *pDistance = NULL;
vector<AbstractExpression *> node; // Store the instruction expression
//Bing's code
int startLoc = 0, endLoc = 0;
startLoc = m_strInstruction.indexOf("\"", startLoc);
endLoc = m_strInstruction.indexOf("\"", startLoc + 1);
QString dataStr;
if (-1 != startLoc && -1 != endLoc)
{
dataStr = m_strInstruction.mid(startLoc + 1, endLoc - startLoc - 1);
}
else if (-1 == startLoc && -1 == endLoc)
{
dataStr = m_strInstruction;
}
else
{
qDebug() << "Input wrong sentence";
return;
}
qDebug() << dataStr;
QVector<QString> dataVec;
int singleLoc = 0;
for (int i = 0; i < dataStr.length();)
{
singleLoc = dataStr.indexOf(" ", i);
if (-1 != singleLoc)
{
dataVec.push_back(dataStr.mid(i, singleLoc - i));
i = singleLoc + 1;
}
else
{
dataVec.push_back(dataStr.mid(i, dataStr.length() - i));
i = dataStr.length();
}
}
for (unsigned int i = 0; i < dataVec.size();)
{
QString tmpStr = dataVec[i];
if ("and" == tmpStr)
{ i++;
pDirection = new DirectionExpression(dataVec[i++]);
pAction = new ActionExpression(dataVec[i++]);
pDistance = new DistanceExpression(dataVec[i++]);
pRight = new SentenceExpression(pDirection, pAction, pDistance);
pLeft = new AndNode(pLeft, pRight);
node.push_back(pLeft);
}
else
{
pDirection = new DirectionExpression(dataVec[i++]);
pAction = new ActionExpression(dataVec[i++]);
pDistance = new DistanceExpression(dataVec[i++]);
pLeft = new SentenceExpression(pDirection, pAction, pDistance);
}
}
m_pTree = node[node.size() - 1];
}
void Context::Output()
{
QString result = m_pTree->interpreter();
QString finalPos = QString("Final Position: ")+
QString(" x = ") + QString::number(Singleton::instance()->getPos().x()) +
QString(" y = ") + QString::number(Singleton::instance()->getPos().y());
qDebug() <<"Process Data: "<< qPrintable(result); //Process data
qDebug() << qPrintable(finalPos); //Final position
}
用戶(Client)
/****************************************************************
Doc : main.cpp
Author : BingLee
Date : 2020-06-16
Info : Interpreter Design Patten
https://blog.csdn.net/Bing_Lee (C)All rights reserved.
******************************************************************/
#include <QCoreApplication>
#include "expression.h"
#include <string>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString originalStr = "up move 5 and down move 10 and left move 20";
Context *pInstructionHandler = new Context(originalStr);
pInstructionHandler->Handle();
pInstructionHandler->Output();
return a.exec();
}
輸出結果:
補充說明
該模式的文法相對來說比較簡單,應該是比較容易理解的,在寫這篇文章的過程中看了很多解釋器的文章,可能是由於個人實力的原因,很多文章久久不能看明白,最後終於看懂一部分後寫下這篇文章希望能幫助你理解該模式。
本篇博客中的代碼均已通過編譯,如有Bug,請提出寶貴意見~
注:文章內函數可以把除Client對象放在一個頭文件中,Client文件放在主函數中執行。