能接触到的常见问法:
项目中有用到什么设计模式?(前端更多点)
重构使用了什么设计模式?(如果你项目强调了重构一般会问)
说说对 XX 模式的理解?(个人接触过的是单例和工厂,这两个比较多)
有接触到哪些?实际用到过哪些?(通用)
除了这些,还有一些不常见的进阶和基础问法,这些直接在下面整理
--
设计模式这个主题还挺玄乎的,个人直观体验是,自己日常实习那段时间问得挺频繁的,可能是因为没啥好问的。
找暑期实习的时候就问的少了。可能设计模式的定位还真是,个人体验也是,没有大量实践注定没有深刻的理解,对校招生的要求不会那么高吧。
所以我感觉一般策略还是要强调,能想到什么说什么,展示自己对设计模式有一个基本理解,有刻意去考虑使用设计模式这种感受。
作为面试 fw 就不多说了,这里就自己能想到什么就整理什么呢。参考价值不大,可能更多的还是要结合自己项目。
--
OOP 有哪些设计原则?如何理解?(这个可能 cpp 问的比较多,go 倒是不常问)
● 封装。定义是我们通过向对象传递消息来直接执行方法,隐藏方法细节,而不是面向过程那样直接展示方法。这个基础上扩展,才有了私有方法变量这种进一步的封装。
● 继承。继承就是字面继承一个类是属性方法,实现复用和多态。
● 多态(比较常问),定义是对象在不同条件在统一接口下有不同的行为。go 主要通过接口实现,java 和 cpp 也都是分解通过继承和接口/虚函数实现。
○ cpp 函数重载是一种「编译时多态」,不太符合传统理念上的多态。
○ 策略模式的设计模式,同一对象在不同环境下使用不同算法,就是一种常见的多态设计。比如 orm 框架适配多种存储策略,cpp 排序 stl 适配多种排序算法。
如何理解设计模式和设计原则?
● 区别简单来说,设计原则是一些比较抽象的知道原则和方向,设计模式则是一些具体的时间。
● 设计模式分为创建型、结构型、行为型三种。
● 五大设计原则 SOLID:
○ 单一职责:一个类只负责一个功能,具体一般也扩展到一个模块只负责一类功能,一个函数只负责一件事情。
○ 开闭:对扩展开放,对修改关闭。这个一旦沾了企业开发确实就接触的挺多,说到这个想起我上段实习就是上去就把代码给重构了被臭骂了一顿。总之这个的意思简单说现有的代码不应该乱改,影响稳定影响线上功能要回归测试。好的做法是,有接口留够扩展性在那(这个需要之前设计的人有实力),你的想做的新的需求应该是在现有的基础上去做扩展。还有一个常见的例子是,假设你有一个新需求,你现在有一个线上接口可以通过加参数来实现,这种情况更好的做法是另起一个新接口,因为你改改逻辑就顶天了,改函数签名影响可能就比预想的要大很多了。
○ 里氏替换:子类必须能替换父类,保持行为不变。这个 go 里边用不到。覆写不违反 lsp 原则,只要不改变预期行为。
○ 接口隔离:客户端不应该知道他不知道的功能。在 go 里面能考虑的地方还挺多的。首先接口设计上,接口要尽量小,去发现接口而不是事先创造接口。其次,go 里面接口独特的机制是隐式实现,所以双端同时使用接口不会有循环依赖问题,所以我们常见的一种实现是服务端负责完整抽象,客户端单独做他的部分抽象。这个就是经常说的,在 go 里面“避免完成生产侧的接口”
○ 依赖倒置:定义是上层不应该依赖下层,而都应该依赖抽象。抽象不依赖细节,细节以应该依赖抽象。设计目的是为了创造耦合性低的依赖关系,所以 DIP 往简单了说就是解耦。此外控制反转,IoC,一种具体实现 DIP 的设计模式,不手动控制对象把控制权交给容器或者框架,其实反面例子就是面向过程的手动控制对象的创建、行动相关操作。依赖注入DI,一个具体的行为,也可以说 IoC 的实现方法之一但是不只是在 ioc 里面用到,就是把对象直接作为参数传入另一个流程的一个写法而已,例子有很多,比如 vue 里边就可以注入全局变量来解决深参数传递的问题。
● 五原则回顾,开闭和依赖倒置比较常问。这两个是有点像的,不过区别也很明显,虽然都可以借助接口实现,不过一个目的是扩展一个目的是为了解耦。
常用的几个设计模式?
● 工厂:一些定义和典型实现是,专门用一个工厂类来封装创建对象的逻辑,类的创建延迟到子类。好处是封装、低耦合(创建的逻辑是独立的内聚的)、便于扩展(要增加新的创建方法的话,直接替换掉原来的工厂方法调用就好了)。
● 单例:目的是全局就是只能只应该唯一实例,否则并发问题或者管理混乱。例子像是 logger,confger,连接池,cache。然后恶汉懒汉 doublecheck 确保能单例实例化。
你 go 项目里会用到设计模式?(Java 废物,Java 那块的 aop,ddd 就不整理了)
不多说,直接提供例子,这种一般你没实际写过,能答出来面试官也不会感兴趣,所以其实一些过于抽象的模式,像是代理、桥接也就不必说了
● NewXXX()是一种简单工厂模式
● sync.Once()实现单例
● 注册模式:实现全局 init 对象到一个 map 里
● 责任链模式,我们不管请求最后会怎么样,没法解决的问题往下传,“丢锅”:gin 的中间件机制。
● WithOption、可变参数、函数闭包 实现 选项模式 https://goplay.tools/snippet/xpnL6BhCsJa
● 建造者模式就是比方我们用个 resty类似的 http 库,来发一个请求 setHeader
● 策略模式比较常见,interface 多种算法实现都是策略模式,比方说多种灰度方案限流方案数据库连接方案
偶见的设计原则开放性场景题:假如有一个历史包袱很重的项目丢给你重构,你会考虑到哪些设计原则和设计模式?(这个很考验积累了,我能做到的只能是分享这个问题和目前的浅见解了,欢迎探讨)
● 没话说就是上面的原则逐个说一遍就好了
● 肯定要强调的是模块化,高内聚低耦合,原因能想到的也围绕一些常用的点说:可维护性、复用性、扩展性、稳定性、简单性。有点八股性质了,具体还是看经验结合例子说。不过能想到这些角度感觉也是有意义的。
--
卡着实习 ddl 昨天实在没写完,精力管理确实重要。决策消耗心智大概到极限了,优化空间在于把实习的事情看得更轻一点,都打算跑了根本不用在意那么多。不过实际做起来还是很难协调,实操性大一点的地方还是在于尽量把排期铺开,分不出时间就延期。
中断一天,重新开始
#每天一篇简单博客 day2-1
(个人打卡,欢迎监督