STK Component:Evaluator pattern(计算器模式)

来源:互联网 发布:gta5麦克捏脸数据 编辑:程序博客网 时间:2024/05/18 13:46

在STK Component中广泛的利用我们称之为的”计算器模式”。在Component中做任何计算时几乎都需要使用计算器模式。

所谓计算器模式,简单的说就是将一个对象的定义和其计算分离开。对象本身只保存数据,并不实际执行任何计算。从对象中获取其对应的计算器,然后利用计算器执行与该对象有关的计算。

C# 中的枚举器

其实在学习C#中,我们已经遇到过计算器模式,这就是C#中的枚举器。让我们来重新温习一下,有助于理解计算器模式。

下面几个链接都是阐述计算器模式的,如果对枚举器不熟悉的可以参考。
http://www.cnblogs.com/JimmyZhang/archive/2007/08/22/865245.html
http://blog.csdn.net/jiangxinyu/article/details/8554818

面向对象设计原则中有一条是类的单一职责原则,所以我们要尽可能的去分解这些职责,用不同的类去承担不同的职责。Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。

以自定义的字符串数组类为例,示例代码如下:

  //  自定义的类,实现字符串数组的功能(仅展示关键代码,其余省略)    public class MyStringList : IEnumerable<string>    {        //  内部数组,用来保存字符串        string[] strings;        //  此类的其他属性及方法        //...        //------------------------------------------------------------        //  Ienumerble<T>接口的实现,返回实现IEnumerator<T>接口的对象        public IEnumerator<string> GetEnumerator()        {            return new MyStringListEnumerator(strings);        }        //------------------------------------------------------------        // 嵌套的私有ListBoxEnumerator类实现        private class MyStringListEnumerator : IEnumerator<string>        {            string[] mystrs;            //  构造函数            MyStringListEnumerator(string[] strs)            {                this.mystrs = strs;            }            // 接口IEnumerator的代码实现: Current/MoveNext/Reset..                        //...        }    }    //  使用方法:    //  创建对象    MyStringList myList = new MyStringList();    //  获取对象的IEnumerator    IEnumerator<string> myEnumerator = myList.GetEnumerator();    //  使用IEnumerator的相关功能    myEnumerator.MoveNext();    //...

类MyStringList的内部字段strings保存着字符串的数组,同时有个内部的类MyStringListEnumerator来实现IEumerator接口。外部通过类的GetEnumerator()函数来获得实现接口IEnumerator的对象myEnumerator,注意,外部无法直接创建MyStringListEnumerator对象,是由GetEnumerator()函数获取的,获取后即可通过myEnumerator调用相关的功能(如Current,MoveNext,Reset)。

类MyStringList负责对象的建模,这个例子中,就是通过内部的数组保存字符串数组;类MyStringListEnumerator负责对象的功能(如MoveNext等),外部对其不可见,由类MyStringList内部进行实现。通过这两个类把两者的职责分离开来,两者又相互联系,都是通过内部的数组(strings和mystrs,其实两个数组是同一个)来联系的。

Evaluator模式

先来看一个STK Component中Evaluator的例子,以坐标系转换为例。

//  创建新的坐标系AxesLinearRate axes = new AxesLinearRate();//  以J2000的坐标系作为新坐标系axes的参考基准(旧坐标系)axes.ReferenceAxes = CentralBodiesFacet.GetFromContext().Earth.J2000Frame.Axes;//  初始时刻axes.ReferenceEpoch = TimeConstants.J2000;//  初始时刻:新坐标系相对旧坐标系的旋转(此处为单位阵,也就是说初始时刻两个坐标系重合)axes.InitialRotation = UnitQuaternion.Identity;//  新坐标系相对旧坐标系的旋转角速度(rad/s)axes.InitialRotationalVelocity = 0.1;//  新坐标系相对旧坐标系的旋转角加速度(rad/s^2)axes.RotationalAcceleration = 0.0;//  新坐标系相对旧坐标系的旋转矢量,此矢量在旧坐标系中表达(此处为绕旧坐标系的X轴旋转)axes.SpinAxis = new UnitCartesian(1.0, 0.0, 0.0);//  ***以上定义了一个新的坐标系,从其定义参数,理论上我们可以求出在任意时刻,旧坐标系到新坐标系的转换矩阵//----------------------------------------------------------------------------------//  ***以下为计算旧坐标系到新坐标系的转换矩阵//  从新坐标系对象获取evaluatorAxesEvaluator evaluator = axes.GetEvaluator();JulianDate dateToEvaluate = new JulianDate(new GregorianDate(2007, 11, 20, 12, 0, 0));//  使用获得的evaluator计算某时刻的旧坐标系到新坐标系的转换矩阵(单位四元素形式)UnitQuaternion rotationFromJ2000 = evaluator.Evaluate(dateToEvaluate);

