为什么 DRY 对于类型声明不被认为是一件好事?

发布于 2024-08-11 17:50:43 字数 592 浏览 5 评论 0原文

似乎那些永远不敢剪切和粘贴代码的人一遍又一遍地指定某事物的类型没有问题。为什么不强调类型信息应该声明一次且仅一次,以便在修改某些内容的类型时在整个源代码中引起尽可能小的连锁反应?例如,使用借用 C# 和 D 的伪代码:

MyClass<MyGenericArg> foo = new MyClass<MyGenericArg>(ctorArg);

void fun(MyClass<MyGenericArg> arg) {
    gun(arg);
}

void gun(MyClass<MyGenericArg> arg) {
    // do stuff.
}

Vs。

var foo = new MyClass<MyGenericArg>(ctorArg);

void fun(T)(T arg) {
    gun(arg);
}

void gun(T)(T arg) {
    // do stuff.
}

如果您更改 MyClass 的名称,或者更改 MyGenericArg 的类型,或者决定更改 foo 的类型,那么第二个似乎就不那么脆弱了。

It seems like people who would never dare cut and paste code have no problem specifying the type of something over and over and over. Why isn't it emphasized as a good practice that type information should be declared once and only once so as to cause as little ripple effect as possible throughout the source code if the type of something is modified? For example, using pseudocode that borrows from C# and D:

MyClass<MyGenericArg> foo = new MyClass<MyGenericArg>(ctorArg);

void fun(MyClass<MyGenericArg> arg) {
    gun(arg);
}

void gun(MyClass<MyGenericArg> arg) {
    // do stuff.
}

Vs.

var foo = new MyClass<MyGenericArg>(ctorArg);

void fun(T)(T arg) {
    gun(arg);
}

void gun(T)(T arg) {
    // do stuff.
}

It seems like the second one is a lot less brittle if you change the name of MyClass, or change the type of MyGenericArg, or otherwise decide to change the type of foo.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

压抑⊿情绪 2024-08-18 17:50:43

我认为您不会对您的论点发现很多异议,即后一个示例对程序员来说“更好”。存在许多语言设计功能,因为它们对编译器实现者来说更好!

请参阅 Scala 以具体化您的想法。

其他语言(例如 ML 系列)进一步采用类型推断,并创建了一种完整的编程风格,其中类型非常重要,比类 C 语言更重要。 (请参阅The Little MLer了解温和的介绍。)

I don't think you're going to find a lot of disagreement with your argument that the latter example is "better" for the programmer. A lot of language design features are there because they're better for the compiler implementer!

See Scala for one reification of your idea.

Other languages (such as the ML family) take type inference much further, and create a whole style of programming where the type is enormously important, much more so than in the C-like languages. (See The Little MLer for a gentle introduction.)

走过海棠暮 2024-08-18 17:50:43

这根本不被认为是一件坏事。事实上,C# 维护者已经开始使用 var 关键字来减少繁琐的样板文件,其中

MyContainer<MyType> cont = new MyContainer<MyType>();

完全等同于

var cont = new MyContainer<MyType>();

尽管您会看到很多人会反对 < code>var 用法,这表明很多人不熟悉具有类型推断的强类型语言;类型推断被误认为是动态/软类型。

It isn't considered a bad thing at all. In fact, C# maintainers are already moving a bit towards reducing the tiring boilerplate with the var keyword, where

MyContainer<MyType> cont = new MyContainer<MyType>();

is exactly equivalent to

var cont = new MyContainer<MyType>();

Although you will see many people who will argue against var usage, which kind of shows that many people is not familiar with strong typed languages with type inference; type inference is mistaken for dynamic/soft typing.

把人绕傻吧 2024-08-18 17:50:43

重复可能会导致代码更具可读性,并且有时在一般情况下可能是需要的。我一直认为 DRY 的重点更多地是复制逻辑而不是重复文字文本。从技术上讲,您也可以从底层代码中删除“var”和“void”。更不用说你用缩进来指示范围,为什么还要用大括号重复呢?

重复也有实际的好处:例如,通过保留“void”,程序的解析会更容易。

(但是,我仍然强烈同意您更喜欢“var name = new Type()”而不是“Type name = new Type()”。)

Repetition may lead to more readable code, and sometimes may be required in the general case. I've always seen the focus of DRY being more about duplicating logic than repeating literal text. Technically, you can eliminate 'var' and 'void' from your bottom code as well. Not to mention you indicate scope with indentation, why repeat yourself with braces?

