返回

16.访问者模式

定义一个能够再一个对象结构中对于所有元素执行的操作。访问者让你可以定义一个新的操作,而不必更改到被操作元素的类接口。

访问者模式

定义一个能够再一个对象结构中对于所有元素执行的操作。访问者让你可以定义一个新的操作,而不必更改到被操作元素的类接口。

访问者模式的定义

笔者认为上述定义的重点在于:定义一个新的操作,而不必更改到被操作原色的类接口,这完全符合“开闭原则”的要求,利用新增的方法来增加功能,而不是修改先有程序代码来完成。

参与者说明如下

  • IShape(形状接口)
    • 定义形状的接口操作
    • 包含了RunVisitor方法,来执行IShapeVisitor访问者中的方法
  • Sphere\Cyliner\Cube(各种形状)
    • 3个实现形状接口的子类
    • 重新实现RunVisitor的方法,并根据不同的子类来调用IShapeVisitor访问者中的特定方法。
  • ShapeContainer(形状容器)
    • 包含所有产生的IShape对象
  • IShapeVisitor(形状访问者)
    • 定义形状访问者的操作接口
    • 定义让每个不同形状可调用的方法
  • DrawVisitor\VectorCountVisitor\SphereVolumeVisitor(多个访问者)
    • 实现IShapeVisitor形状访问者接口的子类
    • 实现与形状类功能有关的敌方
    • 可以只重新实现特定的方法,建立只针对某个形状子类的操作功能

具体实现

    // 繪圖引擎
    public abstract class RenderEngine
    {
        public abstract void Render(string ObjName);
        public abstract void Text(string Text);
    }

    // DirectX引擎 
    public class DirectX : RenderEngine
    {
        public override void Render(string ObjName)
        {
            DXRender(ObjName);
        }

        public override void Text(string Text)
        {
            DXRender(Text);
        }

        public void DXRender(string ObjName)
        {
            Debug.Log("DXRender:" + ObjName);
        }
    }

    // OpenGL引擎 
    public class OpenGL : RenderEngine
    {
        public override void Render(string ObjName)
        {
            GLRender(ObjName);
        }

        public override void Text(string Text)
        {
            GLRender(Text);
        }

        public void GLRender(string ObjName)
        {
            Debug.Log("OpenGL:" + ObjName);
        }
    }


    // 訪問者界面
    public abstract class IShapeVisitor
    {
        // Sphere類別呼叫用
        public virtual void VisitSphere(Sphere theSphere)
        {
        }

        // Cube類別呼叫用
        public virtual void VisitCube(Cube theCube)
        {
        }

        // Cylinder類別呼叫用
        public virtual void VisitCylinder(Cylinder theCylinder)
        {
        }
    }

    // 繪圖
    public class DrawVisitor : IShapeVisitor
    {
        // Sphere類別呼叫用
        public override void VisitSphere(Sphere theSphere)
        {
            theSphere.Draw();
        }

        // Cube類別呼叫用
        public override void VisitCube(Cube theCube)
        {
            theCube.Draw();
        }

        // Cylinder類別呼叫用
        public override void VisitCylinder(Cylinder theCylinder)
        {
            theCylinder.Draw();
        }
    }

    // 計數
    public class VectorCountVisitor : IShapeVisitor
    {
        public int Count = 0;

        // Sphere類別呼叫用
        public override void VisitSphere(Sphere theSphere)
        {
            Count += theSphere.GetVectorCount();
        }

        // Cube類別呼叫用
        public override void VisitCube(Cube theCube)
        {
            Count += theCube.GetVectorCount();
        }

        // Cylinder類別呼叫用
        public override void VisitCylinder(Cylinder theCylinder)
        {
            Count += theCylinder.GetVectorCount();
        }
    }

    // 只計算圓型體積
    public class SphereVolumeVisitor : IShapeVisitor
    {
        public float Volume;

        // Sphere類別呼叫用
        public override void VisitSphere(Sphere theSphere)
        {
            Volume += theSphere.GetVolume();
        }
    }

    // 形狀
    public abstract class IShape
    {
        protected RenderEngine m_RenderEngine = null; // 使用的繪圖引擎
        protected Vector3 m_Position = Vector3.zero; // 顯示位置
        protected Vector3 m_Scale = Vector3.zero; // 大小(縮放)

        public void SetRenderEngine(RenderEngine theRenderEngine)
        {
            m_RenderEngine = theRenderEngine;
        }

        public Vector3 GetPosition()
        {
            return m_Position;
        }

        public Vector3 GetScale()
        {
            return m_Scale;
        }

        public abstract void Draw(); // 繪出
        public abstract float GetVolume(); // 取得體積
        public abstract int GetVectorCount(); // 取得頂點數
        public abstract void RunVisitor(IShapeVisitor theVisitor);
    }

    // 圓球
    public class Sphere : IShape
    {
        public Sphere(RenderEngine theRenderEngine)
        {
            base.SetRenderEngine(theRenderEngine);
        }

        public override void Draw()
        {
            m_RenderEngine.Render("Shape");
        }

        public override float GetVolume()
        {
            return 0;
        }

        public override int GetVectorCount()
        {
            return 0;
        }

        public override void RunVisitor(IShapeVisitor theVisitor)
        {
            theVisitor.VisitSphere(this);
        }
    }

    // 方塊
    public class Cube : IShape
    {
        public Cube(RenderEngine theRenderEngine)
        {
            base.SetRenderEngine(theRenderEngine);
        }

        public override void Draw()
        {
            m_RenderEngine.Render("Cube");
        }

        public override float GetVolume()
        {
            return 0;
        }

        public override int GetVectorCount()
        {
            return 0;
        }

        public override void RunVisitor(IShapeVisitor theVisitor)
        {
            theVisitor.VisitCube(this);
        }
    }

    // 圖柱體
    public class Cylinder : IShape
    {
        public Cylinder(RenderEngine theRenderEngine)
        {
            base.SetRenderEngine(theRenderEngine);
        }

        public override void Draw()
        {
            m_RenderEngine.Render("Cylinder");
        }

        public override float GetVolume()
        {
            return 0;
        }

        public override int GetVectorCount()
        {
            return 0;
        }

        public override void RunVisitor(IShapeVisitor theVisitor)
        {
            theVisitor.VisitCylinder(this);
        }
    }


    // 形狀容器
    public class ShapeContainer
    {
        List<IShape> m_Shapes = new List<IShape>();

        public ShapeContainer()
        {
        }

        // 新增
        public void AddShape(IShape theShape)
        {
            m_Shapes.Add(theShape);
        }

        // 共用的訪問者界面
        public void RunVisitor(IShapeVisitor theVisitor)
        {
            foreach (IShape theShape in m_Shapes)
                theShape.RunVisitor(theVisitor);
        }
    }
