您认为什么是过度概括?

发布于 2024-09-09 01:02:38 字数 760 浏览 6 评论 0原文

在花了一些时间尝试 Haskell 和其他函数式语言之后,我开始欣赏用一般术语描述问题所带来的设计的简单性。虽然模板编程的许多方面可能远非简单,但某些用途非常常见,我认为它们不会妨碍清晰性(尤其是函数模板)。我发现模板通常可以简化当前的设计,同时自动添加一些未来的抵抗力。为什么它们的功能应该交给库编写者?

另一方面,有些人似乎像躲避瘟疫一样回避模板。十年前我就可以理解这一点,当时泛型类型的概念对于大多数编程社区来说都是陌生的。但现在所有流行的静态类型面向对象语言都支持一种或另一种形式的泛型。增加的熟悉度似乎有必要调整保守态度。

最近有人向我表达了这样一种保守态度:

你不应该做任何不必要的事情——软件开发的基本规则。

老实说,我很惊讶地看到这一点如此轻蔑地表述,好像它应该是不言而喻的。就我个人而言,我发现这一点远非不言自明,对于像 Haskell 这样的语言来说,除非您另外指定,否则一切都是通用的。话虽这么说,我想我明白这个观点的来源。

在我的脑海里,确实有类似的规则在盘旋。现在它处于最前沿,我意识到我一直根据整体架构来解释它。例如,如果您有一个类,您不想在其中加载您有一天可能会使用的大量功能。如果您只需要一个具体版本,则不必费心制作接口(尽管可模拟性可能是对此的反驳)。诸如此类的事情......

然而,我不做的是在微观层面上应用这一原则。如果我有一个没有理由依赖于任何特定类型的小型实用函数,我将制作一个模板。

那么你觉得怎么样?您认为什么是过度概括?根据上下文,该规则是否具有不同的适用性?你甚至同意这是一条规则吗?

Having spent some time playing around in Haskell and other functional languages, I've come to appreciate the simplicity of design that comes from describing problems in general terms. While many aspects of template programming can be far from simple, some uses are common enough that I don't think they're an impediment to clarity (especially function templates). I find templates can often simplify the current design while automatically adding a bit of future-resistance. Why should their functionality be relegated to the library writers?

On the other hand, some people seem to avoid templates like the plague. I could understand this a decade ago when the very concept of generic types was foreign to much of the programming community. But now all of the popular statically-typed OO languages support generics of one form or another. The added familiarity seems to warrant an adjustment of the conservative attitudes.

One such conservative attitude was expressed to me recently:

You should never make anything more general than necessary - basic rule of software development.

I was quite honestly surprised to see this stated so dismissively as if it should've been self evident. Personally I find it far from self-evident, what with languages like Haskell where everything is generic unless you specify otherwise. That being said, I think I understand where this point of view comes from.

In the back of my mind, I do have something like that rule rattling around. Now that it's at the forefront, I realize I've always interpreted it in the light of overall architecture. For example, if you have a class, you don't want to load it up with tons of features you might one day use. Don't bother making interfaces if you only need one concrete version (though mockability might be a counterargument to this one). Things like that...

What I don't do, however, is apply this principle on the micro level. If I have a small utility function that has no reason to be dependent on any particular type, I'll make a template.

So what do you think, SO? What do you consider to be over-generalizing? Does this rule have differing applicability depending on the context? Do you even agree this is a rule?

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

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

发布评论

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

