举个例子,考虑超市购物者可以享受的一系列折扣。
我们可以将这些规则定义为某些标准方式的数据(合格项目列表,适用日期,优惠券代码),并编写通用代码来处理这些规则。或者,我们可以将每个代码编写为一大块代码,它会根据客户的购物清单检查相应的内容并返回任何适用的折扣。
您可以合理地将规则存储为对象,序列化为Blob或存储在代码文件中,以便每个规则可以选择自己在数据和代码之间的划分,以允许将来的规则不适合通用处理器的类型考虑在上面。
批评混合数据的代码通常很容易,通过if语句检查应该在文件或数据库中的6个不同的东西,但是有一个规则有助于边缘情况吗?
或者这是面向对象设计的重点,让我们不要担心数据和代码之间的界限?
澄清一下,基本问题是:你如何编写上面的例子?是否有一条经验法则让您决定什么是数据?什么是代码?
(注意:我知道,代码可以编译,但在动态语言和JIT编译的世界中,即使这是一个模糊的概念。)
答案 0 :(得分:8)
从根本上说,数据和代码之间当然没有区别,但对于真正的软件基础架构,可能会有很大的不同。除了明显的事情,如你所提到的,汇编,最大的问题是:
大多数足够大的项目都是为了生产“发布”,这是一个大捆绑,在3个月(或更长)的周期内生产,经过广泛测试,之后不能以严格控制的方式进行更改。 “代码”绝对无法更改,因此任何需要更改的内容都必须进行分解并制作“配置数据”,以便更改它变得适合那些确保发布正常工作的人。
当然,在大多数情况下,错误的配置数据可能会像坏代码一样彻底破坏版本,因此整个过程很大程度上是一种错觉 - 实际上,它的代码或“配置数据”是否会发生变化并不重要,重要的是,主系统和变化的部分之间的界面是狭窄的,定义明确,足以让你有机会做出改变的人理解他正在做的所有后果。
这已经比大多数人认为的更难了,因为它只是配置了几个字符串和数字(我亲眼目睹了生产大型机系统崩溃,因为它有一个布尔值设置与它正在谈论的另一个系统不同) 。当您的“配置数据”包含复杂的逻辑时,几乎不可能实现。但是情况不会更好,因为你使用设计糟糕的特殊“规则配置”语言而不是“真正的”代码。
答案 1 :(得分:5)
这是一个相当哲学的问题(我喜欢),所以我将以哲学的方式回答它:没有什么可以支持的。 ;)
数据是可以改变的系统的一部分。代码定义了行为;数据转换为新数据的方式。
更准确地说:数据可以用两个部分来描述:描述数据应该代表什么(例如,一个带有名称和类型的变量)和一个< EM>值。 变量的值可以根据代码中定义的规则进行更改。当然,描述不会改变,因为如果确实如此,我们会得到一条全新的信息。 代码本身不会改变,除非需求(我们对系统的期望)发生变化。
对于编译器(或VM),代码实际上是执行其操作的数据。但是,要编译的代码没有指定编译器的行为,编译器自己的代码会这样做。
答案 2 :(得分:2)
这一切都取决于要求。如果数据就像查找数据并经常更改,你真的不想在代码中这样做,但是像“星期几”这样的事情不应该在接下来的200年左右进行,所以代码就是这样。
您可能会考虑更改您的主题,因为我在看到它时首先想到的是代码与数据之间历史悠久的LISP讨论。幸运的是,Scheme代码和数据看起来是一样的,但就此而言,你永远不会意外地将代码与数据混合,因为在LISP中使用不卫生的宏很可能。
答案 3 :(得分:2)
数据是由名为Code的指令处理的信息。我不确定我是否觉得OOD模糊,还有属性(数据)和方法(代码)。 OO理论将两者封装到称为类的格式塔实体中,但它们在类中仍然是离散的。
您希望在选择的情况下制作代码的灵活性。包含常量值(通过使用上述if语句执行的操作)不灵活,无需重新处理源,而使用动态源数据则更灵活。这两种方法都错了吗?我会说这实际上取决于具体情况。正如Leppie所说,某些“数据”点是不变的,就像一周中可以硬编码的日子一样,但即便如此,在某些情况下动态地执行也可能是有利的。
答案 4 :(得分:1)
在Lisp中,您的代码是数据,而您的代码 数据是代码
在Prolog中,条款和条款 是条款。
答案 5 :(得分:1)
重要的一点是,您希望将代码中每次都执行相同的部分(即应用折扣)从代码中可能发生变化的部分(即要打折的产品,或者折扣的百分比等)
这只是为了安全。如果折扣发生变化,您不必重新编写折扣代码,只需要进入折扣存储库(数据库,或app文件或xml文件,或者您选择实施它)并制作对数字的一个小改动。
此外,如果折扣代码被分成XML文件,那么您可以将整个应用程序分配给经理,并且只要有足够的指示,他们就不需要在他们想要更改折扣率时纠缠您。
当你混合使用数据和代码时,你会在任何变化时以指数方式增加破坏的可能性。所以,正如leppie所说,你需要提取不断变化的部分,并将它们放在一个单独的地方。
答案 6 :(得分:1)
差异很大。数据是给系统的,而代码是系统的一部分。
错误的数据是毫无意义的:我们的代码=== handler是好的,而您所付的则不是问题,这对系统来说不是什么麻烦。但是,如果代码很糟糕-系统就很糟糕。
在示例中,让我们考虑一些JSON,我自己的一些不好的代码parser.js,并说好的V8。对于我的系统来说,糟糕的parser.js是一个代码,并且我的系统工作不正确。但是对于Google系统,我糟糕的解析器是无法说明V8质量的数据。
这个问题很实际,没有老套。 https://en.wikipedia.org/wiki/Systems_engineering试图做出好的回答和赚钱。
答案 7 :(得分:0)
代码是可以执行的任何数据。现在由于所有数据在某个时间点被用作某个程序的输入,可以说这个数据是由程序执行的!因此,您的程序充当数据的虚拟机。因此理论上数据和代码之间没有区别!
最重要的是软件工程/开发考虑因素,如性能,效率等。例如,数据驱动程序可能不如具有硬编码(因此易碎)条件语句的程序有效。因此,我选择将代码定义为可以有效执行的任何数据,而所有其他数据都是纯数据。
这是灵活性和效率之间的权衡。可执行数据(如XML规则)提供了更大的灵活性(有时),而编码为应用程序一部分时的相同数据/规则将更有效地运行,但经常更改它变得很麻烦。换句话说,可执行数据易于部署,但效率低,反之亦然。所以最终决定权归你 - 软件设计师。
如果我错了,请纠正我。
答案 8 :(得分:0)
数据和代码(程序)之间的界限很模糊。这最终只是术语的问题 - 例如,你可以说数据就是非代码的一切。但是,正如你所写的,他们可以幸福地混合在一起(尽管通常最好将它们分开)。
答案 9 :(得分:0)
数据是信息。这不是你决定把它放在哪里,不管是db,配置文件,通过代码配置还是在类中。
行为/代码也是如此。这不是你决定把它放在哪里或者你选择如何代表它。
答案 10 :(得分:0)
我想说数据,代码和配置之间的区别是在特定组件的上下文中进行的。有时它很明显,有时甚至更少。
例如,对于编译器,它使用的源代码和它创建的目标代码都是数据 - 并且应该与编译器自己的代码分开。
在你的情况下,你似乎在描述一个特别强大的配置文件的选项,它可以包含代码。就像,例如,GIMP允许您使用Scheme“配置”插件。作为读取此配置的组件的开发人员,您会将其视为数据。当在不同的级别工作 - 编写配置时 - 您会将其视为代码。
这是一种非常强大的设计方式。
将此应用于基础问题(“您将如何编写上述示例?”),一个选项可能是采用或设计高级域特定语言(DSL)来指定规则。在启动时或首次需要时,服务器会读取规则并执行它。
提供管理员界面,允许管理员
......所有这些都会在运行时发生。
DSL可能像表解析器或XML解析器一样简单,也可能像脚本语言一样复杂。从C开始,很容易嵌入Python或Lua。从Java开始,很容易嵌入Groovy或Clojure。
您可以在运行时切换已编译的代码,使用巧妙的链接或类加载器技巧。在我看来,这似乎比嵌入式DSL选项更难,更有价值。
答案 11 :(得分:0)
代码和数据之间的关系如下:
编译成程序后的代码在执行时处理数据
程序可以提取数据,转换数据,加载数据,生成数据......
另外 程序可以提取代码,转换代码,加载代码,生成代码太多......
因此,没有编译或插入器的代码是无用的,数据总是值得...,但编译后的代码可以完成上述所有活动....
例如)
Sourcecontrolsystem进程源代码
这里的源代码本身就是一个代码
备份脚本处理文件
这里的文件是数据等等......
答案 12 :(得分:0)
我发现这个问题的最佳实际答案是: 现在或在任何可预见的未来,任何需要序列化的类都是数据。 其他一切都是代码。 这就是为什么,例如,Java的HashMap是数据 - 虽然它有很多代码,API方法和具体实现(即,可能看起来像乍一看的代码)。