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

模型导向编程语言应该是什么样子?

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (18投票s)

2014年10月13日

CPOL

25分钟阅读

viewsIcon

24810

downloadIcon

206

考虑模型导向编程语言在有效的模型导向开发中应具备的各种特性。

引言

模型导向开发 (MOD) 是一种技术,允许您在开发过程的任何时候利用**模型**进行开发。您可以将模型信息**转换**为源代码和/或在开发过程中直接利用模型。这并不意味着任何特定的过程或技术。如何表示和利用模型完全由您决定。

模型导向编程 (MOP) 涉及将模型中的信息转换为代码或您需要的任何材料的过程。为了以最有效的方式实现这一点,我相信您需要一个好的**模型导向编程语言**。

建模就通过图表(或实现此目的的工具)等可视化构造创建和维护模型数据的过程而言,本文不作讨论。本文将从纯编程的角度探讨一些建模。

在本文中,我将阐述我认为一个好的**模型导向编程语言**应具备的必要特性,以便开发人员能够通过 MOD 取得更多成就。我希望引发讨论,并乐意听取您对本文提出的概念和材料的意见。我希望看到 MOD 随着一套良好的支持编程实践和语言而发展。

背景

模型导向语言代码示例将采用 Mo+,因为 Mo+ 语言实现了本文中概述的几乎所有概念。 **Mo+** 模型导向编程语言和 **Mo+ Solution Builder** 模型导向开发 IDE 在这篇Code Project 文章中介绍。 如果您想学习如何使用 Mo+,请参阅该文章及相关资源链接以获取更多信息。 请注意,Mo+ 代码不会在代码块中显示,因为 Code Project 中没有针对该语言的类似代码块语法识别。

模型结构将显示为 UML 类图,其中

  • 类代表模型结构中的对象
  • 成员代表模型结构中对象的属性
  • 有向关联代表模型结构中的父子关系

面向对象代码示例将采用 C#

使用解耦模型的 MOP

我相信模型导向编程(MOP)一词最初是由 Timothy Lethbridge 博士提出的。他所定义并在 UMPLE 中实现的 MOP 暗示您的模型与代码在文本-图表二元性中高度耦合。

我将在这篇文章中修改 MOP 的定义,认为模型可以与您的代码解耦,并且模型导向编程语言可以以您需要的任何方式弥合模型与代码(和其他材料)之间的鸿沟。我相信将模型与代码解耦对模型导向开发具有一些关键优势。

  • 工作可扩展性 - 直接耦合到代码的模型意味着模型中的单个元素对应于代码中的单个元素(例如,类图中的一个类对应于业务逻辑层的一个类)。这不具备可扩展性。要从模型中获得更多代码,您必须增加模型的规模。如果模型与代码解耦,您可以从模型中的单个元素生成多个甚至数十个代码元素。您可以决定通过模型导向编程生成多少种代码或其他材料,以及它们的种类。
  • 建模灵活性 - 直接与代码耦合的模型意味着它是一个设计级别的模型。您可以选择在设计级别进行详细建模,但这不是必须的!就个人而言,我发现通过梳理数据和工作流以巩固需求来建模能获得最大收益,然后直接编程会更容易。如果模型信息来源(例如数据库)已存在,您就不必进行任何建模。无论如何,我认为您应该可以自由地进行适合您的建模。通过解耦模型,您可以自由地通过模型导向编程做出设计决策。
  • 编程能力 - 与代码解耦的模型将弥合差距的更多责任放在模型导向编程语言上。需要一种更强大、更开放的语言来创建您的代码和材料,拥有这样的语言将增强您作为模型导向程序员的能力。

下面概述的模型导向语言特性将基于模型与代码解耦的假设。 我欢迎您就 MOP 以及耦合模型与解耦模型的概念提出意见。请阅读这篇 您已经是模型导向开发者 文章,以获取有关耦合与解耦 MOD 的更多解释。

模型在结构和数据方面

最后,在我们讨论模型导向编程语言的特性之前,我们应该从结构和数据方面考虑模型。

  • 模型结构 - 模型结构定义了相应模型可以组织和构建的“规则”或模式。例如,在通用实体关系模型中,模型结构将包括诸如 **实体**、**关系**和 **属性**等构造。
  • 模型数据 - 模型数据定义了模型结构的特定实例,并为特定目的赋予模型具体的含义。例如,在通用实体关系模型中,模型数据可以包括**实体**的实例,例如**客户**或**订单**,以及**属性**的实例,例如**名字**(对于**客户**)和**订单总额**(对于**订单**)。

