65.9K
CodeProject 正在变化。 阅读更多。
Home

软件开发中的抽象无意义。数据库

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2014 年 1 月 18 日

CPOL

13分钟阅读

viewsIcon

17578

抽象方法在数据库领域的应用。

Abstract nonsense

有用链接

1 简介。

本文是对我之前关于 常见问题实时性 的文章的进一步发展。本文依赖于之前的文章。我的示例通过“序列化”的示例完成,其中数据集已序列化。使用这些示例不需要 SQL Server 数据库。

重要提示:演示解决方案包含 OracleTableProvider 项目。该项目旨在支持 Oracle 客户端。如果未安装 Oracle 客户端,则应从解决方案中排除 OracleTableProvider 项目。

2 背景

数据库域包含以下基本类型。

  • IDataSetProvider:此接口由任何提供 DataSet 的对象实现。
  • IDataSetConsumer:此接口由任何使用 DataSet 的对象实现。
  • DataSetArrow:此箭头的源(或目标)应实现 IDataSetConsumer(或 IDataSetProvider)接口。

以下代码包含这些基本类型。

     /// <summary>
    /// Provider of data set
    /// </summary>
    public interface IDataSetProvider
    {
        /// <summary>
        /// Provided data set
        /// </summary>
        DataSet DataSet
        {
            get;
        }

        /// <summary>
        /// Factory. This object can be null. It is not null for databases (SQL Server, Oracle, ...)
        /// </summary>
        IDataSetFactory Factory
        {
            get;
            set;
        }

        /// <summary>
        /// Change event
        /// </summary>
        event Action<DataSet> Change;
    } 

    /// <summary>
    /// Data set consumer
    /// </summary>
    public interface IDataSetConsumer
    {
        /// <summary>
        /// Adds data set
        /// </summary>
        /// <param name="dataSet">Data set to add</param>
        void Add(DataSet dataSet);

        /// <summary>
        /// Removes data set
        /// </summary>
        /// <param name="dataSet">Data set to remove</param>
        void Remove(DataSet dataSet);

        /// <summary>
        /// Factory
        /// </summary>
        IDataSetFactory Factory
        {
            get;
            set;
        }

        /// <summary>
        /// Add event
        /// </summary>
        event Action<DataSet> OnAdd;

        /// <summary>
        /// Add event
        /// </summary>
        event Action<DataSet> OnRemome;

    }
     /// <summary>
    /// Arrow between data set provider and data set consumer
    /// </summary>
    [SerializableAttribute()]
    public class DataSetArrow : CategoryArrow, ISerializable, IRemovableObject
    {

        #region Fields

         /// <summary>
        /// Source
        /// </summary>
        protected IDataSetConsumer source;

        /// <summary>
        /// Target
        /// </summary>
        protected IDataSetProvider target;

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public DataSetArrow()
        {
        }

        /// <summary>
        /// Deserialization constructor
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        public DataSetArrow(SerializationInfo info, StreamingContext context)
        {
        }

        #endregion

        #region ISerializable Members

        /// <summary>
        /// ISerializable interface implementation
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
        }

        #endregion

        #region ICategoryArrow Members

        /// <summary>
        /// The source of this arrow
        /// </summary>
        public override ICategoryObject Source
        {
            get
            {
                return source as ICategoryObject;
            }
            set
            {
                source = value.GetSource<IDataSetConsumer>();
            }
        }

        /// <summary>
        /// The target of this arrow
        /// </summary>
        public override ICategoryObject Target
        {
            get
            {
                return target as ICategoryObject;
            }
            set
            {
                target = value.GetTarget<IDataSetProvider>();
                source.Factory = target.Factory;
                source.Add(target.DataSet);
            }
        }


        #endregion

        #region IRemovableObject Members

        /// <summary>
        /// The post remove operation
        /// </summary>
        public void RemoveObject()
        {
            if (source != null & target != null)
            {
                if (target.DataSet != null)
                {
                    source.Remove(target.DataSet);
                }
            }
        }

        #endregion

    }

下图显示了这些基本对象的示例。

Data provider & data consumer

IPAC 是从 NASA/IPAC 银河系外数据库获取的数据集的提供者。Chart 是数据集的消费者。LinkDataSetArrow 类型的对象。数据集提供者与数据集消费者分离是 桥接模式 的一种实现,下图展示了不同的消费者如何与不同的提供者连接。

Bridge

任何数据集提供者都可以被另一个提供者替换。以下 3 张图片分别代表从 Sql Server、Oracle 和序列化数据集中获取的相同数据集。

SQL ServerOracleSerialized data set

