代码生成指南

代码生成有许多用途:

  • 我们可以从模式或现有信息源中生成重复代码。 例如,我们可以从数据库模式文件生成数据访问对象
  • 我们可以从向导生成代码
  • 我们可以从简单的模型生成应用程序的框架。 我们在这里写了一个示例: Roslyn的代码生成:UML的骨架类
  • 我们可以从高级DSL生成整个应用程序
  • 我们可以根据处理现有文档获得的信息生成代码
  • 我们可以通过对使用其他语言或框架编写的代码进行反向工程而获得的信息来生成代码
  • 一些IDE具有生成样板代码的功能,例如Java中的equals或hashCode方法。

因此,代码生成可用于一小部分代码或整个应用程序。 它可以与多种不同的编程语言和不同的技术配合使用。

在本文中,我们探索了代码生成提供的所有可能性,以为您提供有关该主题的完整概述。

为什么要使用代码生成

使用代码生成的原因基本上有四个: 生产率简化性,可移植性一致性

生产率

使用代码生成,您只需编写一次生成器 ,即可根据需要重复使用多次。 向生成器提供特定输入并调用生成器比手动编写代码要快得多,因此代码生成可以节省时间。

简化版

通过代码生成,您可以从一些抽象的描述中生成代码 。 这意味着您的真理来源变成了描述,而不是代码。 与整个生成的代码相比,该描述通常更易于分析和检查。

可移植性

一旦有了为特定语言或框架生成代码的过程,您就可以简单地更改生成器并以另一种语言或框架为目标 。 您也可以一次定位多个平台。 例如,使用解析器生成器,您可以使用C#,Java和C ++获得解析器。 另一个例子:您可以编写一个UML图,并使用代码生成在C#中创建骨架类,并使用SQL代码为MySQL创建数据库。 因此,相同的抽象描述可用于生成不同种类的工件。

一致性

通过代码生成,您将始终获得所需的代码 。 生成的代码是根据相同的原理,命名规则匹配等设计的。代码始终按照您期望的方式工作,当然,生成器中的错误除外。 代码的质量是一致的。 通过手动编写代码,您可以让不同的开发人员使用不同的样式,并且即使在最重复的代码中,有时也会引入错误。

为什么不使用代码生成

由于所有工具的代码生成都不完美,因此主要有两个问题: 维护复杂性

保养

使用代码生成器工具时,您的代码将依赖于它。 必须维护代码生成器工具。 如果创建了它,则必须继续对其进行更新;如果您仅使用现有的,则必须希望有人继续对其进行维护,否则您必须接管自己。 因此,代码生成的优势并不是免费的。 如果您没有或找不到合适的能力在发电机上工作,这尤其危险。

复杂

自动生成的代码往往比手工编写的代码复杂。 有时,它与粘合代码有关,需要将不同的部分链接在一起,或者生成器支持的用例比您需要的用例更多。 在第二种情况下,生成的代码可以做的比您想做的要多,但这不一定是优点。 生成的代码肯定也没有手工编写的代码优化。 有时差异很小但并不明显,但是如果您的应用程序需要压缩性能的每一点,那么代码生成可能对您来说并不是最佳的。

我们如何使用代码生成?

根据上下文的不同,代码生成可能只是提高工作效率或开发过程中至关重要的组成部分。 许多现代IDE中都有一个有用的用法示例:它们允许通过单击按钮来创建框架类以实现接口或类似的东西。 您肯定可以自己编写该代码,只是浪费一些时间来执行手动任务。

有很多可能的方法来设计代码生成管道。 基本上,我们需要定义两个元素:

  1. 输入:代码生成中使用的信息来自哪里
  2. 输出:如何获取生成的代码

(可选)您可以在输入和输出之间进行转换。 它们可能有助于简化输出层并使输入和输出更独立。

可能的输入:

  • DSL:例如,我们可以使用ANTLR来描述一种语言的语法。 由此我们可以生成一个解析器
  • 其他格式的代码:数据库架构。 从数据库架构中,我们可以生成DAO
  • 向导:他们允许向用户询问信息
  • 逆向工程:可以通过处理复杂的代码工件来获取信息
  • 数据源,例如数据库,CSV文件或电子表格

