前言
最近在看Solidity编译器代码(C++实现),其中使用到了设计模式中的访问者模式,这里正好学习一下。GoF的设计模式书虽然讲的很详细,但是这里还是结合实际项目中的应用来说一下。
代码中的模式应用
先说下上下文,在编译器中有个重要的概念叫AST(抽象语法树)。它是词法分析、语法分析的产物,用它来转中间代码,进而转换成字节码(不管是虚拟机指令还是机器指令,Solidity是转换成EVM指令,EVM即以太坊虚拟机)。ASTNode代表AST中的节点抽象表示,很多子类继承于它,比如Declaration、Statement等很多AST元素。
这里将AST转换成中间代码或字节码使用到了访问者模式,访问者模式的UML图如下所示:
在此模式中有5种角色:Visitor(抽象访问者)、ConcreteVisitor(具体访问者)、Element(抽象元素)、ConcreteElement(具体元素)和ObjectStructure(对象结构)。
Visitor常常有visitXXX的方法,参数是具体元素,比如在Solidity编译器中的实现(有删减,只保留相关代码):
class ASTConstVisitor
{
public:
ASTConstVisitor() = default;
ASTConstVisitor(ASTConstVisitor const&) = delete;
ASTConstVisitor(ASTConstVisitor&&) = delete;
ASTConstVisitor& operator=(ASTConstVisitor const&) = delete;
ASTConstVisitor& operator=(ASTConstVisitor&&) = delete;
virtual ~ASTConstVisitor() = default;
virtual bool visit(SourceUnit const& _node) { return visitNode(_node); }
virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); }
virtual bool visit(ImportDirective const& _node) { return visitNode(_node); }
virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); }
virtual bool visit(IdentifierPath const& _node) { return visitNode(_node); }
virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); }
// ...方法很多,省略
virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); }
virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); }
virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(IdentifierPath const& _node) { endVisitNode(_node); }
virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); }
// ...方法很多,省略
protected:
/// Generic function called by default for each node, to be overridden by derived classes
/// if behaviour unspecific to a node type is desired.
virtual bool visitNode(ASTNode const&) { return true; }
/// Generic function called by default for each node, to be overridden by derived classes
/// if behaviour unspecific to a node type is desired.
virtual void endVisitNode(ASTNode const&) { }
};
ASTConstVisitor即担当抽象访问者的角色。
Element常常有accept方法,参数是抽象访问者(如ASTConstVisitor),比如在Solidity编译器中抽象元素是ASTNode,代码(有删减,只保留相关代码)如下:
class ASTNode
{
public:
/// Noncopyable.
ASTNode(ASTNode const&) = delete;
ASTNode& operator=(ASTNode const&) = delete;
explicit ASTNode(int64_t _id, SourceLocation _location);
virtual ~ASTNode() {}
virtual void accept(ASTConstVisitor& _visitor) const = 0;
template <class T>
static void listAccept(std::vector<T> const& _list, ASTConstVisitor& _visitor)
{
for (T const& element: _list)
if (element)
element->accept(_visitor);
}
};
其中的virtual void accept(ASTConstVisitor& _visitor) const = 0;
即是Element的核心方法。
ConcreteVisitor常常重写visitXXX方法。Solidity中有大量ConcreteVisitor,这里拿ASTConstVisitor的子类ExpressionCompiler来举例,ExpressionCompiler是用来处理Expression,将Expression翻译成中间代码或字节码的,代码(有删减)如下:
class ExpressionCompiler: private ASTConstVisitor
{
public:
ExpressionCompiler(
CompilerContext& _compilerContext,
bool _optimiseOrderLiterals
):
m_optimiseOrderLiterals(_optimiseOrderLiterals),
m_context(_compilerContext)
{}
/// Compile the given @a _expression and leave its value on the stack.
void compile(Expression const& _expression);
private:
bool visit(Conditional const& _condition) override;
bool visit(Assignment const& _assignment) override;
bool visit(TupleExpression const& _tuple) override;
bool visit(UnaryOperation const& _unaryOperation) override;
bool visit(BinaryOperation const& _binaryOperation) override;
bool visit(FunctionCall const& _functionCall) override;
bool visit(FunctionCallOptions const& _functionCallOptions) override;
bool visit(NewExpression const& _newExpression) override;
bool visit(MemberAccess const& _memberAccess) override;
bool visit(IndexAccess const& _indexAccess) override;
bool visit(IndexRangeAccess const& _indexAccess) override;
void endVisit(Identifier const& _identifier) override;
void endVisit(Literal const& _literal) override;
CompilerContext& m_context;
};
ExpressionCompiler重载了一些visit方法,比如bool visit(FunctionCall const& _functionCall) override;
。
ConcreteElement常常重写accept方法,这里拿Expression来举例,代码如下:
class Expression: public ASTNode
{
public:
explicit Expression(int64_t _id, SourceLocation const& _location): ASTNode(_id, _location) {}
ExpressionAnnotation& annotation() const override;
};
class FunctionCall: public Expression
{
public:
FunctionCall(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> _expression,
std::vector<ASTPointer<Expression>> _arguments,
std::vector<ASTPointer<ASTString>> _names,
std::vector<SourceLocation> _nameLocations
):
Expression(_id, _location), m_expression(std::move(_expression)), m_arguments(std::move(_arguments)), m_names(std::move(_names)), m_nameLocations(std::move(_nameLocations))
{
solAssert(m_nameLocations.size() == m_names.size());
}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
/// @returns the given arguments in the order they were written.
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// @returns the given arguments sorted by how the called function takes them.
std::vector<ASTPointer<Expression const>> sortedArguments() const;
/// @returns the list of given argument names if this is a named call,
/// in the order they were written.
/// If this is not a named call, this is empty.
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
std::vector<SourceLocation> const& nameLocations() const { return m_nameLocations; }
FunctionCallAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments;
std::vector<ASTPointer<ASTString>> m_names;
std::vector<SourceLocation> m_nameLocations;
};
FunctionCall是Expression的子类,而Expression是ASTNode的子类,其中FunctionCall重写了accept方法,比如void accept(ASTConstVisitor& _visitor) const override;
。
进行抽象实现
下面按照UML图将访问者模式用C++实现。
首先是Visitor,因为C++没有接口,这里使用抽象类来实现:
#include <vector>
#include <memory>
using namespace std;
class Element;
class Visitor;
class ConcreteElement;
class Visitor {
public:
virtual void visit(ConcreteElement &_node) = 0;
};
class Element {
public:
virtual void accept(Visitor &_visitor) = 0;
};
class ConcreteVisitor : public Visitor {
public:
virtual void visit(ConcreteElement &_node) override {
// do something
}
};
class ConcreteElement : public Element {
public:
virtual void accept(Visitor &_visitor) override {
_visitor.visit(*this);
}
};
class ObjectStructure {
public:
void listAccept(Visitor &_visitor) {
for (auto &element: elements) {
element->accept(_visitor);
}
}
private:
vector<unique_ptr<Element>> elements;
};
总结
如果不使用访问者模式,就需要通过大段的if/else来处理逻辑。使用访问者模式后,如果新增新的元素或访问者,只需要添加新的具体元素或具体访问者即可,对修改关闭,对扩展开放,满足了开闭原则。
标签:node,const,void,visit,模式,virtual,override,设计模式,访问者 From: https://www.cnblogs.com/bfstudy/p/17251104.html