我的示例通过序列化数据集来完成,以实现自主性。

3 数据集提供者

3.1 数据库数据集

数据集可以从不同的数据库 (SQL ServerOracle 等) 获取。在这种情况下,IDataSetProviderFactory 属性不应为 null。此属性的类型为 IDataSetFactory

     /// <summary>
    /// Factory for creating data set metatata from connection
    /// </summary>
    public interface IDataSetFactory
    {
        /// <summary>
        /// Name of factory
        /// </summary>
        string FactoryName
        {
            get;
        }

        /// <summary>
        /// Creates connection
        /// </summary>
        DbConnection Connection
        {
            get;
        }

        /// <summary>
        /// Command
        /// </summary>
        DbCommand Command
        {
            get;
        }

        /// <summary>
        /// Gets metadata data set from connection
        /// </summary>
        /// <param name="connection">The connection</param>
        /// <returns>The metadata data set</returns>
        DataSet GetData(DbConnection connection);

        /// <summary>
        /// Gets metadata data set from connection string
        /// </summary>
        /// <param name="connectionString">The connection</param>
        /// <returns>The metadata data set</returns>
        DataSet GetData(string connectionString);

        /// <summary>
        /// Data adapter
        /// </summary>
        IDbDataAdapter Adapter
        {
            get;
        }

    // Full code is contained in the source file

 }

以下类图显示了上述接口的不同实现。

Databases

以下代码代表 SQL Server 和 Oracle 的此接口实现。

     /// <summary>
    /// Factory of SQL Server
    /// </summary>
    public class SQLServerFactory : IDataSetFactory
    {
               #region IDataSetFactory Members

        /// <summary>
        /// Name of factory
        /// </summary>
        string IDataSetFactory.FactoryName
        {
            get
            {
                return "SQL Server";
            }
        }

        /// <summary>
        /// Creates connection
        /// </summary>
        System.Data.Common.DbConnection IDataSetFactory.Connection
        {
            get
            {
                return new SqlConnection();
            }
        }

        /// <summary>
        /// Command
        /// </summary>
        System.Data.Common.DbCommand IDataSetFactory.Command
        {
            get
            {
                return new SqlCommand();
            }
        }

        // Full code is contained in the source file

        #endregion
    }
     /// <summary>
    /// Oracle data set factory
    /// </summary>
    public class OracleFactory : IDataSetFactory
    {
        public static readonly OracleFactory Singleton = new OracleFactory();

        private OracleFactory()
        {
        }

        #region IDataSetFactory Members

        /// <summary>
        /// Creates connection
        /// </summary>
        public System.Data.Common.DbConnection Connection
        {
            get
            {
                return new OracleConnection();
            }
        }

        /// <summary>
        /// Command
        /// </summary>
        public System.Data.Common.DbCommand Command
        {
            get
            {
                return new OracleCommand();
            }
        }

        // Full code is contained in the source file

        #endregion
   }

3.2 文件数据集

DataSet 类是可序列化的。SavedDataProvider 仅序列化数据集。以下代码代表其实现。

   /// <summary>
    /// Data provider from xml
    /// </summary>
    [Serializable()]
    public class SavedDataProvider : CategoryObject, ISerializable, IDataSetProvider
    {

        #region Fields

        /// <summary>
        /// Data set
        /// </summary>
        protected DataSet dataSet = new DataSet();

        /// <summary>
        /// Change event
        /// </summary>
        protected Action<DataSet> change = (DataSet ds) => { };


        #endregion

        #region Ctor

        /// <summary>
        /// Default Constructor
        /// </summary>
        public SavedDataProvider()
        {
        }

        /// <summary>
        /// Deserialization constructor
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        protected SavedDataProvider(SerializationInfo info, StreamingContext context)
        {
            dataSet = info.Deserialize<DataSet>("DataSet");
        }

        #endregion

        #region ISerializable Members

        /// <summary>
        /// ISerializable interface implementation
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.Serialize<DataSet>("DataSet", dataSet);
        }

        #endregion

        #region IDataSetProvider Members

        DataSet IDataSetProvider.DataSet
        {
            get { return dataSet; }
        }

        IDataSetFactory IDataSetProvider.Factory
        {
            get
            {
                return null;
            }
            set
            {
            }
        }

        event Action<DataSet> IDataSetProvider.Change
        {
            add { change += value; }
            remove { change -= value; }
        }

        #endregion

        #region Members

        /// <summary>
        /// Sets Data set
        /// </summary>
        /// <param name="dataSet"></param>
        public void Set(DataSet dataSet)
        {
            this.dataSet = dataSet;
            change(dataSet);
        }

        #endregion

    }

