时间机器






4.91/5 (13投票s)
软件设计和开发的长期战略。
1. 引言
最近,一位航空设计师告诉我,他有很多想法。然而,他的同事们威胁要把他送进疯人院。这是俄罗斯移民的一个典型原因。我的同事们没有威胁要把我送进疯人院,他们认为我的想法很愚蠢。没有想法的人反而有最好的机会。这位设计师现在 60 岁了,他是一名过去的“设计师”。我不想有他那样的命运。我希望能实现我的想法。但我明白等待更好的时机不是一个好办法。上面图片中的男人回到了过去修复过去的错误。但现实不支持这种选择。过去的错误无法修复。所以我发明了“时间机器”,面向未来。我不知道我的未来。然而,我希望我未知的未来有好的软件。任何好的软件都应该支持变更模式,而变更模式范式可以被视为时间机器。然而,我的时间机器预测时间以十年为单位。最重要的是使用了范畴论作为主要思想。我会写很多关于它的内容。在本文中,描述了一些具体的想法。我发明时间机器有三个目的。除了时间机器本身,我还想给有才华的人一个例子。如果才华横溢的人没有机会实现他们的想法,那么他/她可以发明时间机器。此外,我还想展示一种良好的软件开发风格。2002 年,我从事3D 图形工作。我需要多窗口模式。事实上DirectX并不支持多窗口模式。所以我应该使用OpenGL。我发现与 OpenGL 的高耦合不是一个好主意。后来,我发现Autodesk 3ds Max可以使用 OpenGL 和 DirectX。我为未来开发了一个 3D 图形抽象层。很多年,我没有从事 3D 图形工作。2010 年,我恢复了 3D 图形开发,并实现了WPF对抽象 3D 图形级别的实现。我发现与 OpenGL 软件的高耦合无法适应其他 3D 图形技术。我现在不需要抽象层,但未来我会需要它。
2. 背景. 元驱动程序
数学包含理论层次。第一层算术包含关于整数的定理。第二层定理(元定理)是关于第一层定理的定理。第三个算术,第四个,等等。对偶定理(对偶定理)涉及定理,因此它是元定理。在本章中,将讨论元驱动程序。下面的代码可以看作是元驱动程序。这段代码正在寻找实现一个接口的对象的单例。
/// <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 = assembly.GetTypes(); // Assembly types
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
}
}
}
下面的代码片段包含在程序集中搜索单例。
/// <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 base directory
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <returns>Interfaces</returns>
public static IEnumerable<T> GetInterfacesFromBaseDirectory<T>() where T : class
{
return AppDomain.CurrentDomain.BaseDirectory.GetInterfaces<T>();
}
在许多情况下,只需要一个驱动程序。下面的代码包含查找单个驱动程序。
/// <summary>
/// Gets first interface object
/// </summary>
/// <typeparam name="T">Type of interface</typeparam>
/// <param name="directory">Directory</param>
/// <returns>First object</returns>
public static T GetFirstInterfaceObject<T>(this string directory) where T : class
{
IEnumerable<T> en = directory.GetInterfaces<T>();
foreach (T t in en)
{
return t;
}
return null;
}
/// <summary>
/// Gets first interface object
/// </summary>
/// <typeparam name="T">Type of interface</typeparam>
/// <returns>First object</returns>
public static T GetFirstInterfaceObjectFromBaseDirectory<T>() where T : class
{
return AppDomain.CurrentDomain.BaseDirectory.GetFirstInterfaceObject<T>();
}
上述代码的使用说明非常简单。动态链接库文件(文件)应复制到当前域的基本目录。
3 应用
3D 图形
以下代码表示通用的 3D 图形接口。
/// <summary>
/// Factory of 3D objects and cameras
/// </summary>
public interface IPositionObjectFactory
{
/// <summary>
/// Creates 3D object
/// </summary>
/// <param name="type">Object's type</param>
/// <returns>The object</returns>
object CreateObject(string type);
/// <summary>
/// Creates new camera
/// </summary>
/// <returns></returns>
Camera NewCamera();
/// <summary>
/// Creates editor of properties of camera
/// </summary>
/// <param name="camera">The camera</param>
/// <returns>The property editor</returns>
object CreateForm(Camera camera);
/// <summary>
/// Creates editor of properties of visible object
/// </summary>
/// <param name="position">Position of object</param>
/// <param name="visible">Visible object</param>
/// <returns>Editor of properties of visible object</returns>
object CreateForm(IPosition position, IVisible visible);
/// <summary>
/// Type of camera
/// </summary>
Type CameraType
{
get;
}
/// <summary>
/// Creates label on desktop
/// </summary>
/// <param name="obj">Object for label</param>
/// <returns>The label</returns>
IObjectLabel CreateLabel(object obj);
/// <summary>
/// Creates label of visible object
/// </summary>
/// <param name="position">Position of object</param>
/// <param name="visible">Visible object</param>
/// <returns>Editor of properties of visible object</returns>
object CreateLabel(IPosition position, IVisible visible);
该接口的 OpenGL 和 WPF 实现正在开发中。下面的类图表示这些实现。
每个实现类都包含 `Singleton` 字段,如下所示。
/// <summary>
/// WPF implementation of 3D Graphics
/// </summary>
public class WpfFactory : PositionObjectFactory
{
#region Fields
/// <summary>
/// Singleton
/// </summary>
public static readonly WpfFactory Singleton =
new WpfFactory();
#endregion
#region Ctor
/// <summary>
/// Private default constructor
/// </summary>
private WpfFactory()
{
}
#endregion
这两个实现都包含在不同的动态链接库中。
N | 技术栈 | 实现类 | 动态链接库文件 |
1 | OpenGL | OpenGLFactory | InterfaceOpenGL.dll |
2 | WPF | WpfFactory | WpfInterface.UI.dll |
下面的代码片段表示 3D 图形的接口选择。
/// <summary>
/// Global factory
/// </summary>
public static IPositionObjectFactory BaseFactory
{
get
{
if (baseFactory == null)
{
baseFactory =
AssemblyService.StaticExtensionAssemblyService.
GetFirstInterfaceObjectFromBaseDirectory<IPositionObjectFactory>();
}
return baseFactory;
}
}
因此,如果我们想使用 OpenGL 技术,那么 InterfaceOpenGL.dll 文件应该被复制到基本目录,等等。更多关于 3D 图形应用的信息包含在这篇关于虚拟现实的文章中。
3.2 科学数据
3.2.1 科学数据处理
数据库可以是一个很好的科学数据仓库。否则,软件用户应该有一个好的数据访问接口。让我们看一个示例,包括数据访问和数据处理。在这个示例中,我们有两个非线性函数 f1、f2。
这些函数的参数 a
、b
、c
、d
、g
是未知的。函数的值以及变量 x
、y
、z
存储在具有以下架构的数据库中。
带有“id
”前缀的参数是变量值的 id。数据库参数的含义很清楚。下面的 SQL 查询提供了变量和函数值的检索。
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
现在我们来考虑这个任务的完整解决方案。下图包含了这项任务的组成部分。
.
Data 组件执行数据库查询。Data 的属性如下所示。
此组件的属性很清楚。使用了 SQL Server 驱动程序。其他属性是连接字符串和SQL 查询。Iterator 组件可以看作是游标。Formula 组件包含 f1、f2 的定义。Formula 的属性如下所示。
公式的参数 a
、b
、c
、d
、g
是常数。变量 x
、y
、z
对应于从数据库检索到的实数。Processor 组件执行上述常数的定义。Processor 的属性如下所示。
上图的左侧面板表示我们希望定义Formula组件的常数a
、b
、c
、d
、g
。右侧面板表示我们希望用Formula的Formula_1
和Formula_2
来近似从数据库检索到的f1、f2。下图的左右部分包含先验值和后验值。
3.2.2 数据库驱动程序
在上述示例中,使用了 SQL Server。很明显,科学家也可以出于同样的目的使用 Oracle。软件应该支持以下选项。
驱动程序应包含在不同的程序集中。低耦合的软件应将Oracle驱动程序与SQL Server驱动程序分开,因为好的软件应该支持以下选项。
- 支持 Oracle
- 支持 SQL Server
- 支持 Oracle 和 SQL Server
- 不支持任何数据库
因此,软件应包含抽象数据库层。以下代码代表了这个抽象层。
/// <summary>
/// Factory for creating data set metadata 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;
}
/// <summary>
/// Creates type from metadata row
/// </summary>
/// <param name="row">The metadata row</param>
/// <returns>The type</returns>
Type GetTypeFromRow(DataRow row);
/// <summary>
/// Table name in metadata
/// </summary>
string TableName
{
get;
}
/// <summary>
/// Column name in metadata
/// </summary>
string ColumnName
{
get;
}
/// <summary>
/// Is nullable in metadata
/// </summary>
string IsNullable
{
get;
}
/// <summary>
/// Gets string representation of type
/// </summary>
/// <param name="type">The type</param>
/// <returns>The string representation</returns>
string GetStringFromType(Type type);
/// <summary>
/// Generates statement from desktop
/// </summary>
/// <param name="desktop">The desktop</param>
/// <returns>The statement</returns>
string GenerateStatement(IDataSetDesktop desktop);
/// <summary>
/// Gets object type
/// </summary>
/// <param name="column">Column</param>
/// <returns>The type</returns>
object GetObjectType(DataColumn column);
/// <summary>
/// Generates script
/// </summary>
/// <param name="column">Column</param>
/// <returns>Script</returns>
string GenerateScript(IColumn column);
/// <summary>
/// Generates create script
/// </summary>
/// <param name="table">Table</param>
/// <returns>Script lines</returns>
List<string> GenerateScript(ITable table);
/// <summary>
/// Generates creation script from metadata
/// </summary>
/// <param name="metaData">Meatadata data set</param>
/// <returns>Creation script</returns>
List<string> GenerateScript(DataSet metaData);
}
数据库驱动程序的列表可以扩展。现在已经开发了 ODBC、SQL Server 和 Oracle 驱动程序。下图表示这些驱动程序的类图。
以下代码表示这些驱动程序的使用。
/// <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
}
如果使用 Oracle 驱动程序而不是 SQL Server 驱动程序,那么Data的属性将按以下方式更改。
在上图中,驱动程序和连接字符串已更改。在某些情况下,需要更改查询。
3.3 其他示例
示例列表可以扩展。但是,总体思路已经很清楚了。其他示例将在未来的特定文章中描述。
关注点
我不知道什么更有用,是我的代码还是你即使被威胁要进疯人院也能激发你做好工作的灵感。