读《后端技术面试38讲》--学习笔记和读后感 (二)

学习笔记和读后感

主要记录阅读《后端技术面试38讲》课程的学习笔记和一些自己的工作和学习体会。
课程所讲的内容是比较系统性的讲了后端技术开发的技术理论,比较适合于有六七年以上开发工作经验的软件开发人员,如已经从事过一些项目的架构设计工作,则能够更系统的理解课程内容;在阅读过程中将自己的工作感悟和既往的技术学习内容,不断地表达出来,则能更充分的提升对软件工程的系统认知和表达能力。

本篇接续上一篇笔记
读《后端技术面试38讲》–学习笔记和读后感 (一)

继续对课程的软件的设计原理部分,做笔记的整理输出并表达自己的理解和看法

08丨软件设计的方法论:软件为什么要建模?

软件建模的必要性: 领域问题 --> 模型 --> 软件系统
4+1视图 :
逻辑视图:功能逻辑 , 模块关系
开发视图:系统层次划分 、包管理 、依赖的第三方包 ,一个程序包一般对应一个功能模块
过程视图:动态的描述程序运行期 进程、线程、对象实例 ,以及并发、同步、通信等
物理视图: 部署架构 服务器间的通信
场景视图:业务角度的功能流程, 展现如何被技术方式的视图解决
UML: 统一建模语言 便于沟通 帮助自己思考设计

感想:
建模主要是将对领域问题的抽象化思考想法表达出来,通过建模的过程也能够促进对问题的分析理解;其次架构师在工作过程中需要将设计方案呈现给开发人员和业务人员,需要采用合适的方案表现形式,模型起到了呈上启下的指导作用,另外在对软件系统的后续测试和验证也起到关键作用;再次对于复杂系统往往会逆向的建模 以便更好的理解软件系统;最后讲模型组织成一份有价值的设计文档是架构设计的主要工作 。

09丨软件设计实践:如何使用UML完成一个设计文档?

UML的常用模型图:
类图 关联、依赖、组合、聚合、继承、泛化
序列图 生命线 、激活条;交互
组件图 依赖关系
部署图 了解系统运行的样子,和现有及第三方服务器的关系, 估算成本
用例图 角色 、 功能(调用依赖 、扩展)
状态图 状态及变迁关系(转化条件)
活动图 圆角矩形 活动 、 菱形 判断 , 泳道
需求分析阶段: 用例图、时序图、类图
概要设计阶段:部署图、组件图、时序图、活动图
详细设计阶段: 类图、时序图、活动图

感想:
UML精粹 那本书还是不错的 ,讲的更详细,这块的使用比较熟练

10 | 软件设计的目的:糟糕的程序员比优秀的程序员差在哪里?

描述什么是糟糕的设计 ,反向对比 :
僵化性 小改动会引起大范围改动
脆弱性 小改动会引莫名其妙的bug
牢固性 无法进行快速有效的拆分 ,无法轻易实现低耦合
粘滞性 阻碍改进 糟糕的方案 更容易实施
晦涩性 难以看懂理解
对应的正向特性 : 灵活、强壮、易于使用、阅读和维护
应对需求变更最好的办法就是一开始的设计就是针对需求变更的,并在开发过程中根据真实的需求变更不断重构代码,保持代码对需求变更的灵活性。

11丨软件设计的开闭原则:如何不修改代码却能实现需求变更?

软件实体(模块、类、函数等等)应该对扩展是开放的,对修改是关闭的
使用哪些模式可以实现开闭原则:
策略模式
适配器模式
观察者模式
模版方法
感想:
实现开闭原则的关键在于抽象 ,利用好面向接口编程 、抽象方法 、模版方法(小框架)

12 | 软件设计的依赖倒置原则:如何不依赖代码却可以复用它的功能?