4.3 外部数据集

数据集可以从不同来源获取。例如,数据集可以从 NASA/IPAC 银河系外数据库获取。我为此目的开发了 ExternalDataSetProvider 类。

    /// <summary>
    /// External data set provider
    /// </summary>
    [Serializable()]
    public class ExternalDataSetProvider : SavedDataProvider, IChildrenObject
    {
        #region Fields

        /// <summary>
        /// Factory of data set
        /// </summary>
        IDataSetPoviderFactory factory;

        /// <summary>
        /// Type name of factory
        /// </summary>
        string factoryType;

        /// <summary>
        /// Url
        /// </summary>
        string url = "";

        IAssociatedObject[] children = new IAssociatedObject[1];

        #endregion

        #region Ctor

        /// <summary>
        /// Constructor from type of child object
        /// </summary>
        /// <param name="factoryType">Type of factory</param>
        public ExternalDataSetProvider(string factoryType)
        {
            this.factoryType = factoryType;
            CreateFactory();
        }

        /// <summary>
        /// Deserialization constructor
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        protected ExternalDataSetProvider(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            factoryType = info.GetString("Factory");
            url = info.GetString("Url");
            CreateFactory();
        }

        #endregion

        #region ISerializable Members

        /// <summary>
        /// ISerializable interface implementation
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Factory", factoryType);
            info.AddValue("Url", url);
        }

        #endregion

        #region IChildrenObject Members

        IAssociatedObject[] IChildrenObject.Children
        {
            get { return children; }
        }

        #endregion

        #region Private Members

        /// <summary>
        /// Creates factory
        /// </summary>
        void CreateFactory()
        {
            Type t = Type.GetType(factoryType);
            if (t != null)
            {
                // Constructor of child object
                ConstructorInfo c = t.GetConstructor(new Type[0]);
                factory = c.Invoke(new object[0]) as IDataSetPoviderFactory;
                dataSet = factory.GetData(this.url);
                factory.Change += (string url) =>
                    {
                        if (this.url.Equals(url))
                        {
                            return;
                        }
                        this.url = url;
                        dataSet = factory.GetData(url);
                        change(dataSet);
                    };
                children[0] = factory as IAssociatedObject;
            }
        }

        #endregion

上面的代码需要一些注释。该类包含一个 IDataSetPoviderFactory 类型的子对象 factory。子对象与其父对象一起被序列化。它通过其类型名称创建。以下代码代表 IDataSetPoviderFactory 类型。

     /// <summary>
    /// Factory of data set provider
    /// </summary>
    public interface IDataSetPoviderFactory
    {
        /// <summary>
        /// Name
        /// </summary>
        string Name
        {
            get;
        }

        /// <summary>
        /// Gets data
        /// </summary>
        /// <param name="url">Url</param>
        /// <returns>Data</returns>
        DataSet GetData(string url);

        /// <summary>
        /// Change event
        /// </summary>
        event Action<string> Change;
    }

此类型的对象提供必要的数据集。以下代码提供了此接口的实现。

namespace Nasa.Ipac.Extragalactic
{
    /// <summary>
    /// Data Provider
    /// </summary>
    [Diagram.UI.Attributes.Url("http://ned.ipac.caltech.edu/forms/nearposn.html")]
    public class DataProvider : CategoryObject, IDataSetPoviderFactory, IUrlConsumer, IUrlProvider
    {
        #region Fields

        private event Action<string> changeData = (string url) => { };

        private DataSet dataSet = new DataSet();

        private string url = "";

        private Action<string> changeUrlConsumer = (string url) => { };

        private Action<string> changeUrlProvider = (string url) => { };

        #endregion

        #region IDataSetPoviderFactory Members

        string IDataSetPoviderFactory.Name
        {
            get { return "NASA/IPAC Extragalactic Database"; }
        }

        DataSet IDataSetPoviderFactory.GetData(string url)
        {
            UpdateData(url);
            return dataSet;
        }

        event Action<string> IDataSetPoviderFactory.Change
        {
            add { changeData += value; }
            remove { changeData -= value; }
        }

        #endregion

        #region IUrlConsumer Members

        string IUrlConsumer.Url
        {
            set
            {
                UpdateData(value);
            }
        }

        event Action<string> IUrlConsumer.Change
        {
            add { changeUrlConsumer += value; }
            remove { changeUrlConsumer -= value; }
        }

        #endregion

        #region IUrlProvider Members

        string IUrlProvider.Url
        {
            get { return url; }
        }

