你觉得过度概括是什么意思?

Cog*_*eel 12 c++ templates generic-programming

花了一些时间在Haskell和其他函数式语言中玩,我开始意识到设计的简单性来源于描述一般问题.虽然模板编程的许多方面可能远非简单,但有些用法很常见,我认为它们不会妨碍清晰度(尤其是函数模板).我发现模板通常可以简化当前设计,同时自动添加一些未来阻力.为什么他们的功能应该降级为图书馆作家?

另一方面,有些人似乎避免像瘟疫这样的模板.十年前,当泛型类型的概念对于大多数编程社区而言是陌生的时候,我能理解这一点.但是现在所有流行的静态类型OO语言都支持这种或那种形式的泛型.增加的熟悉程度似乎需要调整保守态度.

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

你永远不应该做出比必要更通用的东西 - 软件开发的基本规则.

我真的很惊讶地看到这种说法如此夸张,好像应该是不言自明的.就个人而言,我发现它远非不言自明,对于像Haskell这样的语言,除非你另有说明,否则一切都是通用的.话虽如此,我想我明白这种观点来自哪里.

在我的脑海里,我确实有类似的规则在喋喋不休.现在它处于最前沿,我意识到我总是根据整体架构来解释它.例如,如果您有一个类,则不希望使用您可能有一天使用的大量功能加载它.如果你只需要一个具体的版本,就不要费心去做接口(虽然可模拟性可能是对这个版本的反驳).像这样的东西...

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

所以你觉得怎么样?您认为过度概括是什么?根据具体情况,此规则是否具有不同的适用性?你甚至同意这是一个规则吗?

Kat*_*ory 13

过度概括让我发疯.我不怕模板(无处可去),我喜欢一般的解决方案.但我也喜欢解决客户支付的问题.如果这是一个为期一周的项目,为什么我现在资助一个月的盛会,这不仅会通过明显可能的未来变化(如新税收)继续发挥作用,还可能通过发现新的卫星或火星生命来实现?

把它带回模板,客户端要求一些功能,包括你编写一个带字符串和数字的函数.你给我一个模板化的解决方案,它采用任何两种类型,并为我的具体情况做正确的事情,并在其他情况下做出可能或不正确的事情(由于没有要求),我将不会感激.我将被告知,除了付钱给你之外,我必须付钱给某人测试它,有人记录它,以及有人在未来的约束下工作,如果一个更普遍的情况应该发生的话.

当然,并非所有概括都超过概括.一切都应该尽可能简单,但并不简单.尽可能一般,但不一般.尽可能经过测试,但没有经过测试.等等.另外,"预测可能会发生什么变化并将其封装起来." 所有这些规则都很简单,但并不容易.这就是为什么智慧在开发人员和管理他们的人中很重要.


Mat*_*tis 11

如果你可以在同一时间完成,并且代码至少同样清晰,那么泛化总是优于专业化.

有一个原则,XP人跟随叫YAGNI - 你不会需要它.

维基有这样一段话:

即使你完全,完全,完全确定你以后需要一个功能,现在就不要实现它.通常,它会变成a)你毕竟不需要它,或者b)你实际需要的东西与你之前需要的东西完全不同.

这并不意味着您应该避免在代码中建立灵活性.这意味着你不应该根据你以后可能需要的东西来过度设计.


Mat*_* M. 6

太通用了吗?我必须承认我是通用编程的粉丝(作为一个原则),我真的很喜欢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);
}
Run Code Online (Sandbox Code Playgroud)

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

这就是为什么我自己欣赏最小努力的原则.当我想到一个班级或方法时,我先退一步思考一下:

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

根据anwsers,我调整了通用度,我努力避免过早锁定,即我避免使用非通用的方法,因为使用稍微更通用的方法不会花费太多.

例:

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

// VS

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

它不仅更通用(我可以指定输出的位置),它也更具惯用性.


Joe*_*ips 4

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

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