在Golang中如何避免OO思维? - Reddit


问:我的背景是来自php和一点点java,在这种条件下,我已经养成了使用面向对象的习惯,所以当我用go写一个api时,我使用了java中的oo概念,并在go中使用(到处都是指针,大胆的接口和浅层模块),但go不是过程化的吗? 如何在这里最大限度地正确抽象和项目架构来制作一个api?

答:
1、我想说Go并不是特别面向对象,反正不是传统意义上的OOP:它是一个多范式的生态系统,从传统和现代的OOP中借鉴了一些思想,从函数式编程中借鉴了一些思想,并对其进行了精益求精。

传统的OOP是相当有限的。它无孔不入地使用继承,并求助于大量的代码来实现设计模式。有很多Java代码都是这样写的,它的可重用性和可组合性都很差,例如,你不能真的用一个基类替换另一个基类,而保留上面添加的任何行为。我们早就知道继承和名义子类型不是很好,参见围绕Liskov替换原则的讨论。

现代的Java代码就不是这样了,它已经有了很大的发展,然而随着继承被深深地植入到语言和生态系统中,人们在很多情况下还是会伸手去拿老办法。前代Go有它自己的问题,但它确实能够在很大程度上避免OOP。

归根结底,并不是说传统的OOP或典型的Java代码背后的想法没有价值,而是因为那个时代的许多语言根本没有足够的表现力,所以它们被应用得太广泛,太字面化。还有其他(通常是更好的)方法来组织代码:保持函数的小型化和通用化,使用组合而不是继承,编写可组合的抽象(考虑接口/属性/混合)以及其他类似的东西。

我建议寻找并阅读好的Go代码,同时学习最佳实践。无论你学习什么语言,这都是一个合理的通用方法。(你可能最终也想学习其他范式,并学会 "解除 "你过于习惯的东西,这可以提高你对事物进行抽象和推理的一般能力。)


2、我不得不说Go是我知道的最不 "多范式 "的语言。它对问题的正确解决方案有很强的自以为是意见,而且你一般没有什么回旋余地。

FP的影响很弱;它真正的 "函数性 "在于它有带闭包的一级函数。但 "函数式 "在过去15年里并不意味着这一点,因为现在每一种新的语言都把它作为一种特性。现代 "函数式 "意味着的东西,Go几乎完全避开了。


3、也许我对 "多范式 "的使用是混乱的或不恰当的。它确实借用了各种范式,但它在允许你为特定目的作出哪些选择方面并不自由。它是一个混合范式或跨范式。

我想说,我要么夸大了FP的范围,要么你低估了FP的影响,因为你只想到了非常突出的函数方面。我认为FP社区对主流语言的影响至少有以下几点,尽管这并不完全是FP的领域:

  • - 小而纯的(-ish)函数
  • - 透明的结构和抽象
  • - 丰富的静态类型,参数化的多态性
  • - 继承的组合(尽管通常只归功于OOP)
  • - mixin/traits/接口
  • - 控制流的抽象,如迭代器

其中许多不是严格意义上的FP,它们至少有一部分是PL研究界的副产品,以及它们与FP作为领先研究前沿的密切联系。其中一些也与程序化或OO范式有关,然而传统的程序化(如C)或传统的OO语言(老式的Java)并没有在很大程度上应用它们。
是的,FP在这里可能有点宽泛,它并不 "拥有 "这些概念,但由于在研究方面处于相当有利的地位,对它们的发展做出了贡献。现代OOP也是如此。

Go摒弃了 "OOP",就像 "FP "一样,抛弃了类的继承、构造函数等。但我认为我们仍然可以看到其重要的影响。


4、Go是OO的。它有支持抽象的方法,支持多态的接口,用于封装的访问修饰符(导出/未导出是java的public和package-private),用于代码重用的组合。