        event Action<string> IUrlProvider.Change
        {
            add { changeUrlProvider += value; }
            remove { changeUrlProvider -= value; }
        }

        #endregion

        #region Private Members

        void UpdateData(string url)
        {
            if (url == null)
            {
                return;
            }
            if (!url.Equals(this.url))
            {
                DataTable dt = url.GetDataTable();
                this.url = url;
                dataSet = new DataSet();
                dataSet.Tables.AddRange(new DataTable[] { dt });
                changeData(url);
            }
        }

        #endregion
    }
}
namespace Nasa.Ipac.Extragalactic.Data
{
    /// <summary>
    /// Gets data from NASA Astrogalactic website
    /// </summary>
    public static class StaticExtensionNasaIpacExtragalacticData
    {

        #region Public Members

        /// <summary>
        /// Gets data table from http://ned.ipac.caltech.edu/forms/nearposn.html
        /// </summary>
        /// <param name="url">Url</param>
        /// <returns>Data Table</returns>
        static public DataTable GetDataTable(this string url)
        {
            DataTable table = new DataTable();
            lock (ob)
            {
                /// Web request
                WebRequest req = WebRequest.Create(url);
                WebResponse resp = req.GetResponse();
                List<string> l = new List<string>();
                // reads data
                using (TextReader reader = new StreamReader(resp.GetResponseStream()))
                {
                    return reader.GetDataTable();
                }
            }
        }

        /// <summary>
        /// Gets data table from reader
        /// </summary>
        /// <param name="reader">Reader</param>
        /// <returns>Data table</returns>
        public static DataTable GetDataTable(this TextReader reader)
        {
            // Full code is contained in the source file
        }

        #endregion

    }
}

下图解释了与 NASA/IPAC 银河系外数据库的互操作性

NASA/IPAC Extragalactic DatabaseNASA/IPAC Extragalactic DatabaseNASA/IPAC Extragalactic Database

此示例需要附加文件。

4 插件的使用

4.1 业务逻辑

很明显,NASA/IPAC 银河系外数据库是一个非常特殊的组件。因此,它应该实现为 插件。我的软件的所有插件都已在 XML 文件中描述。

<?xml version="1.0" encoding="utf-8" ?>
<Root>
  <Assemblies>
    <Assembly file="Nasa.Ipac.Extragalactic.Data.dll"/>
    <Assembly file="Nasa.Ipac.Extragalactic.dll"/>
  </Assemblies>
  <Page pageName="Web databases" pageHint="Databaes obtained by Web">
    <Object icon="Containers\NED.ico"
        type="DataSetService.ExternalDataSetProvider,DataSetService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        param="Nasa.Ipac.Extragalactic.DataProvider,Nasa.Ipac.Extragalactic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        hint="Extragalactic" arrow="false" />
  </Page>
</Root>

上面的 XML 意味着应用程序应该加载以下附加库

  • Nasa.Ipac.Extragalactic.Data.dll
  • Nasa.Ipac.Extragalactic.dll

Page 标签负责以下选项卡页面。

Tab page

Object 标签负责一个按钮。icon 属性对应按钮的图标,type 属性对应对象的类型。param 属性对应一种对象类型。此类型的含义取决于上下文。此属性可由 反射 使用,如下所示。

                     if (kind.Length > 0) // Kind or additional parameter
                    {
                        // Searches constructor from string
                        ConstructorInfo ci = t.GetConstructor(new System.Type[] { typeof(string) });
                        if (ci != null)
                        {
                            // Creates an object
                            ICategoryObject ob = ci.Invoke(new object[] { kind }) as ICategoryObject;
                            return ob; // returns the object
                        }
                    }

ExternalDataSetProvider 具有以下字符串构造函数

         /// <summary>
        /// Constructor from type of child object
        /// </summary>
        /// <param name="factoryType">Type of factory</param>
        public ExternalDataSetProvider(string factoryType)
        {
            this.factoryType = factoryType;
            Type t = Type.GetType(factoryType);
            if (t != null)
            {
                // Constructor of child object
                ConstructorInfo c = t.GetConstructor(new Type[0]);
                factory = c.Invoke(new object[0]) as IDataSetPoviderFactory;
            }
         }

因此,以下 XML 代码

<Object
        type="DataSetService.ExternalDataSetProvider,DataSetService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        param="Nasa.Ipac.Extragalactic.DataProvider,Nasa.Ipac.Extragalactic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

意味着以下 C# 代码

new DataSetService.ExternalDataSetProvider(new Nasa.Ipac.Extragalactic.DataProvider())

