实用的设计模式
一、访问者模式(Visitor)
osg中节点node系统是一个庞大复杂的类层次体系,包括叶节点Geode、组节点Group、变换节点Transform、几何节点Drawable等等,他们都是Node的子类。开发功能时,想要访问到某个节点的子节点只能通过getChildren()接口获取到Node*类型的指针变量,并动态类型转换为子类指针。
osg支持osg::NodeVisitor类来实现访问者模式。也就是,一个osg::NodeVisitor派生类遍历一个场景图,访问每一个节点,并应用用户定义的操作。他是更新,事件与裁剪遍历(例如osgUtil::CullVisitor)以及其他一些场景图工具,包括osgUtil::SmoothingVisitor,osgUtil::Simplifier与osgUtil::TriStripVisitor的实现的基类,所有这些类都会遍历指定的子场景图并且在osg::Geode节点中的几何体上应用多边形修改。基于双分派理论,开发者可以使用特定的操作请求自定义其访问者,并且在运行时将访问者绑定到不同的元素类型而不修改元素接口。这是一种无需定义多个新元素子类来扩展元素功能的好方法。
virtual void apply( osg::Node& ); virtual void apply( osg::Geode& ); virtual void apply( osg::Group& ); virtual void apply( osg::Transform& );
比如说设计访问者基类ToolBaseVisitor和访问对象基类ToolBase。
class ToolBaseVisitor { public: virtual void excute(QSharedPointer<ToolBase> tool)=0; };
实现更新的访问具体类UpdateToolVisitor和具体的测量工具类。并可以通过模板方式调用不同的visitor。
class UpdateToolVisitor:public ToolBaseVisitor { public: virtual void excute(QSharedPointer<ToolBase> tool) { tool->action(*this); } }; class MeasureTool:public ToolBase { virtual void action(const UpdateToolVisitor& visitor) { ///concrete method } };
所谓双分派就是指的访问者和被访问者都是多态的,设想一下如果仅仅只有被访问者是多态的,我们可以用父类指针执行子类实际的方法,那么每添加一种访问就要在父类加一个虚函数,外部调用就要判断访问者类型,然后根据类型来调用具体的方法,非常不灵活。
二、命令模式(Command)
命令模式可以将调用函数的参数封装,封装后的对象可以记录调用过程,用于撤销重做。
比较简单的例子如下,命令模式响应不同的操作:
///命令基类 class Command { public: virtual ~Command() {} virtual void execute() = 0; }; ///实际的操作命令子类 class JumpCommand : public Command { public: virtual void execute() { jump(); } }; class FireCommand : public Command { public: virtual void execute() { fireGun(); } }; ///外部调用 void InputHandler::handleInput() { if (isPressed(BUTTON_X)) buttonX_->execute(); else if (isPressed(BUTTON_Y)) buttonY_->execute(); else if (isPressed(BUTTON_A)) buttonA_->execute(); else if (isPressed(BUTTON_B)) buttonB_->execute(); }
队列实现撤销重做:
class Command { public: virtual ~Command() {} virtual void execute() = 0; virtual void undo() = 0; }; class MoveUnitCommand : public Command { public: MoveUnitCommand(Unit* unit, int x, int y) : unit_(unit), xBefore_(0), yBefore_(0), x_(x), y_(y) {} virtual void execute() { // 保存移动之前的位置 // 这样之后可以复原。 xBefore_ = unit_->x(); yBefore_ = unit_->y(); unit_->moveTo(x_, y_); } virtual void undo() { unit_->moveTo(xBefore_, yBefore_); } private: Unit* unit_; int xBefore_, yBefore_; int x_, y_; };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具