天天看点

STK Component:Evaluator pattern(计算器模式)C# 中的枚举器Evaluator模式AxesLinearRate类的Evaluator代码实现其他Evaluator

在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 = ;
//  新坐标系相对旧坐标系的旋转角加速度(rad/s^2)
axes.RotationalAcceleration = ;
//  新坐标系相对旧坐标系的旋转矢量,此矢量在旧坐标系中表达(此处为绕旧坐标系的X轴旋转)
axes.SpinAxis = new UnitCartesian(, , );

//  ***以上定义了一个新的坐标系,从其定义参数,理论上我们可以求出在任意时刻,旧坐标系到新坐标系的转换矩阵
//----------------------------------------------------------------------------------
//  ***以下为计算旧坐标系到新坐标系的转换矩阵

//  从新坐标系对象获取evaluator
AxesEvaluator evaluator = axes.GetEvaluator();
JulianDate dateToEvaluate = new JulianDate(new GregorianDate(, , , , , ));
//  使用获得的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。而对于其它类型,其计算器架构与其类似。见下图。

STK Component:Evaluator pattern(计算器模式)C# 中的枚举器Evaluator模式AxesLinearRate类的Evaluator代码实现其他Evaluator

Axes、Point、Vector分别表示坐标系、点、矢量的基类,都包含一个GetEvaluator的方法,不同的是对应不同的类型其返回值也不同,分别为AxesEvaluator、PointEvaluator和VectorEvaluator,每个Evaluator的方法Evaluate的返回值也根据其类型不同而不同。

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

继续阅读