实用的设计模式

wangxydela / 2024-11-07 / 原文

一、访问者模式(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_;
};