任何类型的模型(UML、ORM等),无论是简单还是复杂,都可以用模型结构和模型数据来描述。 阅读这篇有效利用模型结构和数据进行模型导向开发文章以了解更多详细信息。

模型结构示例

下图表示一个实体关系模型结构的简单部分,我们将在本文的其余部分使用它

此模型结构的规则非常简单。

  • 一个 解决方案 是容纳所有模型信息的容器,由 SolutionName 描述。
  • 一个 特性 是一个用于组织额外模型信息的分组,由 FeatureName 描述。
  • 一个 实体 是一种对象或构建块,由一个 EntityName 描述。
  • 一个 属性 是用于进一步描述一个 实体 的属性,由一个 PropertyName 描述。
  • 一个 解决方案 可以包含零个或多个 特性
  • 一个 Feature 可以包含零个或多个 Entity 项。
  • 一个 实体 可以包含零个或多个 属性 项。

我们还将在面向对象代码示例中利用此模型结构。 以下是表示为简单 C# 类的模型结构:

    public class Solution
    {
        public string SolutionName { get; set; }
        public List<Feature> Features { get; set; }
    }

    public class Feature
    {
        public string FeatureName { get; set; }
        public List<Entity> Entities { get; set; }
    }

    public class Entity
    {
        public string EntityName { get; set; }
        public List<Property> Properties { get; set; }
    }

    public class Property
    {
        public string PropertyName { get; set; }
    }

模型数据示例

我们将用一些项目填充此模型,以利用模型数据。

  • Solution: SolutionName = "我的解决方案"
    • FeatureFeatureName = "客户"
      • Entity: EntityName = "客户"
        • Property: PropertyName = "FirstName"
        • PropertyPropertyName = "LastName"
      • EntityEntityName = "订单"
        • PropertyPropertyName = "订单总额"
        • PropertyPropertyName = "产品名称"
    • FeatureFeatureName = "产品"
      • EntityEntityName = "产品"
        • PropertyPropertyName = "产品名称"
        • PropertyPropertyName = "价格"
      • EntityEntityName = "类别"
        • PropertyPropertyName = "类别名称"

模型导向编程语言的特性

模型导向编程语言是让您能够采用模型导向编程 (MOP) 以实现高效模型导向开发 (MOD) 的关键。最终,您需要能够充分利用模型来创建所需的基于模型的代码和材料。那么,这样的语言应该是什么样子呢?

以下是我认为一个好的模型导向编程语言必不可少的几个特性:

  • 全面了解模型结构
  • 完全访问模型数据
  • 能够利用模型结构来简化语言
  • 与面向对象语言的相似性
  • 更新模型结构和数据的能力
  • 有效生成代码和材料的能力
  • 有效管理代码和材料的能力

这些特性中的每一个都将在下面的单独部分中出现,我们将通过示例深入探讨。如果您能想到模型导向编程应该具备的其他特性,请在下方评论!

全面了解模型结构

模型导向语言需要全面了解模型结构的所有元素,否则您将无法对模型做太多事情。该语言应提供带有模型结构所有元素语法识别的关键字。

下面的示例显示了 Mo+ 关键字语法识别我们的模型元素(青色文本)和不在模型中的其他元素(棕色文本)。

其他事物
解决方案
解决方案.解决方案名称
解决方案.其他事物
功能
功能.功能名称
功能.其他事物
实体
实体.实体名称
实体.其他事物
属性
属性.属性名称
属性.其他事物

模型导向语言还需要知道模型结构中的每个元素如何以父/子或其他关系与其他元素结合。 以我们的模型结构为例,语言需要知道 SolutionName 是描述 Solution 的元素。

完全访问模型数据

模型导向语言需要能够让您以多种可想象的方式访问模型数据。 以下是我们为了使用模型数据而需要访问模型数据的几种不同方式。

一般浏览

模型导向语言应该能够以一般方式迭代和浏览模型结构中每种元素的模型数据。 以下 Mo+ 示例展示了使用 foreach 语句浏览解决方案中的所有功能,然后浏览解决方案中的所有实体(Mo+ 语法中出现的其他功能将在稍后解释)。