上面的例子中,创建了一个坐标系对象axes,并计算其基准坐标系(旧坐标系)到它的转换矩阵。可以看出,axes对象仅仅用来描述一个新的坐标系是如何指向的(相对旧坐标系而言),而从旧坐标系到新坐标系的转换计算工作是由另一个类来实现的,它就是AxesEvaluator,是通过axes对象的GetEvaluator()方法来获取的。由AxesEvaluator类的函数Evaluate(JulianDate date)来计算两个坐标系的转换关系。这种将对象的定义和其计算功能分开就是STK Component中的Evaluator模式。
可以推测的是AxesEvaluator对象evaluator中必然保留或者直接指向axes中的相关定义参数,否则evaluator无法计算两个坐标系的转换。

AxesLinearRate类的Evaluator代码实现

还是以坐标系AxesLinearRate类为例,通过.Net Reflector软件看看其源代码,从而了解其内部机制。

//  线性旋转坐标系类(继承自基类:Axes)public class AxesLinearRate : Axes{    // 属性,描述相对旧坐标系的一系列属性参数    // Properties    public UnitQuaternion InitialRotation { get; set; }    public double InitialRotationalVelocity { get; set; }    // **参考坐标系,也就是旧坐标系    public Axes ReferenceAxes { get; set; }    public JulianDate ReferenceEpoch { get; set; }    public double RotationalAcceleration { get; set; }    public UnitCartesian SpinAxis { get; set; }    // Methods    // ...一系列override基类Axes的方法        //    // **注意,此内部方法中,实际创建AxesEvaluator    private AxesEvaluator CreateEvaluator(EvaluatorGroup group)    {      return new Evaluator(this.m_referenceAxes, this.m_epoch, this.m_initialRotation, this.m_spinAxis, this.m_initialRotationalVelocity, this.m_constantRotationalAcceleration);    }    // ...     public override AxesEvaluator GetEvaluator(EvaluatorGroup group);    //------------------------------------------------------------------    // **注意,由内部类来实现AxesEvaluator    // 类Evaluator的名字可以取任何值,反正外部看不到,在外部,是以基类AxexEvaluator来代替。    private class Evaluator : AxesEvaluator    {        // **看看这里的内部参数,是不是和类AxesLinearRate的属性参数基本一致??!!        // 只要靠这些参数,它才能知道怎么计算两个坐标系的转换!!        // Fields        private double m_constantRotationalAcceleration;        private JulianDate m_epoch;        private UnitQuaternion m_initialRotation;        private double m_initialRotationalVelocity;        private Axes m_referenceAxes;        private UnitCartesian m_spinAxis;        // Methods        // 这个构造函数在私有方法CreateEvaluator中被调用        public Evaluator(Axes referenceAxes, JulianDate epoch, UnitQuaternion initialRotation, UnitCartesian spinAxis, double initialRotationalVelocity, double constantRotationalAcceleration);        // **最重要方法!!!用来计算从旧坐标系到此坐标系的转换,此方法内部的代码就是具体的计算过程!        public override UnitQuaternion Evaluate(JulianDate date);        public override Motion<UnitQuaternion, Cartesian> Evaluate(JulianDate date, int order);       //...其他方法        // 这个属性用来保存旧坐标系        public override TimeIntervalCollection<Axes> DefinedInIntervals { get; }        // ...            }}

首先类AxesLinearRate是继承基类Axes,实际上所有的坐标系都继承自Axes。类AxesLinearRate的一个重要方法就是GetEvaluator,用来获取其计算类AxesEvaluator对象。而AxesEvaluator的类的实现是由内部类Evaluator来实现的。

在内部类Evaluator中,保存了类AxesLinearRate的相关参数,以及用于计算坐标系转换的方法:Evaluate,其返回值为UnitQuaternion或Motion

其他Evaluator

对于每个继承自Axes的类来说,其计算器类都为AxesEvaluator,由继承类负责实现AxesEvaluator类的实现,其Evaluate方法的返回值为UnitQuaternion。而对于其它类型,其计算器架构与其类似。见下图。
这里写图片描述
Axes、Point、Vector分别表示坐标系、点、矢量的基类,都包含一个GetEvaluator的方法,不同的是对应不同的类型其返回值也不同,分别为AxesEvaluator、PointEvaluator和VectorEvaluator,每个Evaluator的方法Evaluate的返回值也根据其类型不同而不同。

在自己以这些基类创建相应的具体类时,需要自行实现其对应的Evaluator。

0 0
原创粉丝点击