但是,还有其他类型的含义,例如,如下所示。

         /// <summary>
        /// Creates object the corresponds to button
        /// </summary>
        /// <param name="button">The button</param>
        /// <returns>Created object</returns>
        public override ICategoryObject CreateObject(IPaletteButton button)
        {
            string kind = button.Kind; // Kind of the object
            Type type = button.ReflectionType;
            if (type.IsSubclassOf(typeof(Camera)))
            {
                return factory.NewCamera();
            }
            if (type.Equals(typeof(Motion6D.SerializablePosition)))
            {
                object ob = factory.CreateObject(kind);  // Usage of the kind
                if (ob != null)
                {
                    SerializablePosition pos = new SerializablePosition();
                    pos.Parameters = ob;
                    if (ob is IPositionObject)
                    {
                        IPositionObject po = ob as IPositionObject;
                        po.Position = pos;
                    }
                    return pos;
                }
            }
            return null;
        }

4.2 功能的自动扩展

如果您的计算机上安装了 Oracle 客户端,并且您想在我的框架中使用它。您只需将 OracleTableProvider.dll 放在应用程序目录中。排除 OracleTableProvider.dll 只会减少功能。以下函数搜索所有实现 IDataSetFactory 接口的对象

         /// <summary>
        /// Initialization of database drivers
        /// </summary>
        /// <param name="path">Path of drivers</param>
        void Initialize(string path)
        {
            // Gets all database drivers from this directory
            IEnumerable<IDataSetFactory> en = path.GetInterfaces<IDataSetFactory>();
            List<string> l = new List<string>();
            foreach (IDataSetFactory f in en)
            {
                factories[f.FactoryName] = f; // Dictinary of drivers
            }
            List<string> ln = new List<string>(factories.Keys);
            ln.Sort();
            names = ln.ToArray(); // Ordered names of drivers
        }

其中 GetInterfaces 如下所示。

         /// <summary>
        /// Gets interfaces
        /// </summary>
        /// <typeparam name="T">Interface type</typeparam>
        /// <param name="directory">Directory</param>
        /// <returns>Interface objects</returns>
        public static IEnumerable<T> GetInterfaces<T>(this string directory) where T : class
        {
            string[] fn = Directory.GetFiles(directory, "*.dll");   // Dll files
            Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies(); // Current domain assemblies
            List<string> l = new List<string>();
            foreach (Assembly a in ass) // Looking for objects in loaded assemblies
            {
                l.Add(a.Location);
                IEnumerable<T> en = a.GetInterfaces<T>();
                foreach (T t in en)
                {
                    yield return t;
                }

            }
            foreach (string f in fn) // Looking for objects in directory
            {
                if (!l.Contains(f))
                {
                   IEnumerable<T> en = Assembly.LoadFile(f).GetInterfaces<T>();
                   foreach (T t in en)
                   {
                       yield return t;
                   }
                }
            }
        }

        /// <summary>
        ///  Gets interfaces from assembly (Searching of driver assembly)
        /// </summary>
        /// <typeparam name="T">Interface type</typeparam>
        /// <param name="assembly">Assembly</param>
        /// <returns>Interface objects</returns>
        public static IEnumerable<T> GetInterfaces<T>(this Assembly assembly) where T : class
        {
            Type[] types = new Type[0];
            try
            {
                types = assembly.GetTypes(); // Assembly types
            }
            catch (Exception)
            {
            }
            string st = typeof(T).FullName;     // Interface full name
            foreach (Type t in types)
            {
                Type ti = t.GetInterface(st);   // Gets interface
                if (ti == null)
                {
                    continue;
                }
                FieldInfo fi = t.GetField("Singleton"); // Gets Singleton field
                if (fi != null)
                {
                    yield return fi.GetValue(null) as T; // yeild Singleton
                }
            }
        }

4.3 用户界面

尽管我的框架不知道 Nasa.Ipac.Extragalactic.DataProvider 类型,但我的框架提供了该对象的属性编辑器。用户界面包含两个选项卡页面。

Page 1Page 2

Data table 页面对应 ExternalDataSetProvider 类型的父对象。Data source 页面对应以下类的两个子对象。

namespace Nasa.Ipac.Extragalactic
{
    /// 
    /// Data Provider
    /// 
    [Diagram.UI.Attributes.Url("http://ned.ipac.caltech.edu/forms/nearposn.html")] // Initial url
    public class DataProvider : CategoryObject, IDataSetPoviderFactory, IUrlConsumer, IUrlProvider
    {
        // Implemetation ...
    }

[Diagram.UI.Attributes.Url("http://ned.ipac.caltech.edu/forms/nearposn.html")] 负责 Search 选项卡页面,IUrlConsumerIUrlProvider 接口负责 Result 选项卡页面。

Page 2Page 3

第一个页面负责常量 URL,第二个页面对应更改的 URL。以下代码代表子用户界面的创建。