foreach (Feature)
{
    trace(FeatureName)
}
foreach (Entity in Solution)
{
    trace(EntityName)
}

访问子数据

模型导向语言应该能够获取给定父实例的所有子模型数据。 以下 Mo+ 示例展示了浏览所有特性,并对于每个特性,浏览该特性的子实体。

foreach (Feature)
{
    foreach (Entity)
    {
        trace(EntityName)
    }
}

访问父数据

模型导向语言应该 能够获取给定子实例的所有父数据(一直到父树的顶端)。 以下 Mo+ 示例展示了浏览所有实体并获取每个实体的父特性名称。

foreach (Entity in Solution)
{
    trace(FeatureName)
}

搜索数据

模型导向语言应允许您对模型中的任何元素进行通用搜索,以根据条件查找单个或多个匹配项。 Mo+ 具有执行此类搜索的初级 Find FindAll 方法。 下面,执行 Find 单个 (第一个)匹配搜索,以处理名为“Customers”的特性。

with (Feature from Solution.Find(FeatureName, "Customers"))
{
    trace(FeatureName)
}

过滤数据

一个很好的功能是能够在浏览或搜索模型数据时简单地进行过滤。 以下 Mo+ 示例展示了遍历实体并使用 where 子句只处理以“C”开头的实体。

foreach (Entity in Solution where EntityName.StartsWith("C") == true)
{
    trace(EntityName)
}

能够利用模型结构来简化语言

我相信,如果我们审视模型结构本身的普遍特性,我们可以利用这些特性来简化我们的模型导向语言。 考虑以下 C# 代码来访问某些模型数据:

            Solution solution = Helper.GetSolution();

            foreach (Feature feature in solution.Features)
            {
                foreach (Entity entity in feature.Entities)
                {
                    Console.WriteLine(feature.FeatureName + "." + entity.EntityName);
                }
            }

现在考虑等效的 Mo+ 代码:

foreach (Feature)
{
    foreach (Entity)
    {
        trace(FeatureName + "." + EntityName)
    }
}

请注意,在 foreach 循环中无需定义和使用变量来跟踪当前元素。

模型结构有一个根

我相信模型结构有一个(或者应该有一个)顶层根或容器,所有模型数据都可以放置在其中。在我们的示例模型结构中,根是 Solution

我认为在模型结构中拥有一个真实或占位符的根不会对潜在的模型结构施加任何真正的约束。你可以在整个结构中拥有一大堆定制的模型(ER 模型、领域模型、用户模型、硬件模型等),它们都只与你的根相关联。

如果模型结构有一个根的假设成立,那么我们就不必显式加载模型数据,我们可以让解释器来完成。因此,C# 示例中加载解决方案的语句(例如 Helper.GetSolution() )是不必要的,所有模型数据都应该隐式可用。

模型结构中的元素是父子关系的一部分

我相信模型结构中的所有元素(除了根元素)都是某个其他元素的子元素。因此,模型结构中的所有元素都是父子关系的一部分。模型结构中的父子关系可以非常浅(直接到根),也可以非常深或更复杂。

为了进一步说明这一信念,请考虑以下 UML 类图结构中非常简化的部分(我并非试图在此重现 UML 元模型!):

模型通常需要以图表形式出现,以作为视觉和沟通辅助。如果模型元素没有父子关系,我们如何对它们进行图表化?我们可以将类、成员、关联和注释等放到类图中,因为所有这些元素都直接或间接地是 _ClassDiagram_ 的子元素。

当然,一个模型元素可以有多个子元素(`ClassDiagram` 有子 `Class` 和 `Annotation`),一个元素可以有多个父关联到相同或不同的元素(`Association` 有一个父源 `Class` 和一个父目标 `Class`)。

如果模型元素是父子关系的一部分的假设成立,那么我们可以对模型导向语言进行一些简化。

  • 隐式访问子级 - 我们可以隐式访问给定父元素实例的子元素数据。 回到上面的示例代码,我们不再需要像 C# 中那样使用变量和成员显式迭代解决方案特性(foreach (Feature feature in solution.Features)),而是可以在模型导向语言中隐式迭代特性(foreach (Feature))。
  • 隐式访问父级 - 与子元素数据类似,我们可以隐式访问给定子元素的父数据。 我们不再需要像 C# 那样使用变量显式获取父特性名称(feature.FeatureName),而是可以在模型导向语言中隐式获取父特性名称(Feature.FeatureName 或仅 FeatureName)。