评论(7

梦言归人 2024-09-16 01:02:38

过度概括让我发疯。我并不害怕模板(远不及模板),而且我喜欢通用的解决方案。但我也喜欢解决客户付费的问题。如果这是一个为期一周的项目,为什么我现在要资助一个为期一个月的盛会,它不仅会通过新税收等明显的未来可能的变化,而且可能通过发现新卫星或火星上的生命而继续发挥作用?

回到模板,客户端要求提供一些功能,其中涉及编写一个接受字符串和数字的函数。您给我一个模板化的解决方案,它采用任意两种类型,并针对我的具体情况做正确的事情,而在其余情况下可能会或可能不正确(由于缺乏要求),我将不胜感激。我会很生气的是,除了付钱给你之外,我还必须付钱给某人来测试它,某人来记录它,如果将来出现更普遍的情况,还得有人在你的限制范围内工作。

当然,并非所有的概括都是过度概括。一切都应该尽可能简单,但不能更简单。尽可能笼统,但不能更笼统。尽我们所能进行测试,但不再进行测试。等等。此外,“预测可能发生的变化并将其封装起来。”所有这些规则都很简单,但并不容易。这就是为什么智慧对于开发人员和管理人员来说很重要。

Over generalizing makes me crazy. I'm not scared of templates (nowhere near) and I like general solutions. But I also like solving the problem for which the client is paying. If it's a one week project, why am I now funding a one month extravaganza which will continue to work not only through obvious possible future changes like new taxes, but probably through the discovery of new moons or life on mars?

Bringing this back to templates, the client asks for some capability that involves your writing a function that takes a string and a number. You give me a templated solution that takes any two types and does the right thing for my specific case and something that might or might not be right (due the absence of requirements) in the rest of the cases, and I will not be grateful. I will be ticked off that in addition to paying you I have to pay someone to test it, someone to document it, and someone to work within your constraints in the future if a more general case should happen to come along.

Of course, not all generalization is over generalization. Everything should be as simple as possible, but no simpler. As general as necessary, but no more general. As tested as we can afford, but no more tested. Etc. Also, "predict what might change and encapsulate it." All these rules are simple, but not easy. That's why wisdom matters in developers and those who manage them.

霞映澄塘 2024-09-16 01:02:38

如果你能同时做到这一点,并且代码至少同样清晰,那么泛化总是比专业化更好。

XP 人们遵循的原则称为 YAGNI - You Ain't Gonna Need It。

wiki 有这样的说法:

即使您完全、完全、完全确定自己以后会需要某个功能,也不要现在就实现它。通常情况下,结果要么 a) 你根本不需要它,要么 b) 你实际需要的东西与你之前预见的需要的东西有很大不同。

这并不意味着您应该避免在代码中构建灵活性。这意味着您不应该根据您认为以后可能需要的东西来过度设计某些东西。

If you can do it in the same time, and the code is at least as clear, generalization is always better than specialization.

There's a principle that XP people follow called YAGNI - You Ain't Gonna Need It.

The wiki has this to say:

Even if you're totally, totally, totally sure that you'll need a feature later on, don't implement it now. Usually, it'll turn out either a) you don't need it after all, or b) what you actually need is quite different from what you foresaw needing earlier.

This doesn't mean you should avoid building flexibility into your code. It means you shouldn't overengineer something based on what you think you might need later on.

貪欢 2024-09-16 01:02:38

太通用了?我必须承认我是通用编程的粉丝(作为一个原则)并且我真的很喜欢 Haskell 和 Go 在那里使用的想法。

然而,在使用 C++ 进行编程时,您可以通过两种方法来实现类似的目标:

  • 通用编程:通过模板的方式,即使存在编译时间、对实现的依赖等问题。
  • 面向对象编程:它的祖先是一种将问题放在对象本身(类/结构)而不是函数上的方法......

现在,什么时候使用?这无疑是一个困难的问题。大多数时候,这只不过是一种直觉,而且我确实见过其中任何一种被滥用的情况。

根据经验,我想说,函数/类越小,其目标越基本,就越容易泛化。举个例子,我在大多数我喜欢的项目和工作中都会随身携带一个工具箱。大多数函数/类都是通用的......在某种程度上有点像 Boost ;)

// No container implements this, it's easy... but better write it only once!
template <class Container, class Pred>
void erase_if(Container& c, Pred p)
{
  c.erase(std::remove_if(c.begin(), c.end(), p), c.end());
}

// Same as STL algo, but with precondition validation in debug mode
template <class Container, class Iterator = typename Container::iterator>
Iterator lower_bound(Container& c, typename Container::value_type const& v)
{
  ASSERT(is_sorted(c));
  return std::lower_bound(c.begin(), c.end(), v);
}