Repetition can also have practical benefits: parsing by a program is easier by keeping the 'void', for example.

(However, I still strongly agree with you on prefering "var name = new Type()" over "Type name = new Type()".)

千鲤 2024-08-18 17:50:43

这是一件坏事。 Google 的 Go 语言 Techtalk 中提到了这个主题。

It's a bad thing. This very topic was mentioned in Google's Go language Techtalk.

毅然前行 2024-08-18 17:50:43

阿尔伯特·爱因斯坦说:“一切都应该尽可能简单,但不能简单一点。”

对于动态类型语言,您的抱怨毫无意义,因此您必须打算将此引用为静态类型语言。在这种情况下,您的替换示例隐式使用泛型(又名模板类),这意味着任何时候使用 fungun 时,都会基于类型的新定义的论点。无论程序员的意图如何,这都可能导致数十个额外的方法。特别是,您放弃了针对运行时错误的编译器检查类型安全的好处。

如果您的目标是简单地传递参数而不检查其类型,那么正确的类型将是 Object 而不是 T

类型声明旨在通过在编译时捕获错误而不是在运行时失败来使程序员的生活更简单。如果您的类型定义过于复杂,那么您可能不理解您的数据。在您的示例中,我建议将 fungun 添加到 MyClass,而不是单独定义它们。如果 fungun 不适用于所有可能的模板类型,那么它们应该在显式子类中定义,而不是作为采用模板化类参数的单独函数。

泛型作为一种将行为包装在更具体的对象周围的方式而存在。列表、队列、堆栈,这些都是使用泛型的好理由,但最终,您应该使用裸泛型做的唯一一件事就是创建它的实例,并调用它的方法。如果您确实觉得需要使用泛型做更多的事情,那么您可能需要将泛型类作为实例对象嵌入到包装类中,该包装类定义您需要的行为。这样做的原因与将基元嵌入到类中的原因相同:因为数字和字符串本身并不传达有关其内容的语义信息。

示例:

List 传达什么语义信息?只是您正在使用多个整数三元组。另一方面,列表中的颜色有 3 个整数(红、蓝、绿),且有界值 (0-255),这传达了您正在使用多种颜色的意图,但没有提供关于列表是否是的提示。已排序、允许重复或有关颜色的任何其他信息。最后,调色板可以为您添加这些语义:调色板有一个名称,包含多种颜色,但没有重复,并且顺序并不重要。

这与最初的问题有点相距甚远,但对我来说,DRY(不要重复自己)意味着指定一次信息,但该规范应该尽可能精确。

Albert Einstein said, "Everything should be made as simple as possible, but not one bit simpler."

Your complaint makes no sense in the case of a dynamically typed language, so you must intend this to refer to statically typed languages. In that case, your replacement example implicitly uses Generics (aka Template Classes), which means that any time that fun or gun is used, a new definition based upon the type of the argument. That could result in dozens of extra methods, regardless of the intent of the programmer. In particular, you're throwing away the benefit of compiler-checked type-safety for a runtime error.

If your goal was to simply pass through the argument without checking its type, then the correct type would be Object not T.

Type declarations are intended to make the programmer's life simpler, by catching errors at compile-time, instead of failing at runtime. If you have an overly complex type definition, then you probably don't understand your data. In your example, I would have suggested adding fun and gun to MyClass, instead of defining them separately. If fun and gun don't apply to all possible template types, then they should be defined in an explicit subclass, not as separate functions that take a templated class argument.

Generics exist as a way to wrap behavior around more specific objects. List, Queue, Stack, these are fine reasons for Generics, but at the end of the day, the only thing you should be doing with a bare Generic is creating an instance of it, and calling methods on it. If you really feel the need to do more than that with a Generic, then you probably need to embed your Generic class as an instance object in a wrapper class, one that defines the behaviors you need. You do this for the same reason that you embed primitives into a class: because by themselves, numbers and strings do not convey semantic information about their contents.

Example:

What semantic information does List convey? Just that you're working with multiple triples of integers. On the other hand, List, where a color has 3 integers (red, blue, green) with bounded values (0-255) conveys the intent that you're working with multiple Colors, but provides no hint as to whether the List is ordered, allows duplicates, or any other information about the Colors. Finally a Palette can add those semantics for you: a Palette has a name, contains multiple Colors, but no duplicates, and order isn't important.

This has gotten a bit far afield from the original question, but what it means to me is that DRY (Don't Repeat Yourself) means specifying information once, but that specification should be as precise as is necessary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文