与面向对象语言的相似性

我相信模型导向编程语言应该类似于面向对象语言,但专门适用于支持 MOD。如果面向对象语言处理对象结构和实例,那么模型导向语言应该以类似的方式处理模型结构和实例。面向对象语言以有效封装数据和规则,以及比过程式语言更强大的解决复杂问题的能力而闻名。

模型导向属性

模型导向语言应该具有类似于面向对象属性的属性。 考虑我们模型中 C# 表示中的更新的 Entity 类,其中包含一个名为 FullName 的属性:

    public class Entity
    {
        public string EntityName { get; set; }
        public List<Property> Properties { get; set; }

        public Feature Feature { get; set; }
        public string FullName
        {
            get
            {
                return Feature.FeatureName + "." + EntityName;
            }
        }
    }

我们应该能够在我们的模型导向语言中定义和使用这样的属性。 在 Mo+ 中,属性本质上是作为模板存储的代码块(Mo+ 中的所有模板,无论是否用作属性,本质上都是公共的)。 模板具有名称并与模型中特定类型的元素相关联。 Mo+ 中 FullName 属性的等效代码是名为 FullName Entity 模板,其内容代码如下:

<%%=FeatureName + ".+ EntityName%%>

特殊的 <%%=%%> 标签用于从模型数据中发出内容。然后,这个 FullName Entity 模板可以像任何内置属性一样使用,包括在表达式内部。

foreach (Entity in Solution where FullName.Contains("Customer"))
{
    trace(FullName)
}

模型导向方法

与属性类似,模型导向语言应该具有类似于面向对象方法的方法。 考虑我们模型中 C# 表示中的更新的 Entity 类,其中包含一个名为 GetExtendedFullName的方法:

    public class Entity
    {
        public string EntityName { get; set; }
        public List<Property> Properties { get; set; }

        public Feature Feature { get; set; }
        public string FullName
        {
            get
            {
                return Feature.FeatureName + "." + EntityName;
            }
        }

        public string GetExtendedFullName(string tag)
        {
            return tag + "." + FullName;
        }
    }

我们也应该能够在我们的模型导向语言中定义和使用这样的方法。 Mo+ 对类似方法的调用有初步支持。 方法也是作为模板存储的代码块,但也具有额外的参数。 以下是 GetExtendedFullName Entity 模板的代码:

<%%:
param tag
<%%=tag + ".+ FullName%%>
%%>

GetExtendedFullName Entity 模板也可以像任何内置属性一样使用,只是在调用方法时需要传递参数值:

foreach (Entity in Solution where GetExtendedFullName(tag="ModelOriented").Contains("Customer"))
{
    trace(GetExtendedFullName(tag="ModelOriented"))
}

模型上下文

让我们再看一下我们的 Entity 类 C# 示例:

    public class Entity
    {
        public string EntityName { get; set; }
        public List<Property> Properties { get; set; }

        public Feature Feature { get; set; }
        public string FullName
        {
            get
            {
                return Feature.FeatureName + "." + EntityName;
            }
        }

        public string GetExtendedFullName(string tag)
        {
            return tag + "." + FullName;
        }
    }

当我们在非静态成员体内时,我们可以直接访问其他非静态成员,并且数据基于给定对象实例的上下文。例如,如果我们的 Entity 对象实例是“Customer”,我们可以在非静态成员内部的任何地方使用 FullName 属性,并且会得到“Customers.Customer”的值。

我相信模型导向语言也应该以这种方式实现基于上下文的属性和方法访问,同时利用模型结构的特性来简化或增强其实现方式。我称此功能为**模型上下文**。