             // Searching of additional control
            object o = factory.GetAdditionalFeature<IUrlConsumer>(provider as IAssociatedObject);
            if (o is Control) // Additional control
            {
                form = new FormExternalData(this.GetRootLabel(), o as Control);
                return;
            }

o 对象用作 FormExternalData 上的子选项卡页面。GetAdditionalFeature 函数如下所示。

         /// <summary>
        /// Gets additional feature
        /// </summary>
        /// <typeparam name="T">Feature type</typeparam>
        /// <param name="factory">User interface factory</param>
        /// <param name="obj">Obj</param>
        /// <returns>Feature</returns>
        static public object GetAdditionalFeature<T>(this IUIFactory factory, IAssociatedObject obj)
        {
            IUIFactory f = factory;
            IUIFactory p = factory.Parent;
            if (p != null)
            {
                f = p;
            }
           if (obj == null)
            {
                return null;
            }
            if (obj is T)
            {
                return f.GetAdditionalFeature<T>((T)obj);
            }
            if (obj is IChildrenObject) // If object has children
            {
                IAssociatedObject[] ao = (obj as IChildrenObject).Children;
                foreach (IAssociatedObject aa in ao) // Searches additional feature among children
                {
                    object ob = GetAdditionalFeature<T>(f, aa);
                    if (ob != null)
                    {
                        return ob;
                    }
                }
            }
            return null;
        } 

以下代码代表 IUIFactory 接口实现的 GetAdditionalFeature

         /// <summary>
        /// Gets additional feature
        /// </summary>
        /// <typeparam name="T">Feature type</typeparam>
        /// <param name="obj">Object</param>
        /// <returns>Feature</returns>
        public override object GetAdditionalFeature<T>(T obj)
        {
            if (!typeof(T).Equals(typeof(IUrlConsumer)))
            {
                return null;
            }
            IUrlConsumer c = obj as IUrlConsumer;
            UserControls.UserControlUrl uc = new UserControls.UserControlUrl();
            if (c is IUrlProvider)
            {
                uc.Set(c as IUrlProvider);
            }
            uc.Set(c);
            return uc;
        }

上面的代码意味着如果子对象实现了 IUrlConsumer 接口,则编辑器表单将包含一个 UserControlUrl 类型的控件。因此,用户界面是从接口自动构建的。

5. 低内聚

我的软件包含很多库。然而,这一事实促进了 低内聚,这一事实带来了很多好处。例如,Nasa.Ipac.Extragalactic.Data.dll 提供了数据集的创建,但这个库与我的框架完全独立。因此,这个库可以很容易地集成到其他应用程序中。Nasa.Ipac.Extragalactic.dllNasa.Ipac.Extragalactic.Data.dll 和我的框架之间的桥梁。内聚有许多级别。例如,在 我的一项项目 中,我使用了 地球大气从地面到太空的经验性全局模型 (NRLMSISE-00) 源代码。这段代码不是面向对象的。我开发了一个面向对象的包装器,因为我想同时使用多个大气计算副本。下表代表了 5 个内聚级别。

信号强度名称依赖项功能用法
1Msise C++ 类大气参数的计算任何支持 C++ 的平台
2MSISEAtmosphere.dll 管理的 C++ 项目.NET.NET 包装器(桥梁),用于 Msise任何支持 .NET 的平台
3DynamicAtmosphere.MSISE.dll C# 项目.NET,BaseTypes.dll先前项目的包装器,支持不同的 物理单位任何支持 .NET 的平台
4DynamicAtmosphere.MSISE.Wrapper.dll C# 项目.NET,我的框架我的框架与大气计算之间的桥梁我的框架
5DynamicAtmosphere.MSISE.Wrapper.UI.dll C# 项目.NET,System.Windows.Forms,我的框架System.Windows.Forms 用户界面,用于大气计算我的框架配备了 System.Windows.Forms 用户界面

您可以从我的 文章 下载此代码。

6 数据集消费者

6.1 数据集的统计选择

在我之前的文章中,我考虑了 统计学。数据集可以用作 统计数据集。我的软件使用两种统计分析方案,如下所述。

6.1.1 加载完整选择

此方案意味着一次性加载选择。

First scheme

处理器将 **计算出的参数** 与 **选择** 进行比较,计算残差,然后校正 **回归参数**。以下示例显示了数据集在非线性回归中的应用。

Regression

DataStatementWrapper 类型的对象,它实现了 IDataSetProvider 接口并封装了 SQL 查询Data 的属性如下所示。

Properties of data

此对象使用 SQL Server 并执行以下查询

SELECT x.x, y.y, z.z, f1.f1, f2.f2 FROM f1, f2, x, y, z WHERE x.Id = f1.Idx AND y.Id = f1.Idy AND z.Id = f1.Idz AND f1.Idx = f2.Idx AND f1.Idy = f2.Idy AND f1.Idz = f2.Idz

结果是它提供了一个数据集。SQL Server 数据集可以被 Oracle 数据集替换,如 上面 所示。DS 链接将 IDataSetProviderIDataSetConsumer 关联起来。SelectionDataSetSelection 类型的对象。此类型也实现了 IMeasurementsIStructuredSelectionCollectionIMeasurements信息流 的接口。Selection 对象的输出对应于数据表列。

Table outputs

该表包含 1000 行,所有 5 列均为 **Double** 类型,因此 **Selection** 返回五个 Double[1000]“类型”的对象。这些对象被 **Formula** 使用。

Formula

Formula 对象执行向量函数的 分量式 计算。IStructuredSelectionCollection统计领域 的接口。Selection 对象提供 5 个统计选择,对应于数据表的 5 列。Stat 箭头是 SelectionLink 类型的对象,此处 描述了该对象。箭头对应于统计领域。Regression 对象的属性如下所示。

Regression

这些属性具有以下含义。我们想找到 **Formula** 中 abcdg 的值,使得 Formula_1Formula_2 分别逼近选择 Table_f1Table_f2。下图代表了逼近前后的参数值。

Parameters

6.1.2 迭代方案

有时,前一种方案需要巨大的 随机存取存储器,因为它会同时加载所有数据。迭代方案分步迭代加载参数。

Iterative scheme

迭代器提供数据选择。y 是拟合方程的 **左侧**。**Transformation** 对应于非线性函数 f,并生成拟合模型的 **左侧**。**Processor** 协调所有操作并校正 **Regression parameters**。下图代表了这个模型。

Iterated model

Data 对象与 6.1.1 中的相同,Iterator 是以下 DataSetIterator 类型。

