对我来说,样板代码显然是不好的。但是我遇到了一个开发人员,他在减少样板方面表现出了抵制。我意识到,随着时间的流逝,我对这种厌恶情绪还没有形成,经过深思熟虑的论点。
为了使我能够形成一个令人信服的理由,即主张减少样板,有哪些反对意见?换句话说,支持样板的论点是什么(如果有)?
(我的意思是我通常认为是样板程序,但是一个很好的例子是Java中的getter和setter方法。)
对我来说,样板代码显然是不好的。但是我遇到了一个开发人员,他在减少样板方面表现出了抵制。我意识到,随着时间的流逝,我对这种厌恶情绪还没有形成,经过深思熟虑的论点。
为了使我能够形成一个令人信服的理由,即主张减少样板,有哪些反对意见?换句话说,支持样板的论点是什么(如果有)?
(我的意思是我通常认为是样板程序,但是一个很好的例子是Java中的getter和setter方法。)
Answers:
要记住的一件事是,通常通过删除不必要的上下文来使代码变小。 如果编译器可以解决问题,那么该参数就可以了,无需显式地将其写出。
如果只有编译器打算读取它,那就太好了。但是请记住,“应该编写程序供人们阅读,而只能由机器执行。” (具有讽刺意味的是,这句话来自一本专门针对普通人类阅读的所有语言中最难的一种的教科书,这在很大程度上是因为它过于简洁。)
在您编写时,对您而言可能看起来像是无聊的,重复的样板,对于那些一年(或五年)后又必须维护您的代码的人来说,这可能是有价值的上下文。
特别是WRT Java示例,我同意这是一个糟糕的样板示例,因为它可以用更短,更易读且更灵活的东西代替:属性。但这并不意味着所有语言的所有样板语法元素都像Java和C ++的getter和setter一样浪费。
支持样板代码的一个论据是,如果在一个地方进行更改,则只会影响代码的一种流程。这必须与一个事实相平衡,即,实际上,您实际上希望所做的更改会影响使用该代码的每一段代码。但是我看到了支持该论点的罕见例子。
假设您有一段代码说
public ForTheBar(Foo foo)
{
Bar bar = foo.bar();
return bar.BeFooed();
}
在您的代码中大约2个地方使用了它。
有一天,有人走过来说:“好吧,仅在这条路上,我们希望您先将酒吧放宽,然后再将其放倒。”
而且您认为“这很简单”。
public ForTheBar(Foo foo, bool shouldIGrommit)
{
Bar bar = foo.bar();
if (shouldIGrommit)
{
bar.BeGrommitted();
}
return bar.BeFooed();
}
然后,您的用户添加了一些新功能,您认为它与FooTheBar非常适合。然后,您忠实地问他们,是否应该在Foo之前将其隐去,然后他们说“不,这次不行”。
因此,您只需调用上面的方法。
但是随后您的用户说:“好,等等,在第三种情况下,我们希望您在致电BeFooed之前先涂鸦一下吧台。”
没问题,您认为,我可以做到。
public ForTheBar(Foo foo, bool shouldIGrommit, bool shouldIDoodle)
{
Bar bar = foo.bar();
if (shouldIGrommit)
{
bar.BeGrommitted();
}
if (shouldIDoodle)
{
bar.BeDoodled();
}
return bar.BeFooed();
}
突然,您的代码变得越来越少。也许您应该已经接受了重复的两行代码。到现在为止,您将拥有三段代码,每段2-3行,并且看起来不再重复。
综上所述,我将用“这是不常见的情况来反驳,当它发生时,您可以进行重构”。
我最近听到的另一个论点是样板代码有时可以帮助您浏览代码。我们正在讨论的示例是删除大量样板映射代码并将其替换为AutoMapper的示例。现在,有人争辩说,因为所有内容都是基于约定的,所以您不能对IDE说“该属性集在哪里”,并且希望它知道。
我见过人们争论有关IoC容器的类似问题。
并不是说我同意他们的观点,但这仍然是一个公平的论点。
您从此开始:
<p>
<label for="field">My field</label>
<input type="text" id="field">
</p>
那么您就可以摆脱所有烦人的样板并将其放在函数中:
createFieldHtml( id, label )
很好,我节省了很多行!
createFieldHtml( id, label, defaultValue )
是的,我也需要一个默认值,很容易添加。
createFieldHtml( id, label, defaultValue, type )
很酷,我现在也可以将其用于复选框
createFieldHtml( id, label, defaultValue, type, labelFirst )
UX设计人员说标签必须在复选框之后。
createFieldHtml( id, label, defaultValue, type, labelFirst, isDate )
现在可以在需要时呈现日期选择器。嗯..参数有点失控了
createFieldHtml( id, label, defaultValue, type, labelFirst, isDate, containerCssClasses )
在这种情况下,我需要添加CSS类
createFieldHtml( id, label, defaultValue, type, labelFirst, isDate, containerCssClasses, fieldCssClasses, disabled, clearAfter, helpText, uploadPath )
aaaaaaaaaaaaaaaaaaaaaaa
我很难说出来,因为这确实是我最近才注意到的事情,所以我列出一个清单:
我最近经常问自己一个问题是:
我可以在不做任何更改的情况下将其复制并粘贴到另一个项目中吗?如果是,则可以封装或放入库中;如果否,则是样板时间。
这与通常的样板是复制和粘贴代码的观念大相径庭。对我而言,样板是关于复制和粘贴的,但始终必须对其进行一些微调。
更新:我刚刚碰到一篇文章,上面给出了我的示例的实际名称:“过于干燥的反模式”。
该函数获取更多参数,并具有越来越复杂的内部逻辑来控制不同情况下的行为。太干的功能很容易发现。它们具有许多复杂的if-then逻辑,试图解决各种用法。[...]而且,如果代码很小并且执行离散功能,那么重复代码并不总是一件坏事。
这是一篇简短而有趣的文章,您可以在这里找到该文章:过于干燥的反模式
我鄙视样板代码,但是能够删除样板代码并不总是意味着这是最好的方法。
WPF框架具有依赖项属性,其中包含大量的样板代码。在我的业余时间里,我研究了一种解决方案,该解决方案极大地减少了需要编写的代码量。一年多以后,我仍在对该解决方案进行改进,并且仍然需要扩展其功能或修复错误。
问题是什么?为了学习新知识和探索替代解决方案,这很棒,但这可能不是最佳的商业决策。
WPF框架已有详细记录。它正确地记录了如何编写样板代码。尝试删除此样板代码是一个不错的练习,虽然绝对值得一试,但是要达到与msdn提供的相同的“抛光度”需要很长时间,而我们并非总是如此。
我将采取不同的策略。开发的一致性是软件设计的最重要功能之一,它是使应用程序可扩展和可维护的关键工具,但是在跨多个站点,不同语言和不同时区管理团队时可能很难实现。
如果实现了一致性,那么“一旦您看过代码,您就已经看到了所有代码”,一致性将使代码更易于访问,维护和重构的成本要便宜得多,但最重要的是,易于扩展。当您编写一个需要一些样板的库以及库的强大功能时,您还获得了开发人员的支持:
当您的库的使用者对实例化有所了解时,他们可以轻松地创建私有/扩展方法,父类甚至模板来实现样板代码。