Mo+ 将模型上下文实现为一个堆栈,具有以下特点:

  • 基于模板 - Mo+ 代码块存储为模板,其中特定模板属于某种模型元素类型(例如,本例中的 Solution、 Entity、 Property)。 当解释器调用模板时,它会将模型数据的一个实例作为堆栈中的单个元素传递。 模型上下文堆栈的作用域在模板调用内部。
  • 堆栈上的项目是模型数据的实例 - 堆栈上的每个项目都是模型数据的实例。堆栈上可以(而且通常有)不同模型元素类型的实例。
  • 当前上下文 - 当前上下文或项目是堆栈底部的那个。
  • 压入和弹出模型数据 - 某些语句,例如 foreach with,在其语句作用域内将模型数据实例压入堆栈。在语句块结束时,该实例会自动弹出。
  • 向上访问堆栈 - 您可以使用 ../ 表示法(../../ 等)访问堆栈中当前上下文上方的实例。

现在考虑下面的 Mo+ 代码块:

foreach (Entity)
{
    trace(FullName)
    foreach (Feature in Solution)
    {
        trace(FeatureName)
        trace(../FeatureName)
    }
    with (Feature from Solution.Find(FeatureName, "Customers"))
    {
        trace(SolutionName + "." + FeatureName)
    }
}

让我们逐步检查模型上下文的发生方式以及我们如何利用它。

  • 首先,假设这个代码块位于一个 Solution 级别的模板中。如果是这样,最初堆栈包含一个项,即我们名为“My Solution”的 Solution。我们的当前上下文从这个解决方案开始。
  • foreach (Entity) 语句处,该语句迭代当前上下文(解决方案)中的所有实体,发生以下情况:
    • 迭代中的当前 Entity (例如“Customer”)被压入堆栈并成为当前上下文。通过这样做,我们不需要临时变量来存储当前实体。
    • foreach 语句块的末尾,当前 Entity 从堆栈中弹出。
  • trace(FullName) 语句处,发生以下情况:
    • 当前上下文是一个 Entity (例如“Customer”),所以我们可以直接访问任何与 Entity 相关的内容。我们选择调用名为 FullName Entity 级别模板。
    • 在调用 FullName 时,解释器将当前上下文(Entity)传递给堆栈以开始处理该模板调用。该模板可以对其上下文堆栈进行任何操作,而不会影响此模板的堆栈。
  • foreach (Feature in Solution) 语句中,我们正在迭代解决方案中的每个功能,并在该 foreach 语句块的范围内将一个 Feature 压入(和弹出)堆栈。请注意,如果此处我们没有添加 in Solution,我们将在当前上下文(Entity)中查找所有功能,由于 Feature 不是 Entity 的子级,因此不会找到任何功能。
  • 在 trace(FeatureName) 语句处,当前上下文是一个 Feature (例如“Customers”),我们可以访问任何与 Feature 相关的内容。
  • trace(../FeatureName) 语句中,我们访问堆栈上层的一个项,它是我们第一个 foreach 语句中的一个 Entity。我们选择获取该实体的特征名称,这可能与当前上下文特征的名称不同。
  • with (Feature from Solution.Find(FeatureName, "Customers")) 语句处,发生以下情况:
    • 执行搜索以匹配名为“Customers”的单个 Feature,此 Feature 被压入堆栈并成为当前上下文。同样,不需要临时变量来存储当前功能。
    • 在 with 语句块的末尾,当前 Feature 从堆栈中弹出。
  • trace(SolutionName + "." + FeatureName) 语句中,当前上下文是名为“Customers”的 Feature,并且我们可以访问任何与 Feature 相关的内容。
  • 最后,在这些语句结束时,堆栈中应该只有一个项,即最初调用模板时传入的 Solution (“My Solution”)。在代码块内,堆栈永远不应该为空。

以下是在 C# 中执行此操作的类似代码。值得注意的差异包括:需要显式变量来跟踪循环中的上下文,需要在模型中更高层次上显式管理单独的列表(在解决方案级别需要一个显式的实体列表),以及需要显式管理对父信息的引用(需要从 Entity Feature 的显式引用)。

            foreach (Entity entity in solution.Entities)
            {
                Console.WriteLine(entity.FullName);
                foreach (Feature feature in solution.Features)
                {
                    Console.WriteLine(feature.FeatureName);
                    Console.WriteLine(entity.Feature.FeatureName);
                }
                foreach (Feature feature in solution.Features.Where(i => i.FeatureName == "Customers"))
                {
                    Console.WriteLine(solution.SolutionName + "." + feature.FeatureName);
                }
            }

其他相似之处

