我使用 MyGeneration 和 nHibernate 来创建基本的 POCO 对象和 XML 映射文件。我听说有些人说他们认为代码生成器不是一个好主意。目前最好的想法是什么?当它生成数千行无法理解的代码时,代码生成是不是很糟糕?
26 回答
由代码生成器生成的代码不应(作为概括)用于随后由人工干预进行编辑的情况。一些系统,例如各种 Visual C++ 化身的向导生成的代码,然后期望程序员手动编辑这些代码。这并不流行,因为它要求开发人员分解生成的代码,理解它并进行修改。这也意味着生成过程是一次性的。
生成的代码应该与系统中的其他代码存在于不同的文件中,并且只能从生成器中生成。生成的代码代码应该清楚地标记,以表明人们不应该修改它。我有机会做了不少这样或那样的代码生成系统,并且所有这样生成的代码在序言中都有这样的东西:
-- =============================================================
-- === Foobar Module ===========================================
-- =============================================================
--
-- === THIS IS GENERATED CODE. DO NOT EDIT. ===
--
-- =============================================================
Code Generation in Action是一本关于这个主题的好书。
代码生成器很棒,糟糕的代码就是糟糕的。
此页面上的大多数其他响应都是“不,因为生成的代码通常不是很好”。
这是一个糟糕的答案,因为:
1) 生成器和其他任何东西一样都是工具——如果你滥用它们,不要责怪工具。
2) 开发人员倾向于以自己一次编写出色代码的能力而自豪,但您不会将代码生成器用于一次性项目。
我们在所有 Java 项目中都使用代码生成系统来实现持久性,并且在生产环境中生成了数千个类。
作为经理,我爱他们,因为:
1) 可靠性:该代码中没有明显的剩余错误。多年来,它经过了如此详尽的测试和改进,而不是调试时,我从不担心持久层。
2) 标准化:每个开发人员的代码在这方面都是相同的,所以当一个人从同事那里接一个新项目时,学习的东西要少得多。
3) 进化:如果我们找到更好的方法来做事,我们可以更新模板并快速一致地更新 1000 多个类。
4) 革命:如果我们将来切换到不同的持久性系统,那么每个持久性类都有完全相同的 API 的事实使我的工作变得容易得多。
5) 生产力:只需单击几下即可从元数据构建持久对象系统 - 这可以节省数千个无聊的开发时间。
代码生成就像使用编译器——在个别情况下,您可能能够编写更好的优化汇编语言,但在大量项目中,您宁愿让编译器为您做这件事,对吗?
我们采用了一个简单的技巧来确保类总是可以在不丢失定制的情况下重新生成:每个生成的类都是抽象的。然后开发人员使用具体类对其进行扩展,添加自定义业务逻辑并覆盖他希望与标准不同的任何基类方法。如果元数据发生变化,他可以随时重新生成抽象类,如果新模型破坏了他的具体类,编译器会通知他。
我在使用代码生成器时遇到的最大问题是在维护期间。如果您修改生成的代码,然后对架构或模板进行更改并尝试重新生成,您可能会遇到问题。
一个问题是,如果该工具不允许您保护对修改后的代码所做的更改,那么您的更改将被覆盖。
我看到的另一个问题,特别是 RSA 中用于 Web 服务的代码生成器,如果您过多地更改生成的代码,生成器会抱怨不匹配并拒绝重新生成代码。这可能发生在像更改变量类型这样简单的事情上。然后,您被困在为不同的项目生成代码并将结果合并回原始代码中。
代码生成器可以提高生产力,但需要注意以下几点:
让您以您想要的方式工作。
如果您必须弯曲非生成代码以适应生成的代码,那么您可能应该选择不同的方法。
作为常规构建的一部分运行。
输出应生成到中间目录,而不是签入源代码控制。但是,输入必须签入源代码控制。
无需安装
理想情况下,您也可以将该工具签入源代码控制。在准备新的构建机器时让人们安装东西是个坏消息。例如,如果您进行分支,您希望能够使用代码对工具进行版本控制。
如果必须,请制作一个脚本,该脚本将使用带有源树副本的干净机器,并根据需要配置机器。请全自动。
没有编辑输出
您不必编辑输出。如果输出按原样不够有用,则该工具不适合您。
此外,输出应该清楚地说明它是一个生成的文件并且不应该被编辑。
可读输出
输出应该写得很好并且格式化。您希望能够轻松打开输出并阅读它。
#line
许多语言都支持#line
指令之类的东西,它允许您将输出的内容映射回输入,例如在生成编译器错误消息或单步执行调试器时。这可能很有用,但也可能很烦人,除非做得很好,所以这不是必需的。
我的立场是代码生成器还不错,但它们的许多用途都不错。
如果您使用代码生成器来节省时间并编写好代码,那很好,但通常它没有优化,或者增加了很多开销,在这些情况下,我认为它很糟糕。
如果你想在你的类中混入行为,代码生成可能会让你有些不快。一个同样有效的替代方案可能是属性/注释和运行时反射。
编译器是代码生成器,因此它们本质上并不坏,除非您只喜欢使用原始机器代码进行编程。
然而,我相信代码生成器应该始终完全封装生成的代码。也就是说,您永远不必手动修改生成的代码,任何更改都应通过修改生成器的输入并重新生成代码来完成。
如果它是 Fran Tarkenton 试图向您推销的大型机 cobol 代码生成器,那么绝对可以!
我之前写过一些代码生成器——老实说,它们不止一次地救了我的命!
一旦你有一个明确定义的对象 - 集合 - 用户控件设计,你可以使用代码生成器为你构建基础,让你作为开发人员的时间更有效地用于构建复杂的东西,毕竟,谁真正想要编写 300 多个公共属性声明和变量实例?我宁愿陷入业务逻辑而不是所有无意识的重复任务。
许多人在使用代码生成时犯的错误是编辑生成的代码。如果您记住,如果您觉得需要编辑代码,那么您实际上需要编辑代码生成工具,这对提高生产力很有帮助。如果你不断地与生成的代码作斗争,最终会降低生产力。
我发现最好的代码生成器是那些允许您编辑生成代码的模板的生成器。出于这个原因,我真的很喜欢 Codesmith,因为它是基于模板的,而且模板很容易编辑。当您发现生成的代码存在缺陷时,您只需编辑模板并重新生成您的代码,然后您就会永远保持良好状态。
我发现的另一件事是,很多代码生成器在源代码控制系统中使用起来并不容易。我们解决这个问题的方法是签入模板而不是代码,我们签入生成的源代码控制的唯一内容是生成的代码(主要是 DLL 文件)的编译版本。这为您省去了很多麻烦,因为您只需签入几个 DLL,而不是可能生成数百个文件。
我们当前的项目大量使用了代码生成器。这意味着我第一次看到了生成代码的“明显”好处——没有编码错误、没有拼写错误、更好地遵守标准编码风格——以及在维护模式下几个月后,意外的缺点。实际上,我们的代码生成器最初确实提高了我们的代码库质量。我们确保它是完全自动化的,并与我们的自动化构建集成。但是,我想说:
(1) 代码生成器可以是拐杖。现在,我们的系统中有几个巨大的、丑陋的难以维护的代码块,因为在过去的某一时刻,向我们的代码生成 XML 文件中添加 20 个新类比进行适当的分析和类更容易重构。
(2) 规则的例外会害死你。我们使用代码生成器创建数百个 Screen 和 Business Object 类。最初,我们对类中可能出现的方法实施了一个标准,但像所有标准一样,我们开始制定例外。现在,我们的代码生成 XML 文件是一个巨大的怪物,其中充满了插入到选定类中的特殊 Java 代码片段。几乎不可能解析或理解。
(3) 由于我们的大量代码是使用数据库中的值生成的,因此开发人员很难在其各自的工作站上维护一致的代码库(因为可以有多个版本的数据库)。通过软件进行调试和跟踪要困难得多,而且由于类之间存在额外的抽象和隐式关系,团队的新手需要更长的时间才能弄清楚代码的“流程”。IDE 无法获取通过代码生成的类进行通信的两个类之间的关系。
现在大概就够了。我认为代码生成器非常适合作为开发人员个人工具包的一部分。一组写出样板代码的脚本使启动项目变得更加容易。但是代码生成器不会让维护问题消失。
在某些(不是很多)情况下,它们很有用。例如,如果您想根据数据库表中的查找类型数据生成类。
当代码生成使编程变得更加困难时(IE、生成不良的代码或维护噩梦)是不好的,但当它们使编程更高效时它们是好的。
它们可能并不总是生成最佳代码,但根据您的需要,您可能会决定节省的开发人员工时弥补了一些小问题。
综上所述,我对 ORM 代码生成器的最大不满是,如果架构发生变化,维护生成的代码可能是 PITA。
代码生成器还不错,但有时它们会在存在另一种解决方案的情况下使用(即,当对象数组更适合并在几行代码中完成时,实例化一百万个对象)。
另一种情况是它们使用不正确或编码错误。太多的人因为错误或对如何正确配置它的误解而有过糟糕的体验而对代码生成器发誓。
但就其本身而言,代码生成器还不错。
-亚当
它们就像任何其他工具一样。有些提供比其他更好的结果,但由用户知道何时使用它们。如果你想拧螺丝,锤子是一种可怕的工具。
这是那些极具争议的问题之一。就个人而言,我认为代码生成器非常糟糕,因为它们中的大多数都输出了未经优化的垃圾代码。
然而,这个问题真的是只有你才能回答的问题。在许多组织中,开发时间比项目执行速度甚至可维护性更重要。
我们使用代码生成器来生成数据实体类、数据库对象(如触发器、存储过程)、服务代理等。在任何你看到大量重复代码遵循模式和涉及大量手动工作的地方,代码生成器都可以提供帮助。但是,您不应该过多地使用它,以至于可维护性是一种痛苦。如果您想重新生成它们,也会出现一些问题。
Visual Studio、Codesmith 等工具为大多数常见任务提供了自己的模板,并使此过程更容易。但是,很容易自行推出。
当您必须返回并且无法理解代码中发生了什么时,它确实会成为可维护性的问题。因此,很多时候您必须权衡与易于维护相比,快速完成项目的重要性
可维护性<>简单或快速的编码过程
我将 My Generation 与实体空间一起使用,我对此没有任何问题。如果我有架构更改,我只需重新生成类,一切都很好。
它们充当拐杖,可以使您无法长期维持该计划。
第一个 C++ 编译器是生成 C 代码 (CFront) 的代码生成器。
我不确定这是支持还是反对代码生成器的论据。
我认为米切尔击中了它的头部。代码生成有它的位置。在某些情况下,让计算机为您完成工作会更有效!当进行代码更改的时间成本很小时,它可以让您自由地改变对特定组件的实现的想法。当然,了解代码生成器的输出可能仍然很重要,但并非总是如此。我们刚刚完成的一个项目中有一个示例,其中许多 C++ 应用程序需要通过命名管道与 C# 应用程序进行通信。我们最好使用小而简单的文件来定义消息,并为事务的每一方生成所有类和代码。当程序员处理问题 X 时,
这是一个工作流程问题。ASP.NET 是一个代码生成器。XAML 解析引擎在转换为 MSIL 之前实际上会生成 C#。当代码生成器变成像 CodeSmith 这样与您的开发工作流程隔离的外部产品时,必须特别注意使您的项目保持同步。例如,如果生成的代码是 ORM 输出,并且您对数据库模式进行了更改,您要么必须完全放弃代码生成器,要么利用 C# 处理部分类的能力(允许您添加成员和现有类的功能而不继承它)。
我个人不喜欢生成器工作流程的孤立/ Alt-Tab 特性;如果代码生成器不是我的 IDE 的一部分,那么我觉得它是一个杂物。一些代码生成器,例如 Entity Spaces 2009(尚未发布),比前几代生成器集成度更高。
我认为代码生成器的灵丹妙药可以在预编译例程中使用。C# 和其他 .NET 语言缺乏这一点,尽管 ASP.NET 喜欢它,这就是为什么 SubSonic 在 ASP.NET 上工作得如此之好,但在其他方面就不行了。SubSonic 在正常 ASP.NET 编译开始之前的构建时生成 C# 代码。
要求您的工具供应商(即 Microsoft)更彻底地支持预构建例程,以便可以使用元数据将代码生成器集成到您的解决方案的工作流程中,而不是作为必须单独维护的外部输出代码文件手动管理。
乔恩
代码生成器的最佳应用是当整个项目是一个模型,并且项目的所有源代码都是从该模型生成的。我不是在谈论 UML 和相关的废话。在这种情况下,项目模型还包含自定义代码。
那么开发人员唯一需要关心的就是模型。一个简单的架构更改可能会导致数千行源代码的即时修改。但一切都保持同步。
这是恕我直言最好的方法。听起来乌托邦?至少我知道它不是;) 不久的将来会证明这一点。
在最近的一个项目中,我们构建了自己的代码生成器。我们为我们的视图和视图控制器类生成了所有的数据库内容,以及所有的基本代码。尽管生成器花费了几个月的时间来构建(主要是因为这是我们第一次这样做,而且我们有几个错误的开始),但它在我们第一次运行它并为整个应用程序生成基本框架时就收回了成本大约十分钟。这一切都是用 Java 编写的,但 Ruby 是一种出色的代码编写语言,尤其适用于小型一次性项目。最好的事情是代码和项目组织的一致性。此外,您必须提前考虑基本框架,这总是好的。
假设它是一个好的代码生成器,代码生成器就很棒。尤其是非常冗长的工作 c++/java。