例如,OO设计模式,在Go中都是可行的。对于你所说的 "OO思想",我认为有两方面的原因:

  • 1) Go没有继承性,如果你做过很多OO设计,这根本不是问题,你可以使用组合来重用代码。
  • 2)很多OO设计模式在Go中是 "过时的"。

关于第一点,OO设计师们可能能感觉到,当他们第一次学习OOP时,都是关于建立类的层次结构,然后你出现在一个真正的Java车间里工作,它就像,"忘记你所学的一切,继承很烂。组成类。只使用接口"。说真的,在我的第一份真正的工作中,我不得不跳到500万行的代码库中,唯一使用继承的类是UI的东西(awt和一些Swing)和一些测试夹具,他们有一些共同的测试设置/拆解代码。

关于OO设计模式的问题,这个社区对这些设计模式有一定的鄙视,但是,因为很多OO设计都是为了弥补语言功能的缺失功能。而Go已经有了这些功能。

  • 策略模式和命令模式:这些都是模拟“函数是第一等级”的方法,类的 "状态 "是模拟“闭包”。Go有这两种模式。不要使用这些模式。(事实上,结构嵌入是自动实现策略模式的语法糖(尽管是在编译时,所以它没有那么有用--但你可以自己写单行委托方法,没有问题),所以这个模式是加倍无用的)
  • 访问模式的存在主要是为了实现类型转换:将结构的遍历与数据解耦仍然很好,但你不需要像访问者那样做奇怪的来回舞蹈来遍历树并确保正确的方法被调用。只要依据类型使用switch。
  • 装饰器:你可以组成接口并隐式地实现它们。所以你甚至根本不需要声明一个接口就可以在语言中实现。只要看看所有围绕io.Reader和io.Writer的标准库的好处就知道了。他们把东西包起来,给你一个有更多旋钮的对象,如果你想调整的话,不需要明确地为它设置一个装饰器。它只是自然而然地发生,而且语言在设计时就考虑到了这种行为。


5、这要看你认为什么是 "OO思想"。以面向对象编程的形式使用抽象是坏事吗?- 我说不是。

有了基于组合的继承和接口(主要是定义的),通过依赖注入来使用上述抽象,这与Golang的目标是一致的。


6、Go是极其面向对象的,它只是没有使用继承来完成它。接口基本上是通向该对象行为的最小子集的大门,而该对象的行为是让做函数所要做的事。


7、Go有可以被合理认为是面向对象的,但面向哪里呢?在Smalltalk(面向对象编程的发源地)、Ruby和Objective-C中,你可以拦截信息,就像面向的定义一样,根据特定的环境上下文或需求调整或定制(某对象)。


Go则没有这样的功能,当然,PHP和Java也没有。Java可能因为James Gosling的Smalltalk背景和Patrick Naughton的Objective-C背景而被混淆为面向对象。这些语言的灵感在Java中非常明显,但实际上Java的对象模型更接近于C++,而错过了其他两种语言的实际面向对象的部分。


8、在我看来,Go基本上是为SOLID面向对象代码而生的。它真的很美。我同意另一位发帖者的观点,你的php Java代码将从Go的编码中受益。


9、不要用接口来弥补继承性的不足。也不要把所有东西都放在对象中,并使用方法(即使只有一个实例)。


10、只有在学习了Go之后,我才完全掌握了OO,而我的Java代码也因此变得更加健壮。OO不是问题,滥用继承才是问题。
区分初级开发人员和中高级开发人员的一种简单方法是:看看他们在代码中添加了多少耦合和/或不必要的抽象。
您会看到很多初级开发人员在构建和编写代码时就好像要在许多不同的地方重复使用它们一样,而不是一次用于手头的特定任务。


11、面向对象的心态在 Go 中不一定是坏的——只是取决于你的项目规模和与之相匹配。对于仅供您使用的简短脚本,请保持过程化,对于长期运行的Web服务器,团队必须维护使用领域驱动设计
Go 中没有继承是对类似 java 的主要改进——你必须使用组合来代替