首页 > 其他分享 >【设计模式】行为型之访问者模式

【设计模式】行为型之访问者模式

时间:2023-03-24 12:11:07浏览次数:34  
标签:node const void visit 模式 virtual override 设计模式 访问者

前言

最近在看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

相关文章

  • 重学Java设计模式-结构型模式-组合模式
    重学Java设计模式-结构型模式-组合模式内容摘自:https://bugstack.cn/md/develop/design-pattern/2020-06-08-重学Java设计模式《实战组合模式》.html#重学-java-设计模......
  • 前端设计模式——外观模式
    外观模式(FacadePattern):它提供了一个简单的接口,用于访问复杂的系统或子系统。通过外观模式,客户端可以通过一个简单的接口来访问复杂的系统,而无需了解系统内部的具体实现细......
  • 重学Java设计模式-结构型模式-桥接模式
    重学Java设计模式-结构型模式-桥接模式内容摘自:https://bugstack.cn/md/develop/design-pattern/2020-06-04-重学Java设计模式《实战桥接模式》.html#重学-java-设计模......
  • VMware NAT 模式 虚拟机网络电缆被拔出,连不上网
    检查服务VMnetDHCP,VMwareNATService服务是否已启动,启动后可以正常使用网络......
  • 需求分析阶段商业模式
    需求分析阶段商业模式在需求分析阶段的商业模式中,以下是九种关键要素:名称描述价值主张(ValueProposition)告诉客户企业所提供的产品或服务的唯一价值市场......
  • 6502 寻址模式详解
    6502共有13种寻址模式:A:寄存器寻址。指令形式为OPCA;目标数据位于A寄存器中,属于隐含寻址;使用这种寻址模式的指令都是1个字节长度,需要2个时钟周期abs:绝对地址寻......
  • RabbitMQ 03 直连模式-可视化界面
    这里先演示最简单的模型:直连模式。其结构图为:一个生产者->消息队列->一个消费者生产者只需要将数据丢进消息队列,而消费者只需要将数据从消息队列中取出,这样就实现了......
  • [设计模式]外观模式(Facade)
    外观模式是为了解决类与类之间的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该......
  • [设计模式]代理模式
    代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处......
  • [设计模式]桥接模式(Bridge) DriverManager
    将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,......