您认为模型导向语言还应该与面向对象语言有哪些其他相似之处?

更新模型结构和数据的能力

模型导向语言还应该让您能够修改模型结构和数据。

更新模型数据

首先,我们将讨论模型数据。到目前为止,我们只谈论了读取模型数据。但是,我认为模型导向语言也能够写入和更新模型数据是很重要的。如果您想从其他地方导入模型数据,这将尤为重要,届时您的模型导向语言将大大减少您的建模工作。

Mo+ 具有一种基本但功能强大的添加和更新模型数据的方法。写入模型数据仅允许在规范模板的输出区域中进行。对于模型结构中给定元素的当前实例,您可以创建新实例(使用 New() 方法),将其添加到模型中(使用 add 语句),和/或仅更新该实例的属性。

CurrentProperty = New()
CurrentProperty.PropertyName = "My Property"
add(CurrentProperty)
CurrentProperty.PropertyName = "My Updated Property"

更新模型结构

那模型结构呢? 大多数模型导向程序员可能只需要处理 UML 或 ER 结构等标准结构,但模型导向语言应赋予您创建和使用自己的结构或在需要时添加到现有结构的能力。

例如,假设我们想向模型结构中添加一个名为 Block 的对象,并带有一个名为 BlockName 的属性。模型导向语言应该能够让您添加任何您想要的附加结构,并且应该为每个您创建的元素提供具有语法识别的关键字。

Mo+ 具有一种初步(也许有些复杂)的创建您选择的任何模型结构的方法。 与更新模型数据一样,这是在规范模板的输出区域中完成的。 以下 Mo+ 代码显示了添加 Block BlockName 的一些步骤。

CurrentModelObject = New()
CurrentModelObject.ModelObjectName = "Block"
add(CurrentModelObject)
var blockObjectID = CurrentModelObject.ModelObjectID

CurrentModelProperty = New()
CurrentModelProperty.ModelPropertyName = "BlockName"
CurrentModelProperty.ModelObjectID = blockObjectID
add(CurrentModelProperty)

然后,Block BlockName 成为模型结构的已识别部分,可以与其他模型元素以相同的方式使用。

foreach (Block)
{
    trace(BlockName)
}

阅读这篇 有效利用模型结构和数据进行模型导向开发 文章,了解更多关于利用自定义模型结构的详细信息。

有效生成代码和材料的能力

目前我假设 MOD 及其支持的模型导向语言的主要目的是您需要从模型中生成源代码和其他材料。

为了生成代码和其他材料,您通常需要将模型数据与其他信息合并,以将模型转换为其他形式。因此,我相信模型导向语言应该具有某种模板机制,允许您合并这些数据。

Mo+ 模板支持标签,允许您区分处理、原始内容和模型导向内容。 Mo+ 标签以 <%%:(用于处理)、 <%%-(用于原始内容)和 <%%=(用于模型导向内容)开头,并以 %%> 结尾。 请看以下示例(我们将其称为 ExampleReport,一个 Solution 级别的模板):

<%%:
foreach (Entity)
{
    <%%-
    此实体的完整名称是 %%><%%=FullName%%><%%-.%%>
}
%%>

除了语句(蓝色)、模型元素(青色)和模板(棕色)的语法识别外,Mo+ 将原始内容识别为橙色文本。调用此模板时,其内容如下:

    The full name for this entity is Customers.Customer.
    The full name for this entity is Customers.Order.
    The full name for this entity is Products.Category.
    The full name for this entity is Products.Product.

有效管理代码和材料的能力

实际管理您的代码和材料不仅仅是简单地生成它们。 大多数生成代码的模型导向方法都是盲目地进行,每次更新时都重新生成所有内容,并且几乎不给您控制文件放置位置。 这在大型团队环境中不是很有用。 模型导向语言应该让您完全控制何时、何地以及如何更新您的代码和材料。

Mo+ 模板有一个输出部分,它不仅为您提供了完全控制,而且允许您以模型导向的方式进行控制。回到我们上面的 ExampleReport 模板,考虑以下代码来确定此模板的输出:

<%%:
var path = "c:\\temp\\ExampleReport.txt"
if (File(path) == null)
{
    update(path)
}
%%>