另一方面,你越接近特定于业务的工作,你就越不可能是通用的。

这就是为什么我本人很欣赏最省力的原则。当我想到一个类或方法时,我首先退后一步思考一下:

  • 它更通用是否有意义?
  • 费用是多少?

根据答案,我调整通用性的程度,并且我努力避免过早锁定,即当使用稍微更通用的方法不需要花费太多时,我会避免使用足够的非通用方法一。

示例:

void Foo::print() { std::cout << /* some stuff */ << '\n'; }

// VS

std::ostream& operator<<(std::ostream& out, Foo const& foo)
{
  return out << /* some stuff */ << '\n';
}

它不仅更通用(我可以指定输出位置),而且也更惯用。

Too generic ? I must admit I am a fan of Generic Programming (as a principle) and I really like the idea that Haskell and Go are using there.

While programming in C++ however, you are offered two ways to achieve similar goals:

  • Generic Programming: by the way of templates, even though there are issues with compilation time, dependency on the implementation, etc..
  • Object-Oriented Programming: its ancestor in a way, which places the issue on the object itself (class/struct) rather than on the function...

Now, when to use ? It's a difficult question for sure. Most of the times it's not much more than a gut feeling, and I've certainly seen abuse of either.

From experience I would say that the smaller a function/class, the more basic its goal, the easier it is to generalize. As an example, I carry around a toolbox in most of my pet projects and at work. Most of the functions / classes there are generic... a bit like Boost in a way ;)

// No container implements this, it's easy... but better write it only once!
template <class Container, class Pred>
void erase_if(Container& c, Pred p)
{
  c.erase(std::remove_if(c.begin(), c.end(), p), c.end());
}

// Same as STL algo, but with precondition validation in debug mode
template <class Container, class Iterator = typename Container::iterator>
Iterator lower_bound(Container& c, typename Container::value_type const& v)
{
  ASSERT(is_sorted(c));
  return std::lower_bound(c.begin(), c.end(), v);
}

On the other hand, the closer you get to the business specific job, the least likely you are to be generic.

That's why I myself appreciate the principle of least effort. When I am thinking of a class or method, I first take a step backward and think a bit:

  • Would it make sense for it to be more generic ?
  • What would be the cost ?

Depending on the anwsers, I adapt the degree of genericity, and I struggle to avoid premature locking, ie I avoid using a non-generic enough way when it doesn't cost much to use a slightly more generic one.

Example:

void Foo::print() { std::cout << /* some stuff */ << '\n'; }

// VS

std::ostream& operator<<(std::ostream& out, Foo const& foo)
{
  return out << /* some stuff */ << '\n';
}

Not only is it more generic (I can specify where to output), it's also more idiomatic.

执手闯天涯 2024-09-16 01:02:38

当你浪费时间概括某件事时,它就过于概括了。如果您将来打算使用通用功能,那么您可能不会浪费时间。就这么简单(在我看来)。

需要注意的一件事是,如果软件通用化也会让软件变得更加混乱,那么它并不一定是一种改进。通常需要权衡。

Something is over-generalized when you're wasting time generalizing it. If you are going to use the generic features in the future then you're probably not wasting time. It's really that simple [in my mind].

One thing to note is that making your software generalized isn't necessarily an improvement if it also makes it more confusing. There is often a trade off.

浪漫之都 2024-09-16 01:02:38

我认为你应该考虑编程的两个基本原则:KISS(保持简单明了)和DRY(不要重复)。大多数时候我从第一个开始:以最直接、最简单的方式实现所需的功能。很多时候就足够了,因为它已经可以满足我的要求了。在这种情况下,它仍然很简单(并且不是通用的)。

当第二次(或最多第三次)我需要类似的东西时,我尝试根据具体的现实生活示例来概括问题(功能、类、设计等) - >我不太可能只是为了它本身而进行概括。
下一个类似的问题:如果它能优雅地适合当前的图片,很好,我可以轻松解决它。如果没有,我检查当前的解决方案是否可以进一步推广(不会使其变得太复杂/不太优雅)。