可能的输出:

  • 模板引擎 ; 大多数网络程序员都知道模板引擎,该模板引擎用于用数据填充HTML UI
  • 代码构建API:例如, Javaparser可用于以编程方式创建Java文件

现在让我们检查一些管道:

  • 解析器生成 ; 该网站的读者一定会熟悉ANTLR和其他此类工具,这些工具会根据正式语法自动生成解析器。 在这种情况下,输入是DSL,而输出是使用模板引擎生成的
  • 模型驱动设计 ; 用于IDE或独立IDE的插件,该插件允许描述应用程序的模型(有时带有图形界面),从中可以生成整个应用程序或其框架
  • 数据库相关代码 这种使用可以认为是模型驱动设计和模板引擎的子代。 通常,程序员定义一个数据库架构,从中可以生成整个CRUD应用程序或仅生成用于处理数据库的代码。 还有一些工具可以执行相反的过程:从现有数据库中,他们创建数据库模式或代码来处理该过程
  • 临时应用 ; 此类别包括所有内容:从设计用于处理一件事情的工具到用于企业设置的临时系统,它们可以从正式的自定义描述生成整个应用程序。 这些应用程序通常是特定工作流程的一部分。 例如,客户使用图形界面来描述一个应用程序,一个即席系统生成数据库架构以支持该应用程序,另一个系统生成一个CRUD界面,依此类推。
  • IDE生成的代码:许多静态类型的语言需要编写大量样板代码,而IDE通常可以生成其中的一些代码:   用于实现方法的带有存根的类,标准的equals,hashCode和toString方法,用于所有现有属性的getter和setter

代码生成工具

在简短的代码生成简介之后,我们可以看到一些代码生成工具。

模板引擎

模板引擎组可能是最著名和最常用的。 模板引擎基本上是可以理解简单模板语言的微型编译器。 模板文件包含可以由模板引擎解释的特殊符号。 它可以做的最简单的事情就是在运行时使用适当的数据替换此特殊符号。 大多数模板引擎还支持允许描述简单结构的简单流控制命令(例如for-loopsif-else-statements )。

有很多示例,让我们看看其中两个代表了大多数示例的行为。

Jinja2

Jinja2是用纯Python编写的模板引擎。 它提供了受Django启发的非XML语法,但支持内联表达式和可选的沙盒环境。

Jinja2是用于Python的广泛使用的模板引擎。 它可以完成所有模板引擎的工作:根据提供的数据创建唯一的文档。 它支持模块化模板,控制流,变量等。但是,它还具有强大的安全性措施:HTML转义系统和可以控制对危险属性的访问的沙箱环境。

Jinja2模板的样子。

{% block title %}{% endblock %}

Jinja2对产生HTML页面具有特殊的支持,而这正是最常用的支持。 但是,它可以用于创建其他类型的文件。

哈巴狗

Pug是受Haml影响很大的高性能模板引擎,并使用JavaScript实现了Node.js和浏览器。

在许多方面,Pug就像许多其他模板引擎一样:它支持模块化模板,控制流等。区别在于Pug看起来像DSL,并且只能与HTML一起使用。 结果是Pug模板看起来非常干净和简单。

doctype html
html(lang="en")headtitle= pageTitlescript(type='text/javascript').if (foo) bar(1 + 5)bodyh1 Pug - node template engine#container.colif youAreUsingPugp You are amazingelsep Get on it!p.Pug is a terse and simple templating language with astrong focus on performance and powerful features.

解析器生成

解析器生成是一种工具,可以自动快速地为一种语言创建解析器。 它们非常成功且富有成效,因为解析语言的问题已得到广泛研究。 因此,有一些解决方案可以保证解析人们需要解析的大多数语言。

ANTLR

ANTLR可能是最常用的解析器生成器。 这意味着那里有很多例子。 但是广大社区的真正附加值是大量可用的语法 。

ANTLR将一种语法作为输入:语言的形式描述。 解析器的输出是一个解析树:一种结构,其中包含以易于使用的程序其余部分方式转换的源代码。 ANTLR还提供了两种遍历解析树的方法:访问者和侦听器。 第一个适用于您必须操纵树的元素或与之交互的情况,而第二个适用于在规则匹配时只需要做一些事情的情况。

语法格式简洁明了且与语言无关。

一个非常简单的ANTLR语法

grammar simple;basic   : NAME ':' NAME ;NAME    : [a-zA-Z]* ;COMMENT : '/*' .*? '*/' -> skip ;