依赖倒置原则:
高层模块不应该依赖底层模块,二者都应该依赖抽象
从高到低的传递依赖: 高层申明所需的服务接口 底层对接口进行实现 ,接口所有权的倒置
抽象不应该依赖具体实现,具体实现应该依赖抽象
遵循依赖倒置原则有这样几个编码守则:
1、应用代码中多使用抽象接口,尽量避免使用那些多变的具体实现类。
2、不要继承具体类,如果一个类在设计之初不是抽象类,那么尽量不要去继承它。对具体类的继承是一种强依赖关系,维护的时候难以改变。
3、不要重写(override)包含具体实现的函数。
感想:
依赖倒置 使得代码的复用和依赖关系变得灵活,框架的设计、对第三方服务的调用等场景中包含较多依赖倒置原则的使用 ; 对于现实生活或工作中也可以使用依赖倒置的模式 来处理协作,理清责任和工作范围, 如前后端对接接口时 可以让前端负责提出接口需求,对输入输出做好限定, 后端对给定的限定 可以做扩展,但不应变更,如变更则需要前端的同意 。
另一个例子 来源这个课程的评论: 老板就是找个写代码的人,别把自己看的那么重。你们每天写那么多bug,别怪老板说要换人。他们才理解! 老板定义了诉求 需要写代码的人, 特定员工-如程序员甲 可以写代码 ; 其他人也是可以写, 这样在满足条件的情况下 可以更换写代码的人。

13丨软件设计的里氏替换原则:正方形可以继承长方形吗?

继承的使用
里氏替换原则
子类型必须能够替换掉它们的基本型 ,即:所有使用基类的地方,可以使用子类代替

在具体使用继承时,需要依据场景将子类带入到基类的行为场景中进行验证,看是行为结果是否能符合基类预期,如不符合特性,则继承可能并不合适。 如 课程举例 正方形继承长方形的例子,长方形计算面积 之前设置了长和宽, 3,4 面积12; 正方形 其实只能设置一个边长 4, 计算面积会是16, 特性不相符 ,因而不适合继承。

通常来说,子类型不能比父类型更严格,是违反里氏替换原则的,对于不是抽象类或者接口,一般不建议继承
组合优于继承: 通常说的是如果只是为了使用这个类的方法,则推荐使用组合方式,这种也是对象适配器模式

14 | 软件设计的单一职责原则:为什么说一个类文件打开最好不要超过一屏?

Servlet 做请求响应 内部直接拼接输出 html
JSP 做请求响应 , 再去调用业务模型 ,动态组合结果输出
MVC 模式 控制器做请求响应 , 调用视图层 和 业务层(业务层 -> 服务层 -> 数据持久层)
逐步拆解分离

感想:
对复杂业务功能 需要做好拆解分析 ,分步骤分模块来实现

15丨软件设计的接口隔离原则:如何对类的调用者隐藏类的公有方法?

接口隔离原则 :调用接口 而不是直接调用实现类, 实现类可以实现多个接口, 每个接口有不同的侧重面, 调用者依据自身场景 选择合适的侧重面的接口,从而实现隔离保障。
多重集成 :继承某个类 并实现某个接口, 达到复用和扩展的目的,之后注册到某个管理类中, 实现一个隔离性
接口隔离原则在迭代器设计模式中的应用 // for(var meta : collections){}

16 | 设计模式基础:不会灵活应用设计模式,你就没有掌握面向对象编程

对多态的理解和使用 面向对象本质是多态,设计模式的精髓是灵活使用多态

17 | 设计模式应用:编程框架中的设计模式

框架是对某一类架构方案复用的设计与实现,
层次:
软件应用程序
软件编程框架
面向对象的设计模式
面向对象的设计原则
面相对象的设计目标(低耦合、高内聚)
Web容器主要使用了策略模式 ,多个策略实现同一个策略接口

public interface Servlet {
    public void init(ServletConfig config) throws ServletException;
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;
    public void destroy();
}