我认为即使你事先知道你需要一个通用的解决方案,你也应该做类似的事情:采取一些具体的例子,并根据它们进行概括。否则,很容易陷入死胡同,你有一个“不错的”通用解决方案,但它无法解决真正的问题。

但是,可能存在一些例外情况。
a) 当通用解决方案的工作量和复杂性几乎完全相同时。示例:使用泛型编写队列实现并不比仅对字符串执行相同的操作复杂多少。
b) 如果用一般的方式解决问题更容易,而且解决方案更容易理解。这种情况并不经常发生,我现在无法想出一个简单的、现实生活中的例子:-(。但即使在这种情况下,在我看来,之前拥有/分析具体的例子也是必须的,因为只有它才能确认你走在正确的轨道上,

可以说经验可以克服遇到具体问题的先决条件,但我认为在这种情况下,经验意味着你已经看到并思考过具体的、类似的问题和解决方案,

如果你有时间的话。可以看看计算机程序的结构和解释。它有很多有趣的内容,涉及如何在通用性和复杂性之间找到适当的平衡,以及如何将问题真正需要的复杂性保持在最低限度,

当然,各种敏捷流程也推荐了类似的内容:从简单的开始,需要时重构。

I think you should consider two basic principles of programming: KISS (keep it simple and straightforward) and DRY (don't repeat yourself). Most of the time I start with the first: implement the needed functionality in the most straightforward and simple way. Quite often it's enough, because it can already satisfy my requirements. In this case it remains simple (and not generic).

When the second (or max third) time I need something similar I try to generalize the problem (function, class, design, whatever) based on the concrete real life examples -> it's unlikely that I do the generalization just for itself.
Next similar problem: if it fits to the current picture elegantly, fine, I can solve it easily. If not, I check if the current solution can be further generalized (without making it too complicated/not so elegant).

I think you should do something similar even if you know in advance that you will need a general solution: take some concrete examples, and do the generalization based on them. Otherwise it's too easy to run into dead ends where you have a "nice", general solution, but it's not usable to solve the real problems.

However, there might be some exceptional cases to this.
a) When a general solution is almost exactly the same effort and complexity. Example: writing a Queue implementation using generics is not much more complicated then doing the same just for Strings.
b) If it's easier to solve the problem in the general way, and also the solution is easier to understand. It does not happen too often, I can't come up with a simple, real life example of this at the moment :-(. But even in this case having/analyzing concrete examples previously is a must IMO, as only it can confirm that you are on the right track.

One can say experience can overcome the prerequisite of having concrete problems, but I think in this case experience means that you have already seen and thought about concrete, similar problems and solutions.

If you have some time you could have a look at Structure and Interpretation of Computer Programs. It has a lot of interesting stuff about how to find the right balance between genericity and complexity, and how to keep the complexity at a minimum that is really required by the your problem.

And of course, the various agile processes also recommend something similar: start with the simple, refactor when it's needed.

不必在意 2024-09-16 01:02:38

对我来说,过度概括是指是否需要在进一步的步骤中打破抽象。我住的项目中的示例:

Object saveOrUpdate(Object object)

这个方法太通用了,因为它是在三层架构中提供给客户端的,所以你必须在没有上下文的情况下检查服务器上保存的对象。

For me, over generalization is, if there is need to break the abstraction in any further steps. Example within the project, I live in:

Object saveOrUpdate(Object object)

This method is too generic, because it is provided to the client within a 3-Tier-Architecture, so you have to check the saved object on the server without a context.

两人的回忆 2024-09-16 01:02:38

微软有两个过度概括的例子:
1.) CObject (MFC)
2.) 对象 (.Net)

它们都用于在 C++ 中“实现”泛型,但大多数人不使用它。事实上,每个人都对使用这些(CObject/Object)给出的参数进行了类型检查~

there are 2 examples from microsoft in over-generalization:
1.) CObject (MFC)
2.) Object (.Net)

both of them are used to "realise" generics in c++ which most of the people doesn't utilize. In fact, everyone did type checking on parameter given using these (CObject/Object) ~

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