public class ShapeVisitorTest 
{
	void UnitTest () 
	{
		DirectX theDirectX = new DirectX();
		
		// 加入形狀
		ShapeContainer theShapeContainer = new ShapeContainer();
		theShapeContainer.AddShape( new Cube(theDirectX) );
		theShapeContainer.AddShape( new Cylinder(theDirectX) );
		theShapeContainer.AddShape( new Sphere(theDirectX) );
		
		// 繪圖
		theShapeContainer.RunVisitor(new DrawVisitor());
		
		// 頂點數
		VectorCountVisitor theVectorCount = new VectorCountVisitor();
		theShapeContainer.RunVisitor( theVectorCount );
		Debug.Log("頂點數:"+ theVectorCount.Count );
		
		// 圓體積
		SphereVolumeVisitor theSphereVolume = new SphereVolumeVisitor();
		theShapeContainer.RunVisitor( theSphereVolume );
		Debug.Log("圓體積:"+ theSphereVolume.Volume );
	}
}

访问者模式的优点

运用访问者模式(Visitor)后的系统,可以利用新增访问者类的方式,来便利所有对象并指向特定功能的操作,过程中不需要更改任何其他的类。但是新增被访问者的类,会造成系统大量的修改,这是必须注意的,而被访问者对象需要开放足够的操作方法和信息是访问者模式的另一个缺点

访问者模式的应用

在一般游戏中,除了角色系统之外,其他系统也常需要使用“遍历所有对象”的功能,如角色的道具包、当前已收集的卡片、可以使用的宠物等,正对装载这些对象的“管理容器”类,经常会需要更改类接口来满足游戏新增的需求,此时就可以选择使用访问者模式来减少“管理容器”类的更改。