如果您有兴趣学习如何使用ANTLR,可以查看我们编写的这个庞大的ANTLR教程 。 如果您准备成为一名专业的ANTLR开发人员,则可以购买我们的视频课程,以使用ANTLR构建专业的解析器和语言

模型驱动设计

这些通常是IDE或独立IDE的插件,它们允许使用图形界面描述应用程序模型,并从中生成应用程序的框架。 之所以会发生这种情况,是因为模型驱动设计的基础是抽象模型,可以使用UML图或DSL进行定义。 并且一旦可以与模型相关地描述程序的主要特征,则可以自动生成该程序的表示。 该模型在代码中的表示将具有开发人员设计的结构,但是通常,行为必须由开发人员自己直接实现。

例如,使用模型驱动的设计,您可以自动生成带有字段列表和适当方法的类,但不能自动实现方法的行为。

加速器

Acceleo 3是实现OMG的“模型到文本”规范的代码生成器。 它为开发人员提供了高质量代码生成器IDE可以预期的大多数功能:简单的语法,高效的代码生成,高级工具以及与JDT相同的功能。 Acceleo帮助开发人员处理其代码生成器的生命周期。 借助基于原型的方法,您可以从现有原型的源代码快速轻松地创建您的第一个生成器,然后借助Acceleo工具的所有功能(例如重构工具),您将轻松地改进生成器以实现完整的代码发电机。

这是来自Acceleo网站的引文,很好地描述了Acceleo的工作:实现模型驱动设计的原理。 它缺少的是对使用Acceleo的经验的描述。 它基本上是一个Eclipse插件,可根据您指定的模板为您提供从EMF模型开始创建Java代码的工具。 可以用不同的方式定义EMF模型:使用UML图或自定义DSL。

此图显示了Acceleo项目的工作流程。

m

Umple是一个建模工具编程语言家族 ,可实现作者称为“面向模型的编程”的功能。 它将从UML派生的抽象(例如关联,属性和状态机)添加到面向对象的编程语言(例如Java,C ++,PHP和Ruby)中。 Umple还可以用于通过文本创建UML类和状态图。

Umple是一种以结构化方式将UML模式与传统编程语言结合在一起的工具的示例。 它的诞生是为了简化模型驱动的开发过程,该过程传统上需要特定且复杂的工具。 它实质上是一种编程语言,支持UML(类和状态)图的功能来定义模型。 然后,Umple代码由其编译器转换为Java或PHP等传统语言。

Umple可以以多种方式使用:

  • 它可以用于以文本方式描述UML图
  • 它可以与传统语言结合用作该目标语言的预处理器或扩展。 Umple编译器以目标语言转换Umple代码,并使现有目标语言保持不变
  • 鉴于其对UML状态机的大力支持,它可以作为状态机生成器,通过对状态机的大量描述,可以用多种目标语言生成实现。

以下Umple代码等效于下一个UML图。

class Student {}class CourseSection {}class Registration 
{String grade;* -- 1 Student;* -- 1 CourseSection;
}

相反,以下Umple代码描述了状态机。

class GarageDoor  
{  status {  Open { buttonOrObstacle -> Closing;  }  Closing {  buttonOrObstacle -> Opening;  reachBottom -> Closed;  }  Closed { buttonOrObstacle -> Opening; }  Opening {  buttonOrObstacle -> HalfOpen;  reachTop -> Open;  }  HalfOpen { buttonOrObstacle -> Opening; }  }  
}

数据库相关代码

所有这些都围绕着数据库模式(从中生成代码)或从数据库模式(从中生成模式)进行。 这些生成器之所以可行,有两个原因:

  • 关系数据库支持与之交互的标准语言(SQL)
  • 在编程语言中,存在广泛的模式和与数据库交互的库

这两个原因保证了通常可以在编程语言和包含程序所需数据的数据库之间创建标准粘合代码。 在实践中,数据库模式是可用于生成代码的简单模型。

许多框架或IDE包含一些基本工具,这些工具可以从类中生成数据库架构,反之亦然,可以生成类以与数据库中的表进行交互。 在本节中,我们将看到一个可以做更多事情的工具示例。

塞莱里奥

Celerio是面向数据应用程序的代码生成器工具。