     /// <summary>
    /// Iterator obtained from data set
    /// </summary>
    [Serializable()]
    public class DataSetIterator : CategoryObject, ISerializable, IIterator, IDataSetConsumer, IMeasurements
    {
        // Code is contained in the source file
    }

此类型实现 IIterator 接口。

   /// <summary>
    /// Iterator
    /// </summary>
    public interface IIterator
    {
        /// <summary>
        /// Resets itself
        /// </summary>
        void Reset();

        /// <summary>
        /// Go to next and false otherwise
        /// </summary>
        /// <returns>True if has next and</returns>
        bool Next();
    }

该接口的实现如下所示。

         /// <summary>
        /// Reference to a table row
        /// </summary>
        protected DataRow[] rowreference = new DataRow[] { null };

        #region IIterator Members

        void IIterator.Reset()
        {
            current = 0;
            rowm[0] = table.Rows[0];
        }

        bool IIterator.Next()
        {
            if (table == null)
            {
                return false;
            }
            ++current;
            if (current >= table.Rows.Count)
            {
                return false;
            }
            row = table.Rows[current];
            rowreference[0] = row;
            return true;
        }

        #endregion

因此,该类逐行迭代数据表,并引用不同的行。行的引用实现为数组(此处)。引用用于通过以下方式实现 IMeasure 接口。

         /// <summary>
        /// Measurement from data row
        /// </summary>
        class RowMeasure : IMeasure
        {
            string name;
            object type;
            DataRow[] row;
            int ordinal;

            Func<object> par;

            internal RowMeasure(string name, object type, DataRow[] row, int ordinal)
            {
                this.name = name;
                this.type = type;
                this.row = row;
                this.ordinal = ordinal;
                par = Get;
            }

            #region IMeasure Members

            Func<object> IMeasure.Parameter
            {
                get { return par; }
            }

            string IMeasure.Name
            {
                get { return name; }
            }

            object IMeasure.Type
            {
                get { return type; }
            }

            #endregion

            #region Private Members

            object Get()
            {
                return row[0][ordinal];
            }

