用 Java 和 Go 写了 23 种设计模式的例子实现。大家有感兴趣的可以看看看,给个 star ,有想法可以交流讨论下,互相学习 👀
Github 地址: https://github.com/yuluo-yx/design-pattern
![]() |
1
Ketteiron 9 天前 ![]() 我对设计模式的理解
单例模式:部分落后的面向对象语言特有的反模式,违反单一原则,一个类不仅要实现功能,还需要管理自己的唯一实例。Java 中已由 SpringBoot 解决了底层的垃圾活,如果需要自行实现单例我只会觉得这人的脑袋坏掉了。 大部分情况出自 Java 面试题,经典且无用的八股文,仅仅为了考察根本用不到的懒加载、指令重排。 对于更先进的面向对象语言很好解决,例如 kotlin 的 object ,js 的{},跳过 class 这一步直接创造对象就行了。 至于 Go 呢,package 是单例,sync.Once 可以实现延迟加载。 工厂、建造者、原型模式:Java 有用的设计理念,但是 Go 没有继承/重载,只能写点似是而非的东西。 但作为面试题,这三个只比单例好一点,也几乎没有实际意义。 代理、装饰器模式:由于 SpringBoot ,在 Java 里它们是基础设施,是面试题应该重点考的东西,可以避免一些错误的使用方法。 适配器模式:非常好的理念,编程是在破旧的茅草屋上缝缝补补,拆东墙补西墙,适配器让各种屎山能成功运转起来,是一座丰碑。 很多设计模式我只在自己的玩具项目上尝试引入,确实有些是很好的理念,但工作中我不会自找麻烦。设计模式要为代码实现服务,如果使用了设计模式能少写代码,好写代码,那么是好的,可惜事实并非如此,绝大部分业务/场景的复杂度远达不到 Springboot 框架的程度,强行引入一个又一个设计模式不如去干点更有意义的事。 例如可以折腾下多平台聊天机器人之类的玩具,真心要写得好这些设计模式几乎都会用到。 正常的话是不应该过度使用设计模式的,Java 还好说,Go 去凑合设计模式是没苦硬吃。 如果真的对设计模式情有独钟,就多写点呗,23 种设计模式已经是三十年前的破烂了,要与时俱进挑战一下 62 种: https://github.com/iluwatar/java-design-patterns |
2
vultr 9 天前
懂道设计模式还是有必要的,用不用是另一回事。
|
![]() |
3
veike 9 天前 ![]() 关于设计模式如何我亮出这个网站,阁下准备如何应对
https://refactoringguru.cn/ |
4
pengtao2001 9 天前
@veike 还真是哈哈哈
|
5
craftsmanship 9 天前 via Android
求求了 Java 就 Java 别带上其他语言了 削足适履真的难受
|
![]() |
6
xiuming 9 天前 ![]() golang 是组合式编程
|
![]() |
9
mightybruce 9 天前 ![]() 看了几眼,在 go 中用法错误,属于强行套用 OOP 在 go 上面
https://github.com/yuluo-yx/design-pattern/tree/main/struct-type/07_decorator 这个例子就是 go 语言用法错误 正确写法在这里 https://coolshell.cn/articles/17929.html |
![]() |
11
Ketteiron 9 天前 ![]() @wogogoing #8 哪里有问题?
Go 保证一个 package 只会初始化一次,包内的变量是包级作用域的全局变量,全局唯一,这不就是天生的单例。 扩展阅读: https://stackoverflow.com/questions/1823286/singleton-in-go `Just put your variables and functions at the package level.` 这能够解决 99%使用场景,如果无法解决,参考排名第一的回答。 |
![]() |
12
darksword21 PRO ![]() 应该是大脑被 java 造成了永久性损伤
|
13
sky3hao9 9 天前
大脑被 java 污染后, 再写别的语言, 容易抹黑人家
|
![]() |
14
gowk 9 天前 ![]() 看到前面几楼都在吐槽用设计模式污染 Go ,确实不能照搬,Go 有自己的惯用法( idioms ),好的代码不是设计模式堆砌出来的,而是解决实际问题、可读性强、易于维护的代码。
|
15
encounter2017 9 天前 ![]() 只写 java 的是这样的,建议也多看看其他语言,所谓的设计模式,在现代的编程语言中,很多都内化成语法了,举几个例子:
策略模式:一等函数公民(js/ts),匿名函数 (c#), 类型类( scala ) 构造者模式:case class (scala), record(jdk 17), data class(kotlin), dataclasses/pydantic (python ) visitor 模式: 代数数据类型 (rust), sealed trait + 模式匹配 (scala) 单例模式: 语法关键字支持( scala/kotlin ), 类本身就是 (ruby) Decorator 模式:Decorator 注解(python) |
16
encounter2017 9 天前
引用下 https://refactoringguru.cn/design-patterns 的一段话:
设计模式自其诞生之初似乎就饱受争议, 所以让我们来看看针对模式的最常见批评吧。 一种针对不完善编程语言的蹩脚解决方案 通常当所选编程语言或技术缺少必要的抽象功能时, 人们才需要设计模式。 在这种情况下, 模式是一种可为语言提供更优功能的蹩脚解决方案。 例如, 策略模式在绝大部分现代编程语言中可以简单地使用匿名 ( lambda ) 函数来实现。 |
![]() |
17
kuanat 9 天前 ![]() 设计模式是一种经验总结,属于应对特定问题的一般策略。实际上就算你一点设计模式不懂,一样能写出能用的代码,只是可能比较复杂容易出错,或者看起来没那么简洁易懂。当你反复遇到相同的问题时,很有可能自己重新发现并总结出某种设计模式。或者说你可能早就在用各种设计模式了,只是没有总结成特定的名词而已。
但所谓的设计模式毕竟是经验总结啊,也就是说它是有适用范围的。不能因为手里拿着设计模式的锤子,就看什么都是钉子。不同的编程语言差别很大,解决相同问题的经验是完全不同的,甚至在一种语言中的常见问题在另一种语言中甚至不存在。 举个例子,就拿参数传递来说,Java 因为不支持命名参数但支持重载,所以总结出了 builder 模式,而 Go 因为没有重载和构造函数,所以形成了函数式 Options 的习惯写法。非要 Go 用 builder 模式或者 Java 写函数式 Options 不是不行,只是没必要。 再比如楼上提到的装饰器模式,Java/C++ 这种常用是因为它们的 OOP 都是基于继承的,在不修改原始类的情况下动态增加新功能是比较复杂的,装饰器这个写法可以避免继承爆炸。换到 Go 里面正经人谁用装饰器啊? Go 的 OOP 是基于组合的,简简单单嵌入 struct 不就可以了。 当然面试八股确实喜欢考这些东西,但属实没必要把设计模式当作编程学习的目标。 |
![]() |
18
yb2313 9 天前
写 java 写的
|
19
pengtao2001 9 天前
@Ketteiron 发现新大陆
|
![]() |
20
midsolo 9 天前
设计模式是一套经验思想,只要语言实现了面向对象特性(封装、继承、多态),那么都可以使用设计模式。Java 只需要使用接口跟抽象类,把行为和对象隔离,保证高内聚低耦合。只要不需要直接依赖,那就引入中间层,你要什么设计模式都可以帮你抽。
一些菜鸡面试官自己都没理解,还喜欢问这些,如果你不按照他的认知去回答,他还觉得你在胡说八道,难崩。 |
21
uds9u32br 9 天前
设计模式这玩意,该用的地方自然就会用,不该用的时候别嗯套。
当你写代码思考‘这里该用什么设计模式’的时候,就是不该用。 |
![]() |
22
mmdsun 9 天前 via iPhone ![]() |
![]() |
23
karashoukpan OP @uds9u32br 赞同观点
|
![]() |
24
karashoukpan OP ![]() |
![]() |
25
mightofcode 8 天前
不如分享下怎么用 AI
|
26
zhengxin1993 8 天前
除了几个结构型模式,其他模式都是语言自身实现不了形成的,比如 java 一个 clone 就实现了原型,就不怎么谈这个设计模式,最终都会变成内在语法,不如多看看其他比较现代化的语言。
|
![]() |
28
darksword21 PRO @mmdsun 如果真是人身攻击的话没这么好听,还有别诬陷我,我没说编程语言有三六九等,道德侠玻璃心,一点屁事就 @站长
|
![]() |
29
Ketteiron 8 天前
@iseki #27 这个单例或许加个引号更合适,确实不是"例",一时也想不出更贴切的名词,本质上都是全局变量的唯一访问点。部分语言提出单例概念是因为变量必须放在 class 内,因此催生了一个绕过方法。假如语言支持在 class 外也有作用域,那么全局变量的载体不一定要是对象,js 可以是 module ,go 可以是 package ,也没必要实例化了,它们是语言级别的特殊对象,也能实现对象的特性(一次初始化、修饰符、延迟加载),而二者的具体差异可以忽略不计(例如静态内存 vs 动态内存)。
写 Java 的也从来不会用单例,用的是依赖注入默认情况下表现出来的单例特性,这其实也不是单例。 |
![]() |
30
mmdsun 8 天前 via iPhone
@darksword21
如果没记错,前几天你在另一个帖子也发过“Java 造成永久性脑损伤”的言论。 可能 Go 用久了会变成谷歌的安卓嘴,语法复读症越来越重。😂 error handling 造成的语法性心理创伤,也不比 Java 好到哪去——应该 Go 损伤的,是灵魂吧。 |
![]() |
33
darksword21 PRO @mmdsun 理中客装不下去了自己开始人身攻击了,笑死
|
34
mgaic 8 天前
@darksword21 你不能只在别人攻击你的时候才说这是人身攻击
|
![]() |
35
darksword21 PRO |
![]() |
36
midsolo 8 天前
@mmdsun #22 他俩只是在调侃,你别太当真了。
我写了 5 年的 Java ,刚转 Go 的时候,大脑也受损了,还是 Java Spring 的那套思想。需求一下来,我就想建立多层次的数据模型,然后用接口来定义能力,用抽象类来区分子类的功能,然后再把能力组合起来,用各种设计模式...... 这样一个需求可能要创建很多个类,典型的 “面向对象 + 接口 + 抽象类 + 设计模式“ 的 Java 编程思路,大约写了 8 个月的 Go ,才丢掉这套思想。 |
![]() |
37
Ketteiron 8 天前
@iseki #32 确实如此,虽然多范式语言并不关心这点。singleton (单例) 与 monostate (单态) 都是编程语言在不支持顶层作用域函数/变量的情况下管理全局函数/变量的方案,缺点都差不多,相比之下单态更加差劲。
https://wiki.c2.com/?MonostatePattern 以 kt 举例,顶层 object 基本都被用成了 namespace ,我很少见到把它当单例用的。 |
38
iseki 7 天前 via Android
@Ketteiron 那你 Kotlin 可能还是写少了(笑),你看 x 库和标准库中都有很多利用 object 的可被引用的特性的地方。
他们不是单例就不行吗?当然不是,你大可把全部成员变量都写到顶层 private var 上去,但是你没法让一个 .kt 文件 implements 某个 interface 。Kotlin 选择了 companion 而没有直接像 Java 一样暴露所谓的 static member 正是表达了在这一点上的取向。 |
39
kakki 7 天前
Java 罪大恶极
|
![]() |
40
Ketteiron 7 天前
@iseki #38 A 持有 B 的引用且能修改,是 OOP 的罪恶根源,标准库基本都是无状态/不可变对象(除了和 java 互操作的可变对象),但最下游的消费者还是应该尽量多用函数式。
对一些全局变量,我的通常做法是设为私有顶级成员,相比 companion object 少写 3 行。(FP 致力于消除一切全局可变变量,理论上都是"错误"用法)。 interface implements extends 是类型约束的一部分,kotlin 作为多范式语言提供了更多的可选组合,例如用 object 委托实现适配一些接口,或者用函数类型+高阶函数进行限定(实现接口本质是提供一个符合签名的函数)。多范式语言提倡组合优于继承(委托、扩展函数),函数一等公民(顶层函数、高阶函数、闭包),使用语言提供的单元(module/package/.kt)而非 interface/class/object 组织代码就变成很自然的事,真要用接口也尽量用 SAM 。 写 kt 我一般还是建议不要太沉迷 OOP 的特性,多提升 FP 含量,少点过度抽象少写点代码。 https://www.reddit.com/r/Kotlin/comments/kziv7e/a_closure_is_a_poor_mans_object/ |