Celerio是Java工具,其中包括数据库提取器,用于从现有数据库中获取数据库模式。 然后,它将生成的架构与配置文件耦合,然后启动模板引擎以创建整个应用程序。 提取的数据库模式为XML格式。

例如,有一个示例应用程序可以从数据库架构生成Angular + Spring应用程序。

领域特定语言

DSL是一种以形式化方式捕获业务逻辑的好方法。 之后,这需要以某种方式执行。 尽管有时会构建解释器和编译器来执行DSL,但经常会使用代码生成器。 这样,可以将DSL转换为已经存在编译器的语言,例如Java或C#。

如今,可以使用语言工作台来构建DSL,这些工作台是为设计和实现DSL而创建的特定IDE。 语言工作台非常有用,因为它们还可以以较低的成本为DSL定义编辑器和其他支持工具。 这非常重要,因为非开发人员可以使用DSL,非开发人员需要自定义编辑器才能利用语言的功能,或者根本无法使用通用的文本编辑器。 除其他功能外,语言工作台通常与代码生成工具集成在一起。 让我们看几个例子。

JetBrains MPS

JetBrains MPS是基于投影编辑器的语言工作台 您可以使用它来创建一个或多个DSL。 它也可以用于扩展现有语言。 例如, mbeddr是C的扩展,用于改进嵌入式编程,它基于JetBrains MPS。

投影编辑器一词的含义是MPS保留数据的基础结构并以易于编辑的形式向您显示。 这个概念可能很难将您的声音包起来。 考虑一下传统的编程:编写源代码,然后编译器以其逻辑表示(即解析树)转换源代码,该逻辑树用于执行多项操作,例如优化或转换要执行的机器代码。 使用投影编辑器,您可以直接使用逻辑表示形式:解析树。 但是,只能以编辑器(MPS)允许的方式对其进行修改。

主要结果是,在使用JetBrains MPS创建DSL时,您需要整个IDE及其所有功能和强大功能。 您可以获取语法突出显示,代码完成,项目管理等。

这种方法的缺点是只能以某些方式修改代码。 因此,例如,通常您无法执行将某些代码复制并粘贴到程序中的操作,在编辑器中看到的只是代码的表示形式,而不是真实的代码(即解析树)。

但是,这种方法的优势在于,您可以创建一个使用任何形式的输入来修改代码的DSL,从而可以创建图形编辑器,表格输入甚至普通文本。 这个优点使创建DSL尤其有用,它也可以由非程序员使用。

该视频概述了JetBrains MPS。

使用JetBrains MPS生成代码

使用JetBrains MPS的工作流程主要在此软件本身内部完成:您可以在其中操作语言的所有方面。 完成所有这些操作后,您可能想要执行代码,最常见的方法是将其翻译为具有编译器的语言。 这里是播放代码生成。

这需要做两件事:

  1. 将高级概念转换为低级概念(本质上是模型转换模型)
  2. 具有文本生成器,该文本生成器接受较低级别的概念并将其直接映射到文本

显然,这里我们不能详细介绍,但是可以向您展示一个用于模型到模型转换的模板示例:在这里,我们希望将名为Document的概念映射到相应的Java代码。 您将看到,我们基本上有了一个带有main方法的Java类。 在该类内部,某些元素是固定的,而其他则取决于Document的值(即,类的名称和容器的内容)

我们已经说过这是模型到模型的转换,因为此模板将生成Java 模型 。 但是,MPS附带了一个文本生成器,可以将Java模型转换为代码,因此最终您将获得如下所示的Java文件:

package jetbrains.mps.samples.generator_demo.test_models.test1;/*Generated by MPS */import javax.swing.JFrame;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JButton;public class Button {public static void main(String[] args) {JFrame frame = new JFrame("Demo");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);Container container = frame.getContentPane();container.setLayout(new FlowLayout());container.add(new JButton());frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);}
}

这是使用MPS平台提供的功能生成代码的最简单方法。 但是,没有什么禁止您自己生成代码。 由于还可以使用这种语言来定义组件的行为,因此可以使用它与文件系统直接交互并自己编写代码。 例如,您可以手动将C#,Python或任何其他语言编写到文件中。

基本上,您可以创建一个新文件并向其添加内容,例如print("Python code here") 。 当然,这会很麻烦,因为您基本上会在文件中写入较长的文本字符串。 因此,此方法仅适用于编写配置文件或数据格式,而不适用于编程语言的代码。

