程序员三门课¶
Preface¶
世界需要什么样的程序员?¶
- “工型人才”
- 具体来说,就是先要具备完成完整应用的能力,包括:线上运维,成为熟手,这是下面的一横;
- 在某些领域足够深入,成为高手,这是中间的一竖;
- 在达到更高的水平之后,兼通很多领域,比如业务、产品、项目管理、测试、运维、团队组织,成为驱动者和领导者,这是上面的一横
一句话概括程序员的工作?¶
- 程序员的工作是,将从现实或者虚拟世界中抽象出来的逻辑,以代码的形式实现。
技术精进¶
程序员技能与成长¶
-
如何学习新的编程语言
- 《计算机程序的构造和解释》:在学习一门新的编程语言时,应该关注这门语言的基本表达形式(Primitive Elements)、组合的方法(Means of Combination)及抽象的方法(Means of Abstraction)这三个特性。
- ◎ 基础知识:基本语法、关键字、变量与常量、数据类型、运算符、流程控制、异常处理、文件处理、编程思想(面向对象、面向过程、函数式编程)、多线程支持等。
- ◎ 应用知识:网络请求、数据处理、内置函数、对日志和调试的支持、对单元测试的支持、序列化与反序列化等。
- ◎ 高级知识:开源类库、开源框架、底层原理等
-
测试驱动设计
-
基本思想
- 在开发功能代码之前先编写测试代码。
- 也就是说在明确要开发的需求之后,首先思考如何分析这个需求,并完成测试代码的编写,然后编写相关代码来满足这些测试用例,最后循环添加其他测试用例,直到该需求对应的测试用例都测试通过。
-
3个原则
- 原则1:无测试,不代码。
- 原则2:单元测试不在多,能够识别出问题即可。
- 原则3:代码不在多,让当前单元测试全部通过即可。
-
“金丝雀测试“
- ”金丝雀测试”的概念来自早期的煤炭矿井行业:金丝雀对有毒气体比较敏感,在19世纪左右,英国的矿井工人在下矿井时常常会带一只金丝雀,如果矿井内的有毒气体超标,金丝雀就会立刻死亡,这会救矿井工人一命。
- 同样,在测试驱动设计实践中,在开发具体的测试用例之前,也需要先写一个dummy的测试用例,确保整个编译、运行和JUnit环境是正常运行的。
-
-
代码分析工具
- FindBugs
-
根据代码缺陷性质分类
- ◎ Dodgy code:糟糕的代码。
- ◎ Bad practice:不好的做法。
- ◎ Correctness:可能不正确。
- ◎ Experimental:实验。
- ◎ Internationalization:国际化。
- ◎ Malicious code vulnerility:恶意的代码漏洞。
- ◎ Multithreaded correctness:多线程问题。
- ◎ Performance:性能问题。
-
代码审查
-
要有审查清单
- ◎ 代码结构:是否包含超长代码,代码层次嵌套是否过深,函数是否入参过多,循环条件是否有跳出点,if语句是否有对应的else语句,是否存在重复的代码,等等。
- ◎ 代码安全性:I/O流是否正常关闭,资金计算是否使用了Double数据类型,是否有超大的临时对象,线程池大小是否合理,异常是否被忽略,是否有详细的日志记录,是否存在并发问题,参数是否做了必要的检查,远程服务的入参出参是否实现了Serialization并且自定义了serialVersionUID,应用是否依赖了SNAPSHOT版本的类库,等等。
- ◎ 代码安全性:I/O流是否正常关闭,资金计算是否使用了Double数据类型,是否有超大的临时对象,线程池大小是否合理,异常是否被忽略,是否有详细的日志记录,是否存在并发问题,参数是否做了必要的检查,远程服务的入参出参是否实现了Serialization并且自定义了serialVersionUID,应用是否依赖了SNAPSHOT版本的类库,等等。
- ◎ 代码注释:指类及方法是否有注释,注释是否可以表达其准确含义,在代码中是否存在FIXME及TODO等注释,注释是否包含边界值及对异常情况的说明,等等
- ◎ 代码注释:指类及方法是否有注释,注释是否可以表达其准确含义,在代码中是否存在FIXME及TODO等注释,注释是否包含边界值及对异常情况的说明,等等
- ◎ 代码优化:是否可以使用枚举代替自定义的常量,在代码中是否包含魔法值,是否可以使用Optional代替NPE的检查,是否可以使用Stream代替for循环,是否可以使用设计模式,等等。
- ◎ 其他:代码逻辑是否正确,是否实现了业务功能,代码是否有好的可读性及可测试性,等等。
-
重点关注的改动点
-
代码审查应该是日常性的工作
- 换句话说,代码审查应该是伴随着代码提交的,而不是伴随着代码发布的。
-
不要一次审查太多代码
- 我们发现,当代码行数超过200时,缺陷密度(每千行代码的缺陷数,为缺陷数量与代码行或功能点数量的比值,其测量单位是defects/KLOC)就会急剧减小,超过400后,缺陷密度几乎为0。
- 每次代码审查的代码最好在200行左右,最多不要超过400行
-
进行一次代码审查的时间不要太长
-
-
工作法则
-
关于文档
- 一个系统或平台都需要有Core文档,比如领域模型、主体架构等
- 用例即文档
- 提倡活文档
-
YAGNI
- YAGNI:You Ain't Gonna Need it,你如无必要,别增复杂度。
- Cunningham 解释为:即使你非常确信自己将来需要某个特性,也不要现在就去实现它。在很多情况下,你会发现或许最终自己不需要它了,或者真正需要的特性与之前预期的有很大出入。
-
两个原因
- 你节约了时间,因为你避免编写了最终证明不必要的代码。
- 你的代码质量更高了,因为你让代码不必为你的“推测”所污染,而这些“推测”最终可能或多或少有些错误,但此时这些错误已牢牢地依附在你的代码中了。
-
Martin Fowler进一步表示:当我们在考虑推定特性时,很有可能我们是错的。
-
程序员如何加速成长¶
-
选择合适的平台
- 等我们成长慢时,就应该到下一个平台:换一个业务、岗位或者公司。
- 离开习舒适区。
-
时间管理
-
设置优先级
- ◎ 对于重要且紧急的事情,把它们安排在第一时间处理。
- ◎ 对于重要但不紧急的事情,要避免它们变成重要且紧急的事情,在处理重要且紧急的事情之余,要把重要但不紧急的事情进行拆解,并制定计划,按部就班地完成。
- ◎ 对于紧急但是不重要的事情,首先想办法看看能不能把它们变成不重要也不紧急的事情,如果还是无法改变,则可以考虑和同事分担。
- ◎ 对于不重要也不紧急的事情,不要做!
-
说不
- 有一句话是这样的:对于需求,先通过讨论解决,解决不了再用代码解决。这才是正确的顺序。
- 这不是指程序员要尽最大努力减需求,而是要对不合理的需求说不。
-
想清楚再做
- 让开发者彻底想清楚所有流程,这样才能在开发时按照自己的设计按部就班地实现。
- 没有想清楚就动手写代码是会带来很多麻烦的,尤其是现在很多公司都在实施服务,各个应用之间依赖关系复杂
-
-
写业务代码有成长机会吗?
- 答案非常肯定:必须有成长机会。对于大部分公司而言,能够写底层代码或者中间件代码的人总是有限的,写业务代码会面临更高的复杂度。
-
三个层次
- ◎ 第1个层次,让代码写得不一样。可从代码规范、可读性、可扩展性等角度着手,这也是程序员的基本功。
-
◎ 第2个层次,考虑业务问题和技术问题的匹配。可从写业务代码中理解需求,并做好分析与设计。被动接收需求和实现接口,确实成长空间不大。
-
“烟囱型架构”
- 团队在早期发展阶段对新来的每个业务都会搭建一套系统,业内人士将这种见招拆招、垂直化发展的架构称为“烟囱型架构”。
- 好处:在早期业务成败未知的情况下,不过度设计架构,能直接、有效地支持业务。
-
问题
- 人手不够,业务响应慢了下来。
- 重复建设。在同类烟囱系统中有80%的功能是类似的
- 维护成本高。面对日常升级包、咨询支持服务,该团队疲惫不堪。
-
平台化架构
- 1)快速支撑、响应业务。
- 2)抽象共性、边界清晰。快速支撑、响应业务是以终为始的出发点。
-
-
◎ 第3个层次,总结相关方法体系,成为业务及技术双料专家。
学会学习¶
-
两个著名的学习理论
-
1.德雷福斯模型
- 德雷福斯模型是一种衡量人们的工作方法和能力,反省并提高专业技能的层级模型。
-
五个层级
- ◎ 新手(Novice):新手需要指令清单,在该技能领域经验很少或者没有经验。这里提到的经验,指的是通过实施这项技术促进了思维的改变。
- ◎ 高级新手(Advanced Beginner):高级新手不想要全局思维。一旦经过新手的历练,人们就开始以高级新手的角度看待问题。高级新手能够或多或少地摆脱固定的规则,可以独自尝试任务,但仍难以解决问题。
- ◎ 胜任者(Competent):胜任者能够解决问题,处于这一水平的人通常被认为“有主动性”和“足智多谋”。他们往往在团队中发挥领导作用(无论是否有正式的头衔),既可以指导新手,也不会经常“骚扰”专家。
- ◎ 精通者(Proficient):精通者能够自我纠正。精通水平的从业者需要全局思维,将围绕某种技术,寻找并想了解更大的概念框架。对于过于简单化的信息,他们会非常沮丧。
- ◎ 专家(Expert):专家凭直觉工作。专家是各个领域知识和信息的主要来源,总是不断地寻找更好的方法和方式去做事。他们有丰富的经验,可以在恰当的情境中选取和应用这些经验。他们写文章,并做巡回演讲。
-
2.刻意锻炼理论
- 这套练习方法的核心假设是,专家级水平是逐渐练出来的,而有效进步的关键在于找到一系列的小任务让受训者按顺序完成
-
-
终身成长
-
终生成长理念将人的思维分为固定型思维和成长型思维。
- ◎ 拥有固定型思维的人喜欢与人对比,并不断证明自己最棒,挫折、批评和否定能让其收到打击或者重创。
- ◎ 拥有成长型思维的人则只考虑能不能够学到知识,可不可以变得更强,能不能够继续成长。
-
业务分析与设计¶
-
概念
- 业务分析指应用特定的方式或方法,把复杂的需求拆解成简单且容易理解的对象,并找出这些对象之间的关系。
- 业务分析也是系统开发中最重要、最困难的阶段,只有依据业务分析的结果,运用合理的思想和方法,才能设计出理想的系统架构。
-
黄金圈法则
-
2W1H分析法:由为什么(Why)、怎么做(How)、做什么(What)三部分组成的由内到外的思维方式
- Why:愿景和目标
- How:方法和措施
- What:现象和成果
-
人才金字塔
- 执行层:大部分人都知道做什么,能使用技术将工作做好,通常处于执行层;
- 管理层:有些人知道怎么做,具备组织、管理和协调能力,通常处于管理层;
- 决策层:只有极少一部分人知道为什么做,能够制定战略、目标和计划,具有宏观把控能力,通常处于决策层。
-
-
UML建模工具
-
UML图
-
用例视图
-
用例图(UseCase Diagram)
- 包含关系(Include)
- 扩展关系(Extend)
- 泛化关系(Generalization)
-
-
设计视图
-
类图(Class Diagram)
- 泛化关系:类之间是继承关系,为一般与特殊的关系
- 实现关系:类与接口的关系,类是接口所有特征和行为的实现
- 关联关系:使一个类知道另一个类的属性和方法
- 组合关系:代表整体的对象负责代表部分的对象的生命周期
- 聚合关系:整体与部分的关系,并且部分可以离开整体而独立存在
- 依赖关系:一个类的实现需要另一个类的协助
-
对象图(Object Diagram)
-
-
进程视图
- 状态图(Statechart Diagram)
- 活动图(Activity Diagram)
-
序列图(Sequence Diagram)
- 对象之间消息发送的先后顺序,强调时间顺序
-
协作图(Collaboration Diagram)
- 描述了收发消息的对象的组织关系
-
实现视图
- 构件图(Component Diagram)
-
拓扑视图
-
部署图(Deployment Diagram)
- 可视化部署软件的物理组件拓扑
-
-
-
应用场景
- (1)需求阶段:采用用例图描述需求。
- (2)分析阶段:采用类图描述静态结构,采用顺序图、协作图、活动图和状态图描述动态行为。
- (3)设计阶段:采用类图对类的接口进行设计。
- (4)开发阶段:采用某种面向对象语言实现。
- (5)测试阶段:单元测试采用类图和类的规格说明书;集成测试采用类图、协作图;系统测试采用用例图。
- (6)交付阶段:采用构件图和部署图。
-
-
业务分析与设计的方法
-
问题
- 每个特定的需求又有其特别复杂之处,所以几乎没有人能够第一时间抓住这些专业领域的需求本质。
- 因此,用户的需求在历经几次演变之后变得面目全非,每一个“加工制造环节”都认为自己在做“正确的事”。
-
面向对象分析与设计(OOAD)方法
- 面向对象分析与设计方法对复杂需求的解决方法是:让建模专家进行需求分析,并在需求和需求之间建立关系。
-
局限性
- 面向对象分析与设计主要关注微观层次的抽象,比如类和对象实例等;
- 对每个问题域通常都需要创建单独的用例分析模型,项目或产品的大方向在许多情况下变得很模糊,很难全局把控系统的总目标,所以这样的系统分析与设计方式存在很大的风险。
-
依旧是一种自底向顶的设计方式
-
面向服务的架构(Service-Oriented Architecture, SOA)
- 面向对象分析与设计的局限性,催生了面向服务分析与设计(SOAD)方法,它有效地弥补了面向对象分析与设计的局限性。
- 在做面向服务分析与设计时会采用自顶向底的设计方法,首先要分离出业务流程和服务,然后细化对象。
-
领域驱动设计(DDD)
-
问题域
- 不管是在面向对象分析与设计中还是在面向服务分析与设计中,对系统分析和设计都是分开的、割裂的,即分析人员从领域中收集基本的业务概念,系统设计人员再将基本的业务概念映射为相应的编程工具的构造组件。
- 在这个过程中,由于分析模型和设计模型不同,在分析和设计之间形成一条鸿沟
-
解决
- 领域驱动设计(DDD)方法打破了分析和设计割裂的状态,
- 并给出了领域模型的概念,抛弃了将分析与设计分开的做法,使用统一的模型来满足分析与设计的需求,使系统开发能够更加灵活、快速地响应需求的变化。
-
-
-
系统分析与设计的三个发展阶段
-
面向数据驱动分析与设计
- 软件设计总是从设计数据库及其字段开始的,这一阶段的特征就是围绕数据库编程。
-
典型的两层架构
- 展示层
- 数据库层
-
面向过程(Procedure Oriented)
- 一种思维方式,在面对一个问题时,我们通常先关注解决这个问题的步骤,即过程。
- 比如对于如何将大象装入冰箱这个问题,我们想到的步骤是:第1步,打开冰箱;第2步,装入大象;第3步,关上冰箱。
-
缺点
- 这种分析与设计思维中,世界不仅由简单的关系数据组成,还使用关系数据库反映现实需求,这不符合人类的自然思维,是一种扭曲的分析方法。
- 软件运行时的负载集中在数据库端,系统变成集中式和高风险的大型单体模式(Monolithic)
-
面向对象和服务分析与设计
-
经典的三层架构
- 展示层
- 业务逻辑层
- 数据访问层
-
分析阶段和设计阶段不能很好地衔接,出现了难以逾越的鸿沟
-
-
面向问题域分析与设计
-
领域模型,是对现实世界或领域中的对象的可视化表示;
- 可分为概念模型、
- 领域对象模型
- 和分析对象模型。
-
领域建模是一种艺术,融合了分析阶段和设计阶段,目的是使复杂的软件快速应付变化。
- 如果一个模型在实现时不具备可行性,就需要重新寻找新的模型;如果该模型没有忠实表达领域的关键概念,则也必须重新寻找新的模型。
-
-
-
面向对象分析与设计(OOA/D)
-
什么是面向对象
- 面向对象是对现实世界进行理解和抽象的方法,是设计原则和设计模式的前提。
- 首先,找出现实世界中的动作和实体:①打开冰箱;②将大象放入冰箱;③关闭冰箱。
- 然后,抽象出概念:①冰箱、可以打开门、可以存储、可以关门;②大象、体重、体积。
-
面向对象的特征
-
(1)封装
- 将相关属性和方法重组成一个新的对象,并提供对外访问的接口,对数据的访问或修改只能通过该接口进行。
-
(2)继承
- 某类对象可以继承其他类对象的属性和方法
- 继承是代码重用的具体表现之一,也破坏了对象的封装性
-
(3)多态
- 不同的类对象对同一消息做出响应,得到不同结果
-
(4)抽象
- 将共性元素进行合并
-
-
面向对象设计的原则
-
(1)高内聚
- 系统中的模块具有高度相关的职责,单一责任原则
-
(2)低耦合
- 耦合指元素与元素之间连接、感知和依赖的程度。
- 耦合度越低,系统越独立,可扩展性就会越好
-
(3)可扩展
- 采用动态加载的插件、回调函数、抽象接口及认真设计的代码结构和类层次结构,使系统在面对不断变化的需求时,其代码不被大量重构开发。
-
(4)可复用
- 可以利用已有的代码或者相关模块去实现新的功能需求,实现高效、低成本和高质量。
-
-
-
面向服务分析与设计
-
什么是面向服务架构(SOA)
- 面向服务(SOA)宗旨是 以服务为基准点 完成系统的组件化和模块化,继而完成架构搭建。
- 传统的面向对象的模型的替代模型,面向对象的模型是紧耦合的,SOA 通过中立的接口定义实现松耦合。
- SOA 的系统使用 OOD 来构建单个服务,但是其整体设计却是面向服务的。
-
SOA 的关键技术
-
UDDI
- UDDI 规范描述了服务的概念,同时也定义了一种编程接口
- UDDI(Universal DescriptionDiscovery and Integration,统一描述、发现和集成)提供了一种服务发布、查找和定位的方法
-
WSDL
- WSDL(Web ServiceDescription Language,Web 服务描述语言)是对服务进行描述的语言
- 服务接口定义就是一种抽象的、可重用的定义
-
SOAP
- SOAP(Simple ObjectAccess Protocol,简单对象访问协议)定义了服务请求者和服务提供者之间的消息传输规范。
-
REST
- 基于 Web 通信的技术,可以降低开发的复杂性,提高系统的可伸缩性。
-
-
微服务架构
-
概念
- 微服务架构是SOA架构的延续,是一种比较现代化的细粒度的SOA实现方式,强调业务需要彻底组件化和服务化,
- 也就是说,系统需要按照业务边界做细粒度的拆分和部署。
-
三个特点
- 1)独立部署、灵活扩展。
- 2)资源的有效隔离。
-
3)敏捷的组织结构。
- 微服务设计的思想也改变了原有的企业研发团队的组织架构,从以往水平的 职能型团队 变成了垂直的 业务型团队。
-
微服务架构的问题
- ◎ 微服务架构把原有的项目拆成多个独立的工程,增加了开发、测试和运维的复杂度。
- ◎ 微服务架构需要保证不同服务之间的数据一致性,引入了分布式事物和最终一致性机制,为设计和开发带来了挑战。
- ◎ 很多微服务的拆分粒度过大,导致服务的调用链过长,性能降低,维护成本增加。
-
解决问题
- 领域驱动设计可以弥补微服务架构中的很多不足,为微服务架构提供指导思想。
-
-
-
领域驱动设计
-
概念
- 定义:将实现连接到持续进化的模型中来满足复杂需求的软件开发方法。
-
三个核心原则
- ◎ 把项目的重点放在核心域(Core Domain)和域逻辑上。
- ◎ 把复杂的设计放在有界域(Bounded Context)的模型上。
- ◎ 和领域专家不断协作完善应用模型来解决特定领域的问题。
-
领域指一个组织在某个特定的范围内应用特定的方式进行特定的活动。
-
领域概念结构
-
问题域
- 核心子域
- 支撑子域
- 通用子域
-
解决域
- 架构
-
限界上下文
- 通用语言
- 领域对象
- 聚合
- 领域服务
- 领域事件
- 工厂
- 仓储
-
上下文映射
-
限界上下文之间的关系有以下几种
- ◎ 合作关系(Partnership):两个合作的上下文会相互影响和制约。
- ◎ 共享内核(Shared Kernel):两个上下文都依赖部分共享的模型。
- ◎ 客户方-供应方开发(Customer-Supplier Development):上下文之间有组织的上下游依赖。
- ◎ 遵奉者(Conformist):下游上下文只能盲从上游上下文。
- ◎ 防腐层(Anti-Corruption Layer):上下文之间通过适配和转换进行交互。
- ◎ 开放主机服务(Open Host Service):指定一套明确的协议,方便其他上下文访问。
- ◎ 发布语言(Published Language):在一般情况下和开放主机服务一起使用,用来定义开放主机的协议。
- ◎ 大泥球(Big Ball of Mud):混杂在一起的上下文关系,边界不清晰。
- ◎ 无关系(Separate Way):两个完全没有联系的上下文
-
-
-
核心要素
- 分层架构(Layered Architecture)
-
应用层(Application Layer)
- 负责将展示层的请求转发到领域层,将领域层的执行结果返回给展示层
-
领域层(Domain Layer)
- 负责表达业务的概念,
- 业务逻辑包括业务的流程、策略、规则、状态及完整性约束等。
-
实体(Entity)
- 实体对象具有唯一性和可变性,唯一性由唯一的身份标识来确定,可变性则反映了实体本身的状态和行为。
-
值对象(Entity Object)
- 只包含元素属性的不可变对象。
-
服务(Service)
-
应用服务
- 是用来表达用户故事(User Story)和用例的主要手段。
- 应用服务只负责处理业务用例的执行顺序和结果拼装
-
领域服务
- 表示一种无状态的操作,用来实现某个特定领域的任务。
- 应用服务是领域服务的客户方。
-
-
模块(Module)
- 通常将领域模型分解成不同的模块,来降低领域模型的复杂度,从而提高领域模型的可读性。
-
聚合(Aggregate)
- 聚合根通过禁止外部对象对其成员的引用来保证在聚合内进行的更改是一致的,
- 所以它的难点一般在对一致性的维护上:在聚合内实现强一致性,在聚合外实现最终一致性。
-
工厂(Factory)
- 一个对象的创建可能是它自身的主要操作,但是复杂的组装操作不应该成为被创建对象的职责,因为组装这样的职责会产生笨拙的设计,也很难让人理解。
-
仓储(Repository
- 仓储是对聚合的管理,它介于领域模型和数据模型之间
- 肯定有人问:“为什么不能直接使用数据访问层(比如ORM框架等),而需要多加一个仓储层来解耦呢?”
-
-
领域模型实践
-
概念
- 领域模型(Domain Model)是对现实世界或者领域中对象的可视化表示。
- 领域模型是为了准确定义需要解决的特定问题而构造的抽象模型,其最重要的功能是形成统一的认知
-
我们经常在公司听到一句话:一颗心,一张图,一场仗
- 指我们首先要明白解决什么问题(一场仗),才能把这个问题毫无歧义地表述成统一的模型(一张图),有了这模型,我们就能全心全意地投入到解决这个问题的过程中,达到对问题的统一认知(一颗心)
-
领域模型的作用
- 领域模型是对业务概念的可视化描述,是需求分析的产物,用于指导程序设计,但领域模型与实现方式无关,在领域建模时不应该考虑如何实现,需要同项目的所有成员(客户、项目经理、开发、测试等)达成共识。
- 面向对象分析强调的是在问题域发现并描述概念,解决的问题是做正确的事情;面向对象设计强调的是定义软件对象,解决的问题是正确地做事情。
- 建模的重要性在所有工程实践中都已经得到了广泛的认同,它是一种抽象和分解的方法,可以将复杂的问题拆解成一个个的抽象块,代表特定的一块密集而内聚的信息。
-
论设计先行的重要性
- 如果不做设计,直接实现,则很有可能在开发过程中发现思维的局限性,使开发推倒重来;并且,通过这种方式构造的代码,并没有和现实世界连接起来,当我们的软件和需求稍加修改时,代码就可能变得异常混乱和难以维护。
-
举个例子
- 举个例子,在某电商系统的初期设计中,一个卖家账号只能开一个店铺,因此卖家和店铺的概念全部由卖家这一个模型来承载。所有与店铺相关的模型(店铺红包、店铺评价等)全部与卖家模型关联。这时,同一个模型拥有了两层业务含义,职责不明确。
- 领域建模可以降低软件和现实世界之间的差异,用真实的业务概念划分职责,目的是实现一个可以高效率、低成本维护的可持续发展的软件系统
-
从领域模型推导到系统实现是一套引导思考的方式,也是一套科学的开发流程,其核心目的在于提供系统设计的“指导方针”。领域模型必须基于用户需求和业务发展,既可以用来同用户沟通验证需求,又可以避免模型因实现的考量而被带偏(实现成本、遗留系统)。
-
如何进行领域建模
-
领域驱动设计本身是一套完整、详尽的方法论,从如何进行需求沟通(构建领域知识),到高层设计(战略建模)、详细设计(战术建模),细致到代码的实现风格都给出了示例
- (1)构建领域知识。
- (2)创建通用语言。
- (3)创建实体。
- (4)创建值对象。
- (5)创建聚合根
-
1.用例分析法
- 1)获取用例描述
- 2)寻找概念类
- 3)添加关联
- 4)添加属性
- 5)模型精化
-
2.四色建模法
- ◎ Moment-interval(时标性原型):时标性原型是建模的起点,代表着我们需要记录的某一时刻发生的事件,例如订单、行程和会议。
- ◎ Party-place-thing(人-事-物原型):是一种有形的可唯一识别的实体,可以是人、机构、地点、物品等。
- ◎ Role(角色原型):角色是人、事、物的一种参与方式。例如,在一份雇用关系中,某个人扮演者雇员的角色,那么这个人就是人-事-物原型,雇员就是“Role”。
- ◎ Description(描述原型):表示资料类型的资源,是一种类似目录条目的描述,用来对对象进行分类或标记,可以被其他原型反复使用。例如,一个商品的品牌、描述和属性。
-
-
架构修炼¶
架构思维¶
-
分解
-
"分而治之"
- 分解是“分而治之”思想的体现。“分而治之”是一种处理复杂问题的通用方法,能保证分解后的各个部分高内聚、松耦合,最终集成为一个整体。
- 在架构设计过程中,会通过将 关注点分离 对架构进行多层次分解,将系统层层分解为多个架构元素
- 分解的目的是 加速开发和降低问题的复杂度 。
-
分解的原则
- 德国哲学家、数学家莱布尼茨一针见血地指出:“不讲究分解技巧,分而治之的作用就不大。无经验者对问题分解不当,反而增加了解决问题的困难。”
-
(1)高内聚、低耦合。
- 莱布尼茨指出:“分解的主要难点在于怎么分。分解策略之一是按容易求解的方式来分,之二是在弱耦合处下手,切断联系。”
-
(2)层次性
- 分解通常是先业务后技术,循序渐进,先逻辑后物理,从上到下逐级进行分解和展开,一般是系统→子系统→模块→组件→类。
-
(3)正交原则
- 所谓「正交」,就是指两个向量的内积为零。
- 在一个正交系统里,沿着一个方向的变化,其另外一个方向不会发生变化。
- 架构分解出的架构元素应是相互独立的,在职责上没有重叠。
-
正交设计原则
- 消除重复
- 分离关注点
- 缩小依赖范围
- 向稳定的方向依赖
-
4)抽象原则
-
5)稳定性原则
- 将稳定部分和易变部分分解为不同的架构元素,稳定部分不应依赖易变部分。
- 根据稳定性原则,将通用部分和专用部分分解为不同的元素;
- 将动态部分和静态部分分解为不同的元素;
- 将机制和策略分离为不同的元素;
- 将应用和服务分离。
-
6)复用性原则
- 就是对之前成果的复用,复用类似的架构设计、分析设计、开源框架、设计模式、数据结构、领域模型、架构思想等
-
分解的时机
- 架构分解的时机通常就是架构改造和演变的时机。
- 时机一:架构在腐化时,已经难以满足关键场景的关键需求,例如对用户请求的响应速度越来越慢,已经接近临界值,并且根据经验,响应速度还有可能继续降低,越来越难以维护,这时可以考虑进行架构演变,对架构进行改造和分解。
- 时机二:如果能提前预见系统的问题,在经过慎重评估后,提前进行架构演变,则也是可以的。
- 注意:不要过早分解、过度分解,这样做除了增加成本,还可能带来风险。
-
-
集成
- 分解完成的各个组件或子系统,通过合适的接口设计,最终还能够集成为一个完整的整体。
- 如果分解后的内容无法集成在一起,分解就没有任何意义。
-
常见的集成方式
- 点对点方式
- API网关方式
- 消息代理方式
-
三道难关:数据服务设计;数据的一致性;分布式查询。
-
动静分离
-
复用
- 复用的表现:SOA参考架构的核心思考模式,包括最近广受关注的业务能力组件化、组件能力服务化、平台+应用、共享中心建设、共性能力下沉等都是复用的表现。
-
一个复用性较好的系统,就是一个易维护的系统。可维护性差的原因:
- 过于僵硬:加入一个新的功能,不管大小都很难,这个新功能会波及很多其他模块,最后造成跨越几个模块的改动。
- 过于脆弱:修改一个地方,会导致系统到处发生故障。
- 复用率低:已有的代码、函数、模块的功能在新的模块或系统中使用时发现依赖很多其他因素,导致很难被再次利用。
- 耦合度过高:实现一个需求需要改变原有设计意图和原有架构设计
-
常规复用
- 代码复用
- 算法复用
- 数据结构的复用
-
系统级复用
- 设计复用
- 分析复用
-
复用的设计原则
-
- “开-闭”原则:OCP
- 2.里氏代换原则:LSP
- 3.依赖倒转原则:DIP
- 4.接口隔离原则:ISP
- 5.组合/聚合复用原则:CARP
- 6.迪米特法则
-
-
分层
-
重要原则
- 每层只能与位于其下方的层发生耦合。
- 严格分层架构
- 松散分层架构
-
分层架构的优点
- ◎ 开发人员可以只关注某一层
- ◎ 可以很容易地用新的实现来替换原有层的实现。
- ◎ 可以降低层与层之间的依赖。
- ◎ 有利于标准化。
- ◎ 有利于各层逻辑的复用。
-
分层架构的缺点
- ◎ 因为增加了中间层,所以降低了系统的性能,不过可以通过缓存机制来改善。
- ◎ 可能会导致级联的修改。这种修改尤其体现在自上而下的方向,不过可以通过依赖倒置来改善。
-
-
模式
-
架构设计中的模式匹配
- 将最终的业务域问题匹配到对应的技术实现上,即根据业务需求来挑选最适合的技术;
- 而不是用主流和最先进的技术去反推业务。
-
分层模式
- 经典四层架构
- 六边形架构
-
C/S模式
-
Master-Slave 模式
- 基于分而治之的思想
-
代理模式
- P2P模式
- 事件总线模式
- MVC模式
-
-
抽象
-
结构化
- 强调结构和逻辑,通过结构的完整和逻辑的严密来保证结果的正确。
-
结构化的原则
- 以终为始:核心在于对问题进行正确界定的基础上
- 知道目标,知道方案、步骤
- 避免陷入细节
- 对各种观点进行分组、抽象,构成金字塔
- MECE
-
结构化分析工具
- 逻辑树,又叫问题树、演绎树或分解树
- 议题树
- 假设树
- 是否树
- 二维表格
- 80/20法则:找到那些导致80%产出的20%投入
-
结构化思维的7个环节
- Why
- What
- How
- Who
- When
- Where
- How Much
-
迭代
- 没有最优的架构,只有不断进化的架构和最适合的架构
- “凡事预则立,不预则废”,在软件设计初期,投入精力进行架构设计是很有必要的
- 会经历一个振荡期,这个振荡期可能横跨了数个迭代周,最后会趋向于平稳。
-
过度设计
- 项目需求或不确定架构的性能。为了避免出现过度设计的现象,我们需要使
-
过度设计的特征
- ◎ 超高强度。
- ◎ 终极性能。
- ◎ 重新设计。
- ◎ 过度合理。
架构设计¶
-
架构设计概要
-
业务架构
- 业务架构是对业务需求的提炼和抽象
- 软件架构是业务架构在技术层面的映射,合理的开发分工也应该基于业务架构去做。
-
业务架构的设计原则
- 1)将业务平台化
- 2)将核心业务和非核心业务分离
- 3)隔离不同类型的业务
-
应用架构
- 应用架构介于业务与技术之间,是对整个系统实现的总体架构,需要指出系统的层次、系统开发的原则、系统各个层次的应用服务。
-
应用架构的设计原则
- 1)稳定
-
2)分解
- 将稳定部分与易变部分分离
- 将核心业务与非核心业务分离
- 将应用与数据分离
- 将服务和实现细节分离
-
3)抽象
- 4)松耦合
-
5)容错设计
- 服务自治:服务能彼此独立修改、部署、发布和管理,避免引发连锁反应。
- 集群容错
- 多机房容灾
-
技术架构
- 技术架构就是对在业务架构中提出的功能(或服务)进行技术方案的实现
-
技术架构的设计原则
- 1)无状态
- 2)可复用
- 3)松耦合
-
4)可治理
- 服务可降级
- 服务可限流
- 服务可开关
- 服务可监控
- 白名单机制
- 制定服务契约
-
数据架构
-
两部分内容
- 静态部分的内容的重点是数据元模型、数据模型,包括主数据、共享动态数据和所有业务相关的业务对象数据的分析和建模;
- 动态部分的内容的重点则是对数据全生命周期的管控和治理。
-
数据架构的设计原则
- 1)统一数据视图,即保证数据的及时性、一致性、准确性和完整性
- 2)数据和应用分离
- 3)数据异构,即在源数据和目标数据内容相同时做索引异构
- 4)数据库读写分离
- 5)采用关系数据库 & 合理利用NoSQL数据库
-
-
-
架构设计的流程
- 架构设计的元模型
-
架构设计的核心要素
- 性能
- 可用性
- 伸缩性
- 扩展性
- 安全性
-
高性能设计
-
高可用设计
-
高可用性的度量与考核
- 可用性指标
- 故障分:故障分 = 故障时间(分钟)×故障权重
-
高可用的架构
-
高可用的应用层
- 应用的无状态性:通过负载均衡实现失效转移
-
业务总是有状态的,称为Session
- 1)复制Session
- 2)绑定Session。利用负载均衡的源地址Hash算法,将源于同一IP的请求分发到同一台服务器上
- 3)利用Cookie记录Session。将Session记录在客户端,在每次请求服务器时,都将Session放在请求中发送给服务器,在服务器处理后,再将修改过的Session发送回客户端。
- 4)Session服务器。把应用服务器分为无状态的应用服务器和有状态的Session服务器
-
高可用的服务层
- 1)分级管理
- 2)超时设置
- 3)异步调用
- 4)服务降级
- 5)幂等性设计
-
高可用的数据层
- 保证数据高可用性的手段是失效确认、失效转移和数据恢复。
- 1)失效确认。系统通过心跳检测和应用访问失败的报告来确认某台服务器是否宕机。
- 2)访问转移。
- 3)数据恢复。从健康的服务器上复制数据,将副本的数量恢复到系统设定的值。
-
-
高可用质量保证
- “不允许没有监控的系统上线”
- 1)采集监控数据。
-
2)监控管理
- 系统告警
- 失效转移
- 自动优雅降级
-
-
可伸缩设计
-
可扩展性设计
- 扩展性是指在对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力。
架构保障:质量与风险¶
-
内建质量体系
- 成本理论告诉我们,设计和开发是成本控制的源头。
- 将这种前期预防的质量管理模式称为“内建质量(Built-in Quality)”
- 内建质量强调“三不原则”:不接受不良品,不设计或制造不良品,不流出不良品
- “早发现、早解决”的难点在于发现而不是解决,质量管理的基本思路是上游管控,但要建立有效的“早发现、早解决”机制并不那么容易。
-
PDCA循环
- 1)P(Plan)
- 2)D(Do)
- 3)C(Check)
- 4)A(Act)
- PDCA 循环是爬楼梯上升式的循环,每转动一周,质量就提高一步。在一轮循环结束后,也许只解决了一部分问题,或者在解决的过程中又衍生出新的问题,因此必须再进行一轮PDCA循环。如此循环迭代,不断提高软件的质量。
-
黑天鹏事件、蝴蝶效应和墨菲定律
-
黑天鹅事件
- 黑天鹅寓意不可预测的重大稀有事件。
-
黑天鹅事件特点
- 1)稀缺、史无前例(Rarity);
- 2)影响很极端(Extreme Impact);
- 3)具有意外性,但人的本性促使我们在事后为它的发生编造理由,并且或多或少认为它是可解释和可预测的。
-
人类总是过度相信经验。培根:我们要当心被自己思想的丝线束缚。
-
蝴蝶效应
- 美国气象学家洛伦兹:“亚马孙雨林中的一只蝴蝶偶尔振动翅膀,也许会引起两周后美国得克萨斯州的一场龙卷风。”
- 图灵奖得主Tony Hoare在Null References: The Billion Dollar Mistake文章中写到:“我将Null引用称为自己的10亿美元错误,它的发明是在1965年,那时我用一种面向对象语言(ALGOL W)设计了第1个全面的引用类型的系统,目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。但是我未能抵御住诱惑,加入了Null引用,仅仅因为实现起来非常容易。Null引用导致了数不清的错误、漏洞和系统崩溃,可能会在之后40年里造成10亿美元的损失。”
- 初始条件十分微小的变化经过不断放大,也许会对未来造成极大的影响。
-
墨菲定律
- 爱德华·墨菲(Edward A. Murphy)原句:如果有两种或两种以上的方式去做某件事情,而其中一种选择方式将导致灾难,则必定有人做出这种选择。
-
主要内容
- 任何事都没有表面看起来那么简单;
- 做所有的事花费的时间都会比你预期的时间长;
- 会出错的事总会出错;
- 如果你担心某种情况会发生,它就更有可能发生。
-
莫存侥幸心理
- 由于认知的局限性、骄傲心态、问题域的复杂性及不可把握性等因素,软件行业发生问题的概率很高。
- 墨菲定律告诉我们:事情往往会朝着我们所能想到的不好的方向发展,只要存在这种可能性。
-
启示
- 黑天鹅事件告诉我们要走出经验主义,不要将自己知道的东西太当回事,我们不知道的事比我们知道的事更有意义。
- 蝴蝶效应告诉我们要及时感知问题,止损和熔断,避免问题范围扩大甚至传染到其他相关领域。
- 墨菲定律告诉我们,我们知道的但是忽略的地方终究会出问题。
-
-
技术债问题
-
定义
- 技术债务是由Ward Cunningham在1992年创造的一个比喻,被定义为我们有意或无意中做了错误的或不理想的技术决策所累积的债务。
-
技术债务分为两类
- ◎ 无意的:由于经验的缺乏导致初级开发者编写了质量低劣的代码。
- ◎ 有意的:团队根据当前而非未来进行了设计选型,这种方式可能很快解决了当前的问题,但很拙劣。
-
临时方案是技术债
- 随着技术团队人员的能力提升,无意的技术债务可能会越来越少。但是在很多时候,我们欠下的技术债务往往是有意的,随着业务的高速发展,还会带来很多变化。为了快速实现业务的功能及验证商业决策,开发人员大多会采用很多临时方案。 毫不客气地说,这些临时方案其实就是技术债。
- 临时方案的可怕之处,不仅仅在于其不全面,更在于如果不对其及时处理,可能就永远“临时”下去了。随着团队成员的不断更替,慢慢地就没人记得这个临时方案了。一个说好只支撑两个月的临时代码,可能两年后还在线上机器里运行着。
- 随着系统中的临时方案越来越多,我们需要还的技术债也越来越多。欠债并不可怕,可怕的是没有偿还计划。
-
解决技术债
- 通过人员培训、代码审查等方式杜绝无意的技术债。
- 要尽量避免有意的技术债。
- 如果一定要引入技术债,则一定不要引入不计后果的技术债。
- 要记录所有欠下的技术债,并且有偿还计划。
-
技术债 与金融债
- 一个人贷了款就会产生债务,如果可以定期还款,则所欠的债务是可以接受的,不会产生进一步的问题;
- 但是,如果不还款,就会以利息作为惩罚,利息还会随着不还款次数的增加而增加。
- 如果这个人在很长一段时间内不能支付任何款项,那么应计利息会使他更难以偿还债务。在极端情况下,这个人就不得不宣布自己破产。
- 技术债务也一样,如果越积越多,最终也会导致非常严重的后果。
-
偈语
- 技术债要做清偿计划。
- 不要在下雨天补屋顶。
-
-
康威定律
- 康威定律:设计系统的架构受制于产生这些设计的组织的沟通结构。
-
烟囱式架构
- 满足业务快速试错,甚至产品之间有一些代码重复,但各团队各司其职,是最快反应的最小研发响应单元。
- 对应到康威定律的组织和架构关系,这也是依赖最少的组织,沟通成本最低。
-
平台型架构
- 随着业务的发展,相似的诉求会凸显,因此沉淀为越来越多的平台。
-
求解质量熵
-
敏捷思想
- 基于敏捷的思想可以快速发布、纠正和调整,以满足用户的需求。
-
价值观
- 个体和互动优于流程和工具。
- 能工作的软件优于详尽的文档。
- 客户合作优于合同谈判。
- 响应变化优于遵循计划。
-
系统化思想
-
创新解决方案
- Perl设计者在Programming Perl:“优秀的程序员有三大美德:懒惰、急躁和傲慢。”
- 为了减少总能量支出而不遗余力地努力的素质就是懒惰;受不了程序低效执行就是急躁;容不得对错误不管不顾就是傲慢。
-
-
故障复盘流程及模板
- 故障的处理流:故障发现、故障处理及故障复盘。
-
什么是故障复盘
- “复盘”原是围棋术语,本意是对弈者在下完一盘棋之后,重新在棋盘上把对弈过程摆一遍,看看哪些地方下得好,哪些地方下得不好,哪些地方可以有不同甚至更好的下法等。
-
为什么要做故障复盘
- 故障复盘的目的不是“分锅”和“甩锅”。
- 最终目的是通过对已经产生的故障的反思,进行总结及优化。
-
如何做故障复盘
-
“RASA”理论
- Review(回顾)
- Analyze(分析)
- Summary(总结)
- Action(行动)
-
故障复盘的流程
- 1)故障记录
- 2)故障分析
- 3)复盘会议
- 4)行动点制定
- 5)行动点验收
-
-
监控与告警
-
应急处置
- 应急处理原则
- 应急流程
管理探秘¶
前期准备¶
-
构建自我阶段性目标
- 《游戏改变世界》中写到:满意的工作从两件事开始,一是有明确的目标,二是有实现这一目标的可操作性步骤。
- 有明确的目标可激励我们采取行动,让我们知道自己该做什么;
- 有可操作性步骤可确保我们立刻朝着目标前进。
- 我们需要对自己在做的事情有一张明确的阶段性画像,这张画像一定是自己内心所向往的。
-
体验自己的目标身份
- 必备技能准备:追求最优化的实现,剖析问题的本质及整体业务架构。
- 从技术主管的视角去工作:带着业务目标去看待每一次的项目实现,充分考虑稳定性、可扩展性和每一次的项目实现对目标的影响
- 学会用户思维
- 建立经济视角
- 成为内部BD(商务合作)
- 规划团队从规划自己开始
- 数据驱动:关注结果数据且分享给团队,并对数据做出实时反馈
-
勇于抓住机会
- 保持对事件的敏锐洞察能力,当一件能够让你独立完成且把能力付诸实践的任务发生时,请对自己说:“这就是我的!”
管理团队¶
-
领导力
-
什么是领导力
-
管理定义
- 管理是指在特定环境条件下,对组织所拥有的人力、物力、财力、信息等资源进行有效决策、计划、组织、领导、控制,以期高效地达到既定的组织目标的过程。
- 概括一下:管理的核心,就是利用组织的资源把事干好,从而达到组织的目标
-
领导力定义
- 领导力就是把一个人的视野提到更高的境界,把一个人的成就提到更高的标准,锤炼其人格,使之超越通常的局限。
-
管理 vs 领导力
- 管理的核心在于事,领导力的核心在于人。
-
-
如何构建领导力:“技术领导力4D框架”
- 维度1:提供清晰的领导力风格,并以信任感作为基石
- 维度2:了解业务,并带领团队达到高绩效
-
维度3:发展自己和团队成员
- 通用电气(GE)前CEO杰克·韦尔奇:“成为领袖之前,成功就是让自己成长。成为领袖之后,成功是让别人成长”。
- 调查后最能够激励员工的因素:成就感、认可和工作本身。
- 技术专家面对的问题域主要是“事”层面的技术、系统、算法和程序等,领导者面对的问题域则主要是“人”(人的情绪、观念、想法等
- 对于刚从技术专家转换角色的领导者,首先需要实现的突破就是忍住“我自己做”的冲动,转为发掘和培养一个最合适的人,然后选择最适合他的工作,把事做到最好。
-
维度4:塑造未来
- 愿景型领导
-
-
让自己成为T型人才
- “一专多能”,T字的横轴表示跨专业领域的思考能力,T字的纴轴表示精深的专业能力。
-
具备的能力
- 有专业的深度,也有思维的广度,能够跨界思考和探索。
- 专注于当下,且对外部世界持开放态度,能接纳不同的视角和观点。
- 关注问题的根源,不急于设计解决方案,而是从系统的角度整合方案设计。
-
高效时间管理
-
如何做到高效时间管理?
- 博恩·崔西:只需效仿优秀、成功管理者的高效时间管理方法,就可以获得很好的效果!
-
确定在做的事情符合自己的目标
- 第1步:确定自己究竟要什么
- 第2步:把目标写下来
-
第3步:为目标设定最后期限,并让大家都知道
- 承诺和一致性原理指的是,人一旦做出承诺,所做的行动都会跟承诺的事保持一致。
-
第4步:将实现目标要做的最重要事情列出来
- “魔数3”原理:因为只有三件事,所以人们会觉得更有信心去完成;同时,能够立即着手去实施,还能较快地看到成效,从而更有信心完成任务,进入一个正循环。
-
第5步:每天都做一些接近自己目标的事情
-
应用80/20法则
- 歌德曾经说过:“只要合理利用时间,我们就永远有充足的时间。”
- 把80/20法则运用到时间管理:如果你有10项工作要做,则做其中两项的价值比做其他8项加起来都要大!
-
事实证明,完成一项重要工作的时间往往和完成一项琐碎工作的时间类似。
- 但是完成高价值的工作,可以给我们带来较高的自豪感和满足感;
- 而完成一项琐碎的工作,只能让我们有极低的满足感。
-
-
遇到“不服管”的员工怎么办
- 如何处理冲突
- 引导员工主动工作
-
如何从带10个人到带100个人
- 拿破仑说:“我们现在讨论的进攻计划就是战略,但是,让100万大军心甘情愿地向莫斯科进军的是文化!”
- 企业文化,是一个组织由其价值观、信念、仪式、符号、处事方式等组成的特有的文化形象。
-
企业文化作用
-
胶水作用
- 从“同伙”到“同伴”
-
地基作用
- 标尺作用
- 罗盘作用
-
-
如何打造统一的文化和价值观?
-
第1步:团队名称和形象标识讨论
- 西奥迪尼博士在《影响力》书中揭示:人们都喜欢和自己有着共同特征的人,而讨论团队名称和团队形象标识的过程,就是团队寻找和确定共同特征的过程。
-
第2步:团队价值观“内涵”和“外延”讨论
- 团队成员回答3个问题。
- ◎ 问题1:我如何理解这个价值观。
- ◎ 问题2:哪些实际工作中的行为践行了这个价值观。
- ◎ 问题3:哪些实际工作中的行为违背了这个价值观。
- 通过这几步,就完成了团队对这个价值观“内涵”和“外延”的私人订制。
-
第3步:基于团队认可的价值观,团队协同一致的处事方式。
-
-
OKR + KPI
-
OKR 和 KPI一同使用,才能组成一个闭环的Smart KPI。
- OKR的含义是“目标和关键成果”,简单地说,目标确定了前进的方向,而关键成果能告诉我们需要取得什么样的结果,才能确保达到了预定的目标。
- 另一方面,KPI能够对结果提供精确和定量的评估。
- 因此,结合OKR和KPI,就能够完整地涵盖目标、关键成果,以及结果评估这三个相辅相成的重要方面,从而形成一个目标设定、评估和持续提升的闭环结构。
-
第1步:根据目标和验收标准制订OKR(目标和关键成果)
- 第2步:为OKR中的关键成果制订KPI
- 第3步:在监测先导性指标过程中巧妙利用OKR进行纠偏
-
-
如何向上管理