基本上,这意味着如果 path 位置没有文件,则创建它(使用 update 语句),其中包含此模板的内容(如上所示)。如果文件已存在,则不会重新生成。显然,您更新生成文件的实际条件通常不止于此。

这使得它成为模型导向的原因是,这个模板掌控着自己的命运。 另一个模板可以选择通过如下语句诱导 ExampleReport 输出一些东西:

<%%>ExampleReport%%>

即便如此,除非满足条件,否则 ExampleReport 不会更新其报告文件。以这种方式管理输出在大规模上变得非常有效和可维护。

附加语言特性

以下是我认为一个好的模型导向语言应该具备的一些附加特性(其中大部分特性至少部分得到了 Mo+ 的支持):

  • 能够处理从最简单到最复杂的任何模型结构,包括任何约束或规则,例如结构是否允许元素重复名称。
  • 能够处理从少量到大量的所有模型数据,包括任何约束或规则,例如命名约束。
  • 能够解耦网络架构细节,并允许编译器/解释器映射到这些细节(这一点比我目前考虑的需要更多的思考和演变)
  • 能够配置自定义点,以便自定义代码可以直接与同一文件中的生成代码一起维护。
  • 能够拉取和过滤现有文件,以帮助决定更新生成内容。
  • 能够生成对文件以外的其他资源的更新,例如数据库。
  • 能够处理目录以删除过时的生成文件。
  • 能够从 XML 和各种流行数据库导入模型信息。
  • 能够从其他流行的建模工具导入模型信息。
  • 能够将模型信息导出到其他来源。
  • 能够从数据库记录以及模式中读取信息。
  • 一套完整的字符串测试和操作方法。
  • 能够配置输出的基本格式,例如制表符和空格。
  • 当然,还包括添加注释的能力!

您认为模型导向语言应该是类型安全的还是非类型安全的? Mo+ 不是类型安全的(var 是您唯一的变量数据类型),它允许您自由地在字符串和非字符串格式之间使用数据,我认为这对于代码生成目的更有用。

您认为模型导向语言还应该具备哪些其他特性和品质?

示例代码

随附的下载包含一个 Visual Studio 解决方案,其中包含一些面向对象代码示例,以及一个 Mo+ 解决方案 (xml) 文件和一些相关的代码模板。本文的重点并非在于如何具体地使用代码,但这些示例在您阅读本文时可能会有所帮助。

总结

希望本文能为您提供一些关于一个好的模型导向编程语言应该是什么样子,以便让您作为程序员最大限度地利用模型导向开发的一些思考。再次,我希望您能在下面的评论部分分享您的想法。

我还有一个值得深思的问题。1969年我们登上了月球。我原以为到目前为止我们也会类似地登上火星,但不知何故我们没有足够的投入和动力去做。类似地,到1960年代,软件行业开发了过程式语言,使我们能够解决与CPU架构细节解耦的问题。到1980年代,行业开发了面向对象语言,赋予我们更强大的解决问题能力,并且能够更紧密地表达业务需求。我原以为到目前为止,行业会开发出能够让我们以更面向业务的方式解决问题,并与平台和网络架构细节解耦的语言。我认为这样的语言将是模型导向的。为什么我们仍然在针对网络架构细节(例如云平台选择(AWS、Azure)、DBMS选择(SQL Server、PostgreSQL)等)进行编程,而这些决策可以在部署时做出和更改,并从更高阶的语言中解释/编译?为什么行业在这种语言方面没有取得更多进展?我们目前正在用框架解决一些这些问题,但框架来来去去,我们不得不不断更新我们的编程以适应新的和更好的框架。

此外,我希望您尝试 Mo+,充分利用模型导向方法进行开发,使用最初的良好模型导向编程语言尝试。Mo+ 内置了灵活的通用实体-关系类型模型结构,但它也允许您创建和使用自己的模型结构(或者如果您选择,可以重新创建其他标准化结构)。 这个免费的开源产品可在 moplus.codeplex.com 或 github.com/moplus/modelorientedplus 获取。 除了产品,该网站还包含用于构建更完整模型和生成完整工作应用程序的示例包。 视频教程和其他材料也可在 codeplex 网站上找到,IDE 中也提供了在线帮助。 Mo+ 社区通过网站 https://modelorientedplus.com 获得额外支持,并为 Mo+ 的发展做出贡献。

© . All rights reserved.