文字

Xtext是一种语言工作台,建立在Eclipse和Eclipse Modeling Framework之上。 它可以用于设计文本DSL并为其获取编辑器。 从功能上讲,Xtext是组装以生成DSL的不同工具(例如,用于解析的ANTLR,用于UI的Eclipse等)的组合。

例如,您将使用ANTLR格式为DSL定义语法,如本例所示。

grammar org.example.domainmodel.Domainmodel withorg.eclipse.xtext.common.Terminalsgenerate domainmodel "http://www.example.org/domainmodel/Domainmodel"Domainmodel :(elements+=Type)*;Type:DataType | Entity;DataType:'datatype' name=ID;Entity:'entity' name=ID ('extends' superType=[Entity])? '{'(features+=Feature)*'}';Feature:(many?='many')? name=ID ':' type=[Type];

Xtext本身将生成一个解析器,该解析器将识别您的语言代码并为您提供EMF模型。 那时,您可以使用其他系统来执行模型到模型的转换或在模型上生成代码。

模型到模型的转换意味着您可以在另一个模型中转换使用Xtext创建的模型。 这可能是生成代码或项目最终产品的中间步骤。 那是因为Xtext利用了已经可用的工具,因此您可以将EMF模型与EMF平台中的其他工具一起使用。 这是使用Xtext这样的平台的最大优点:它可以用作更大的工具链的一部分。

使用Xtext生成代码

在为您的语言生成代码方面也具有灵活性。 与JetBrains MPS整体式相比,它既有优点也有缺点。 一个重要的优点是,生成过程更简单。

本质上,Xtext为您提供了一种语言(Xtend),它是Java的一种更强大的方言,具有对宏的支持等功能。 但是,它不提供生成代码的特殊权力。

这是控制代码生成的文件的示例。

class StatemachineGenerator implements IGenerator {override void doGenerate(Resource resource, IFileSystemAccess fsa) {fsa.generateFile(resource.className+".java", toJavaCode(resource.contents.head as Statemachine))}[..]def generateCode(State state) '''if (currentState.equals("«state.name»")) {if (executeActions) {«FOR c : state.actions»do«c.name.toFirstUpper»();«ENDFOR»executeActions = false;}System.out.println("Your are now in state '«state.name»'. Possible events are [«state.transitions.map(t | t.event.name).join(', ')»].");lastEvent = receiveEvent();«FOR t : state.transitions»if ("«t.event.name»".equals(lastEvent)) {currentState = "«t.state.name»";executeActions = true;}«ENDFOR»}'''	
}

如您所见,虽然您注意到完全在编写很长的代码字符串,但是您已经非常接近了。 对宏的支持减少了重复,但是这并不是一个平稳的体验。

然后将Xtend语言的代码转换为Java代码,这在手动过程中非常明显。

[..]public CharSequence generateCode(final State state) {StringConcatenation _builder = new StringConcatenation();_builder.append("if (currentState.equals(\"");String _name = state.getName();_builder.append(_name);_builder.append("\")) {");_builder.newLineIfNotEmpty();_builder.append("\t");_builder.append("if (executeActions) {");_builder.newLine();{EList _actions = state.getActions();for(final Command c : _actions) {_builder.append("\t\t");_builder.append("do");String _firstUpper = StringExtensions.toFirstUpper(c.getName());_builder.append(_firstUpper, "\t\t");_builder.append("();");_builder.newLineIfNotEmpty();}}_builder.append("\t\t");[..]return _builder;}

看起来好像是您手动构建了生成器。

无论如何,这也显示了Xtext的即席性质的优点。 这只是您可以执行和使用的Java代码。

JetBrains MPS和Xtext非常不同:它们各有千秋。 一种是全部集成的,这使得创建独立的DSL更容易,另一种是工具的组合,这使得将DSL与现有的代码库或基础结构集成起来更加容易。

本演示文稿介绍了如何使用Xtext开发DSL。

临时应用

此类别包括所有内容:从设计用于处理一件事情的工具到用于企业设置的临时系统,它们可以从正式的自定义描述生成整个应用程序。 这些应用程序通常是特定工作流程的一部分。 例如,客户使用图形界面来描述一个应用程序,一个即席系统生成数据库架构以支持该应用程序,另一个系统生成一个CRUD界面,依此类推。