            #endregion

上面的代码意味着行中的元素被用作测量值。Iterator 作为 IMeasurement 被 **Formula** 使用,**Formula** 的属性如下所示。

Iterated formula

此示例的公式与 前一个示例的公式 一致,但参数类型不一致。这里 xyzDouble 类型的参数,在前一个示例中它们是 Double[1000]“类型”的参数。因此,这些公式返回两个 Double 类型的参数,之前的公式返回两个 Double[1000]“类型”的参数。**Processor** 对象的属性如下所示。

Processor

上图意味着我们想定义 Formulaabcdd 的值,使得 Formula_1(或 FormulaFormula_2)逼近 Iterator 的参数 f1(或 f2)。结果与 前一个示例 的结果一致,但它不需要大量的 随机存取存储器

6.2 2D 图表

下图包含赫罗图,取自 http://www.astro.uni-bonn.de/~deboer/sterne/hrdtxt.html

Diagram

上图与我的框架提供的图类似。

Star diagram

Data 执行到 Hipparcos 数据库的 SQL 查询。

Star SQL

Formula 提供必要的计算,特别是它计算恒星的 绝对星等ColorDrawSeries 类型的对象。Color 的属性如下所示。

Color properties

这意味着 2D 指示的 XY 坐标分别对应于 **Formula** 的 Formula_2Formula_1。指示点的颜色和大小由 **Formula** 的 Forlula_3、...、Forlula_6 定义。数据库可以被替换为 Internet 数据,下图显示了 NASA/IPAC 银河系外数据库的应用。

IPAC

但是,如果数据表包含大量行,则 Internet 数据库的应用会变得有问题。2D 指示图可以扩展如下。

Main diagram

现在我们除了指示外还有回归。FilerFormulaFilterIterator 类的对象,它实现了 IIterator。该对象用于数据过滤。该对象的属性如下。

Filter

过滤器条件是以下 3 个条件的 逻辑合取

  • x>ay 表示视差误差超过视差误差的三倍;
  • z< b 表示视差不超过 bb=1000);
  • |c + v - kz|< d 表示对象属于允许域,如下所示。

Admissible domain

属于允许域的恒星被近似为 3 次多项式,如下所示。

Star regression

Regression 提供必要的公式。

6.3 3D 图表 + 6D 运动学

下图配有电影,显示了具有 6D 运动学的 3D 图表。

Stars + 3D

StarsPositionCollectionData 类型的对象,该对象的属性如下所示。

3D Properties

这些属性与 2D 图表的属性 类似。其他组件提供以下操作。

6.4 数据库 + 数字图像处理 + 统计。

在我以前的文章中,我描述了 将 2D 图像用作统计数据集 的应用。与此数据集和数据库数据集的比较使我们能够估计物理参数。下图解释了这个样本。

此样本可用于 导航。任务算法如下。

Astro sensor

此方案的左侧包含图像处理。右侧代表星表的使用。桥梁比较两者,结果是我们获得了航天器姿态参数。让我们详细考虑。

6.4.1 图像处理

假设设备提供以下天体图像。

Stars

该图像包含干扰信息。我们需要进行过滤以排除它。非局部数字图像处理用于此目的。处理方案如下。

Filtration

该方案包含设备提供的 **初始图像**(源图像)。小的正方形提供了必要的数学运算。结果是我们得到了 **过滤后的图像**(过滤结果)。两张图片如下。

Stars Filtered

下图解释了过滤算法。

Non local

如果我们有 9 个连续的白色像素,我们将用一个黑色像素替换它们。所有其他像素都是白色的。过滤结果使我们能够获得黑色像素的 X 和 Y 坐标。然后将这些数字与星表进行比较。Stat 组件从 **Filtered image** 中提取这些数字。

6.4.2 数据库的应用

星表存储在数据库中。可以通过 SQL 查询提取必要的信息。

Query

查询语句如下。

++++++++++++++

SELECT RAdeg, DEdeg FROM hip_main WHERE RAdeg > @RAMIN AND RAdeg < @RAMAX AND DEdeg > @DEMIN AND DEDeg < @DEMAX AND BTmag > @BTMIN ORDER BY RAdeg

++++++++++++++

此语句的含义如下。首先,我们考虑一个有限的天空区域。 赤纬赤经 属于小区间。其次,我们只考虑那些 星等 超过给定常数的恒星(在本例中,常数为 9)。查询结果提供了以下图表。

Chart

我们想将此图表与过滤后的图像进行比较。此操作需要一组数学变换。完整变换方案如下。

Transformation

这些变换的基本特征是欧几里得变换。

Euclidean transformation

参数 a、b 和 Phi 是未知的。比较星表和过滤后的图像可以帮助我们确定这些参数。使用这些参数,我们可以确定航天器的姿态。

© . All rights reserved.