这篇文章是系列文章的一部分,在该系列文章中 ,我将尽我所能组织有关Go的想法:它的范例和作为编程语言的可用性。 我以尊重优雅对象原则的Java程序员的身份编写的。
我正在研究Go Code Review的咒语“ 接受接口,返回结构” ,并在Go的database / sql包中遇到Eli Bendersky的post Design模式后受到启发写这篇文章。 这是我第一次可以自信地赞同这一口头禅。 Eli在分析database/sql
的体系结构方面做得很好–我只是在这里提供一些细微差别和一些我自己的注释。
问题陈述
对于最常见的用例,应用程序程序员需要在各种SQL或类似SQL的数据源上的数据库抽象层 。
设计DAL很困难,主要有两个原因:
- 各种各样的数据库实现和驱动程序
- 多种常用案例
- 欺诈
任何解决方案都必然会成为胖API ala sql.DB
或JDBC
。
数据库/sql.DB
有趣的是, sql.DB
是具体类型,而不是接口。 为什么?
由于Problem语句中列出的原因, sql.DB
公开了一个胖接口。 有几种限制API 肥胖的策略 1 – 接口分离原则的 database/sql
和JDBC
选择。 database/sql
通过做多于一件事情使事情变得混乱,而JDBC
提供了更清晰的关注点分离。
由于sql.DB
必然是脂肪2 , 3 ,使得它的界面将仅依赖于该故障码 :这是痛苦的,浪费必须实现在您的生产代码,并在你的嘲笑所有这些方法时,你只需要3或4由于这些原因,程序员通常更喜欢设计外观或适配器 ,并将其放置在胖API的前4个 。 在Java和Go中,这个额外的组件都可以是具体类型或抽象类型。
YAGNI :使用具体类型还是抽象类型取决于您是否真正需要额外的抽象级别。
将用户界面与驱动程序界面分离
关于database/sql
为什么将用户界面sql.DB
与driver.Driver
,Eli指出:
- 添加面向用户的功能很困难,因为它们可能需要向接口添加方法。 这破坏了所有接口的实现,并需要多个独立项目来更新其代码。
- 封装所有数据库后端通用的功能非常困难,因为如果用户直接与DB接口进行交互,则没有自然的地方可以添加它。 它必须为每个后端单独实现,这既浪费又后勤复杂。
- 如果后端要添加可选功能,那么在不使用特定后端类型转换的情况下,使用单个接口就具有挑战性。
这些观点是正确的,但我认为此设计决定背后的首要主题是简单和易用 。 适当的接口隔离和关注点分离5占据了后座,所有这些都导致在单个接口中混合了几个正交需求:
- 执行查询
- 连接池
- 线程安全
他们决定自己实现连接池和线程安全,而驱动程序只需要提供连接(和语句,以及从连接派生的其他所有内容)。
明显违反SRP 6的好处是为用户提供了更简单的学习曲线(它们暴露于单个简单的界面)和供供应商实施的更简单的驱动程序界面。
不利之处在于,维护人员负担的代码维护工作不一定能满足所有用户的需求,并且可能扼杀Golang 7在该领域的创新。
结论
database/sql
通过提供一个简单的API缓解了用户的学习困难,该API仍然打破了单一责任原则,并且在该程序包中引入了正交但有用的功能,从而可能阻碍创新。
sql.DB
最好以一种具体的类型而不是一种接口的形式呈现,因为它的要求必然会使它膨胀为胖API,从而极大地减少了接口以结构化语言(如Go)返回的任何返回值。
1 aka“表面积”,但是,嘿-因为我们在谈论“ Fat API”,我们不妨使用它:)
2合理地打破Go谚语 界面越大,抽象度越弱
3请参见Elegant Objects vol 1的 2.9节。 使用智慧
4又名“换行”,但我不喜欢该术语,因为其含义已被淡化,并且可能涉及几种不同模式中的任何一种
5 在这里查看database/sql
设计参数
6 单一责任原则
7考虑了Java生态系统中各种各样的数据库连接池库,以及它们各自如何强调不同方面,例如易用性,性能,功能等。
8参见Rob Pike的Google Go的 痛点 :软件工程服务中的语言设计,第4节“缓慢的构建”和“不受控制的依赖性”。
翻译自: https://www.javacodegeeks.com/2019/04/golang-the-database-sql-package.html