这不是一个正确定义的类别,而是一个包罗万象的类别,其中包括不属于特定组的所有内容。 这意味着这组程序没有标准结构。 这也证明了代码生成的多功能性:如果您可以创建问题的模型或描述,则可以通过代码生成来解决它。 当然,您仍然必须了解解决一般问题并创建代码生成工具还是直接解决问题是否有意义。

在本节中,我们讨论两个工具:CMake(开发工具)和Yeoman(脚手架工具)。 第一个实质上是生成配置文件:用于提供其他软件的软件。 第二种方法通过提供一种创建针对特定软件平台,库或需求而优化的即用型项目的方式,简化了开发人员的工作。

CMake的

CMake是旨在构建,测试和打包软件的开源,跨平台工具系列。

CMake包括创建的三个开发工具,以帮助使用C和C ++进行开发。 主要工具旨在为不同平台和工具链生成构建文件(即,makefile和项目文件)。 例如,tt可以为Linux和Visual Studio项目文件生成makefile。

CMake不是编译器。 用户以CMake格式定义项目的结构,然后该工具生成在传统构建过程中使用的常规构建文件。

CMake文件看起来像一系列命令/宏,可为编译器,链接库,执行自定义命令等设置选项/标志。

cmake_minimum_required(VERSION 2.8)project("Antlr-cpp-tutorial")[..]if (NOT WIN32)set(CMAKE_CXX_FLAGS "-Wdeprecated -Wno-attributes" )
endif()[..]if(APPLE)
add_custom_command(TARGET antlr4-tutorial POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy_if_different"${PROJECT_SOURCE_DIR}/libs/antlr4-runtime.dylib"$)
endif()

约曼

Yeoman是一个通用的脚手架系统,允许创建任何类型的应用程序

如今,成为一名优秀的程序员不仅仅意味着知道如何编码,还意味着更多。 您需要了解使用的每种工具的最佳做法,并记住每次都实施。 在编写代码本身时,这已经足够困难了,但是在配置文件和正确的项目结构时,这甚至需要更多工作。 这就是类似Yeoman的工具出现的地方:它是一种脚手架工具 ,可通过一个命令生成一个新项目,该项目立即实现所有最佳实践。

Yeoman的核心是一个生成器生态系统,开发人员在其之上构建自己的模板。 该工具是如此流行,以至于已经有成千上万的模板可用。

Yeoman是一个JavaScript应用程序,因此编写生成器仅需编写JavaScript代码并使用提供的API。 工作流程也非常简单:您询问用户有关项目的信息(例如名称),收集配置信息,然后生成项目。

以下代码显示了用于创建Yeoman模板的示例生成器的一部分。

function makeGeneratorName(name) {name = _.kebabCase(name);name = name.indexOf('generator-') === 0 ? name : 'generator-' + name;return name;
}module.exports = class extends Generator {initializing() {this.props = {};}prompting() {return askName({name: 'name',message: 'Your generator name',default: makeGeneratorName(path.basename(process.cwd())),filter: makeGeneratorName,validate: str => {return str.length > 'generator-'.length;}},this).then(props => {this.props.name = props.name;});}[..]writing() {const pkg = this.fs.readJSON(this.destinationPath('package.json'), {});const generatorGeneratorPkg = require('../package.json');[..]this.fs.writeJSON(this.destinationPath('package.json'), pkg);}conflicts() {this.fs.append(this.destinationPath('.eslintignore'), '**/templates\n');}install() {this.installDependencies({ bower: false });}
};

摘要

在本文中,我们看到了代码生成的广阔世界。 我们已经看到了生成器的主要类别以及一些特定生成器的示例。 这些类别中的某些对于大多数开发人员来说是熟悉的,而对于大多数开发人员来说,它们是新的。 只要您正确使用它们,所有这些都可以用来提高生产率。

关于每个特定类别,还有更多要说的话,实际上,我们写了几篇有关解析不同语言的生成器的文章: Java , C# , Python和JavaScript 。 但是,我们希望撰写一篇介绍性文章来向这个世界介绍,而又不让读者不知所措。 而且,您打算如何利用代码生成的优势?

翻译自: https://www.javacodegeeks.com/2018/05/a-guide-to-code-generation.html


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部