HttpServlet 实现 Servlet 这个策略接口, 并只给出模板方法service 便于开发

   protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
   {
       String method = req.getMethod();
       if (method.equals(METHOD_GET)) {
               doGet(req, resp);
       } else if (method.equals(METHOD_HEAD)) {
           long lastModified = getLastModified(req);
           maybeSetLastModified(resp, lastModified);
           doHead(req, resp);
       } else if ...

JUnit 中的设计模式: 模版方法模式 、组合模式

架构师如何确保自己的架构设计被整个团队遵守、执行? 通过开发框架,约定开发的规范,写Demo ,让其他人理解架构意图 。

18 | 反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

反应式编程:
即时响应
回弹性
弹性
消息驱动
RxJava、Reactor 基于观察者设计模式的异步编程方案
框架Flower 的基本原理:
1、使用极少的容器线程就可以处理较多的并发用户请求 ,容器线程不会阻塞
2、Service之间通过AKKA Actor进行消息通信 , 只需要几个线程就可完成大量的Service处理和消息传输,这些线程不会阻塞等待
3、数据库的访问是提交给异步数据库驱动 ,也不会有线程阻塞
总结:有限的线程可以完成大量的并发用户请求,从而大大提高了系统的吞吐能力和响应时间;由于线程不会阻塞,应用就不会因为并发量太大或者数据库处理缓慢而宕机,从而提高了系统的可用性。
说明:Flower 框架实现异步无阻塞,一方面是利用了 Web 容器的异步特性,主要是 Servlet3.0 以后提供的 AsyncContext,快速释放容器线程;另一方面是利用了异步的数据库驱动以及异步的网络通信,主要是 HttpAsyncClient 等异步通信组件。而 Flower 框架内,核心的应用代码之间的异步无阻塞调用,则是利用了 Akka 的 Actor 模型实现。
说明:Akka Actor 的异步消息驱动实现:Sendor(也是Actor) 通过ActorRef 发送Message , Message 进入Mailbox 就返回 , Recevier(也是Actor) 从Mailbox 拿消息进行处理。
Flower的设计方法 :
只要实现一些细粒度的Service即可, Service 会被包装到Actor里面; 通信关系通过流程编排
Flower 的落地效果也十分的优秀,课程举了两个例子 重构服务和网关应用
感想:
异步编程在高并发场景、物联网场景 会有更多的应用

19 | 组件设计原则:组件的边界在哪里?

组件内聚原则:
	复用发布等同原则  
	共同封闭原则  将那些会同时修改且为了相同目的而修改的类放到同一个组件
	共同复用原则  不要强迫一个组件的用户依赖他们不需要的东西 
组件耦合原则:
	无循环依赖原则
	稳定依赖原则
	稳定抽象原则 
组件的边界与依赖关系划分,不仅要考虑技术问题,也要考虑业务场景问题,甚至有时候还要考虑人的问题(如组件的职责和部门的职责相关)

感想:
JDBC 就是个组件设计的很好例子,java定义好接口,其他厂商提供具体实现,利用JNDI和反射加载使用。SLF4J 也是很好的例子, log4j \ logback 是具体实现

20 | 领域驱动设计:35岁的程序员应该写什么样的代码?

领域模型模式:
业务逻辑的组织方式主要是事务脚本模式, 也就是面向过程方式 , 贫血模型
领域模型是合并了行为和数据的领域的对象模型 充血模型
领域驱动设计 DDD
从领域出发,分析领域内模型及其关系,进而设计软件系统的方法
领域 、 子域 、限界上下文、集成关系
上下文映射图 、 实体(实体设计 , 值对象-描述性的)
分层架构 : 用户接口层 - 应用层 - 领域层
六边形架构:适配器 - 应用程序 - 领域模型
* CQRS 命令查询责任分离 、事件驱动 、事件溯源 *
感想:
领域驱动设计 是在更大范围将系统当做对象,进行内聚设计,聚焦核心功能,描述出系统边界

答疑丨对于设计模式而言,场景到底有多重要?

《UML精粹》马丁 这本书个人感觉对UML的学习理解确实很好,看了这本书确实能收获很多
《敏捷软件开发原则、模式与实践》 设计原则和模式在工作中基本常见,但理念的理解还是有些偏差,个人之前对敏捷管理是小步快跑,但是未将敏捷管理和敏捷软件设计紧密联系,看了李老师的解读,才发现敏捷的关键点; 另外想说的就是,有时候未读原著真是会错失一些东西 。
书的作者Bob认为,我们能够进行敏捷开发,能够快速响应需求变更,不在于什么敏捷开发过程和敏捷项目管理,而在于敏捷的软件设计
《架构整洁之道》 Bob 的又一作 ,
《企业应用架构模式》马丁

参考资料:

极客时间上李智慧老师的 《后端技术面试38讲》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值