简介

计算机系统 – 基础知识

  • 计算系统,是一个完整的工作系统。系统不仅包括计算机,还包括各种软件以及外部设备。
  • 计算机就是用于执行指定操作的机器,这些操作通常是一组指令的集合,这些指令也称为软件(software)

  • 计算机硬件(hardware)是系统的物理组成部分,是可以真真切切触摸到的
  • 硬件包括计算机及其周边设备,例如键盘,鼠标,显示器,硬盘,打印机等

  • 计算机软件是以电子形式在硬件中驻留和运行的程序,例如编译器,操作系统以及应用程序等。软件提供了人机接口(Human Computer Interface, HCI),并定义了计算机应该执行的操作

计算机硬件

  • 在冯·诺依曼的模型里,可以看到输入设备,输出设备,存储单元以及由控制单元(control unit)和带有累加器(accumulator)的算数逻辑单元(Arithmetic Logic Unit, ALU)构成的模块

  • 存储单元存储数据,控制单元则控制数据的传输和数据的处理。
  • 控制单元取回指令并对其进行译码后存入存储单元中,同时从输入设备接收数据,并将数据存入存储单元

  • 控制单元对可执行指令进行译码,并将数据送入ALU。 ALU执行操作,控制单元则将计算结果送入输出设备。
  • 这里的控制单元和ALU一起称为中央处理单元(Central Processing Unit, CPU)

  • ALU中的累加器是一组高速寄存器的集合,用作算术和逻辑操作的操作数和结果的临时存储
  • ALU中的寄存器大小对应着计算机的字长(word size)。
  • 一般的字长有16位,32位,64位,这取决于处理器的设计方案
  • 1位代表一个二进制位,而且处理器的字长都是2的幂

计算机软件

  • 计算机软甲包含了我们希望计算机执行的指令或者命令。
  • 在计算机软件中的分类中有几类比较重要,包括操作系统,软件工具,各种程序语言的编译器

  • 如果某个工程问题可以通过使用软件工具解决,通常来说,这比写一个程序来解决问题更高效。
  • 然而,很多问题都不能通过现有软件工具来解决,或者软件工具在需要解决问题的计算机系统上不可用,因此,我们也需要知道如何使用计算机语言来写程序。
  • 随着一些功能强大的软件的出现,这些软件除了支持专门的操作系统还包含了编程语言,这使得软件工具和计算机语言之间的区别变得越来越模糊

计算机语言

  • 计算机语言有不同的层次

  • 机器语言(machine language)是最基础的语言,是与计算机硬件设计密切相关的
  • 因为计算机的设计是建立在二态技术(类似的情况有电路的开合,开关的打开与关闭,电池的正负极)基础上的,所以机器语言也使用两种符号来编写,通常使用数字0和1来表示。
  • 因此,机器语言也是一种二进制语言,它的指令都是使用由0和1组成的序列携程的,这些序列称为二进制串
  • 因为机器语言是与计算机硬件紧密相关的,所以SUN计算机上与HP计算机上的机器语言是不同的

  • 对于某种特定的计算机设计语言,汇编语言(assembly language)也是唯一的,但是汇编语言的指令是用可读性更好的符号语句而非二进制串编写的
  • 汇编语言的语句类型通常并不是很多,在使用汇编语言时,必须了解与之相应的硬件信息,

  • 包含微处理器的设备通常要求程序能够极快地执行,这样的程序称为实时程序(real-time program)
  • 实时程序通常使用汇编语言编写,这样可以发挥特定计算机硬件的优势,以提高执行速度

  • 高级语言(high-level language)是具有类似自然语言的命令和指令的计算机语言,C++,C, Fortran,Ada, Java,Basic等都是高级语言

  • 执行计算机程序,要执行使用诸如C++这样的高级语言编写的程序,必须先将高级语言指令翻译成机器语言。
  • 执行这种翻译任务的程序称为编译器(compiler)
  • 在编译阶段出现的错误称作解析错误(parse error)或语法错误(syntax error)
  • 当没有语法错误后,编译器就可以成功地翻译程序,并声称机器语言形式的程序,生成的程序将完成最开始C++程序所要执行的功能。
    • 这一,C++程序称为源文件(source file),而生成的机器语言版本则称为目标文件(object file)
    • 因此,源程序和目标程序所描述的功能是一致的,但是源程序使用高级语言编写的,而目标程序则是以机器语言的形式
  • 执行(execution),这些步骤包括将目标程序与机器语言语句进行链接(linking)和把程序载入(loading)内存
  • 在执行阶段出现的错误称为执行错误(execution error),运行时错误(run-time error)或逻辑错误(logic error),这些错误也叫做程序bug
  • 如果执行错误是由于程序中所编写的语句产生的,必须在源程序中修改错误并重新进行编译,这个过程叫做调试(debugging)

数据的表示与存储

  • 在数字计算机中,一个二进制位可以使用一个位(bit)表示。
  • 位上的值在任何时候都只能是0或1.从硬件的角度来说,当该位处于关闭或低电位时,它的值为0,而当其处于打开或高电位时,其值为1

  • 二进制数在内存中以序列的形式存储,位序列称为字(word)
  • 字的长度每增加1位,字所表示的数的大小就以2的幂增加,而其所能表示的范围就翻一倍
  • 内存中可用的字的数目称为内存空间,或者地址空间(address space)

  • 字长决定了存储在某个地址中的数值范围,而地址空间则决定了可以存储多少个字
  • C++包含整型数,浮点数,字符和布尔值等内建的数据类型。每种数据类型都由一个以字节(byte)为单位来确定的预定义大小,1个字节就是一个8位的序列。
  • 为了定义标识符(identifier)并为它分配空间,则需要使用类型声明语句(type declaration statement)。
  • 当定义一个标识符时,便确定了其数据类型,并在内存中分配相应字节数的空间。

  • 当数据存储在内存中时,它是以位序列存在的。
  • 这些位序列可能是指令,数字,字符,图像或数字信号的一部分,或者是其他类型的数据
    • 负数的存储
    • 浮点数的存储

框架(Framework)

  • 什么是框架?
    • 框架(Framework)是构成一类特定软件可复用设计的一组相互协作的类。框架规定了你的应用的体系结构。
    • 它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节
    • 框架一般处在低层应用平台和高层业务逻辑之间的中间层
  • 为什么要用框架?
    • 因为软件系统发展到今天已经很复杂了,特别是服务器端软件,涉及到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业务逻辑设计。
    • 而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事务处理,安全性,数据流控制等问题。
    • 还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处
  • 软件为什么要分层?
  • 为了实现“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源等

  • 框架和设计模式
    • 构件通常是代码重用,而设计模式是设计重用,框架则介于两者之间,部分代码重用,部分设计重用,有时分析也可重用
    • 框架与设计模式虽然相似,但却有着根本的不同。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述,它比框架更抽象
    • 框架可以用代码表示,也能直接执行或复用,而对模式而言只有实例才能用代码表示;
    • 设计模式是比框架更小的元素,一个框架中往往含有一个或多个设计模式,框架总是针对某一特定应用领域,但同一模式却可适用于各种应用
  • 构件(component)
    • 构件是面向软件体系架构的可复用软件模块。
    • 构件是可复用的软件组成成份,可被用来构造其他软件。它可以是被封装的对象类、类树、一些功能模块、软件框架(framework)、软件构架(或体系结构Architectural)、文档、分析件、设计模式(Pattern)等
    • 1995年,Ian Graham给出的构件定义如下:构件(Component)是指一个对象(接口规范、或二进制代码),它被用于复用,接口被明确定义
    • 构件是作为一个逻辑紧密的程序代码包的形式出现的,有着良好的接口
    • 采用构件软件不需要重新编译,也不需要源代码并且不局限于某一种编程语言。该过程叫做二进制复用(Binary Reuse),因为它是建立在接口而不是源代码级别的复用之上的。虽然软件构件必须遵守一致的接口,但是它们的内部实现是完全自动的。因此,可以用过程语言和面向对象语言创建构件
  • 构件和面向对象设计的差别
    • 在纯面向对象的设计中,对象(类)、封装和继承三者缺一不可,但对构件可以没有继承性,只要实现封装即可
    • 从构件和对象的生成方式上,对象生成属于实例化的过程,比较单一,而生成构件的方式较多
    • 构件是设计的概念,与具体编程语言无关,不像对象属于编程中的概念,要依赖于具体的编程语言
    • 在对构件操作时不允许直接操作构件中的数据,数据真正被封装了。而对象的操作通过公共接口部分,这样数据是可能被访问操作的
    • 对象对软件复用是通过继承实现的,构件对软件复用不仅可以通过继承还可以通过组装时的引用来实现
    • 因此,构件不是对象,只是与对象类似
  • 在软件生产中有三种级别的重用:
    • 内部重用,即在同一应用中能公共使用的抽象块
    • 代码重用,即将通用模块组合成库或工具集,以便在多个应用和领域都能使用
    • 应用框架的重用,即为专用领域提供通用的或现成的基础结构,以获得最高级别的重用性
  • 框架开发
    • 框架的最大好处就是重用。面向对象系统获得的最大的复用方式就是框架,一个大的应用系统往往可能由多层互相协作的框架组成
    • 由于框架能重用代码,因此从一已有构件库中建立应用变得非常容易,因为构件都采用框架统一定义的接口,从而使构件间的通信简单
  • 框架解决的问题
    • 框架要解决的最重要的一个问题是技术整合的问题
    • 软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响
    • 举例:
      • 一个做视频流应用的软件企业,他为电广行业提供整体的解决方案。他的优势在于将各种各样的视频硬件、服务器、和管理结合起来,因此他扮演的是一个集成商的角色。因此他的核心价值在于使用软件技术将不同的硬件整合起来,并在硬件的整合层面上提供一个统一的管理平台。所以他的精力应该放在解决两个问题
        • 如何找到一种方法,将不同的硬件整合起来,注意,这里的整合并不是技术整合,而是一种思路上的整合
        • 如何描述这个管理系统的规范。你需要描述各种管理活动,以及管理中所涉及的不同实体。因为管理系统是针对硬件的管理,所以它是构架在硬件整合平台之上的

数据模型(Data Model)

  • 数据模型(Data Model)是数据特征的抽象,它从抽象层次上描述了系统的静态特征、动态行为和约束条件,为数据库系统的信息表示与操作提供一个抽象的框架。数据模型所描述的内容有三部分,分别是数据结构、数据操作和数据约束

  • 模型可更形象、直观地揭示事物的本质特征,使人们对事物有一个更加全面、深入的认识,从而可以帮助人们更好地解决问题。利用模型对事物进行描述是人们在认识和改造世界过程中广泛采用的一种方法。计算机不能直接处理现实世界中的客观事物,而数据库系统正是使用计算机技术对客观事物进行管理,因此就需要对客观事物进行抽象、模拟,以建立适合于数据库系统进行管理的数据模型。数据模型是对现实世界数据特征的模拟和抽象
  • 数据模型是数据库设计中用来对现实世界进行抽象的工具,是数据库中用于提供信息表示和操作手段的形式构架。数据模型是数据库系统的核心和基础

  • 数据模型所描述的内容包括三个部分:数据结构、数据操作、数据约束
    • 数据结构:数据模型中的数据结构主要描述数据的类型、内容、性质以及数据间的联系等。数据结构是数据模型的基础,数据操作和约束都建立在数据结构上。不同的数据结构具有不同的操作和约束
    • 数据操作:数据模型中数据操作主要描述在相应的数据结构上的操作类型和操作方式
    • 数据约束:数据模型中的数据约束主要描述数据结构内数据间的语法、词义联系、它们之间的制约和依存关系,以及数据动态变化的规则,以保证数据的正确、有效和相容

如何分析和解决一个任务:思维方式

  1. 先从整体上有一个总体思路,比如各个模块之间如何通信、依赖等,然后从一个点开始,照着调用路径分析下去,不要看一个类中的每个方法,而是只看用到的方法。
  2. 把所想要完成的目标分步实现,如果思路不清晰,不知道如何去做,可以模拟现实中的步骤。
    1. 要把想实现的东西转换成代码,不要想着最终结果好难,不知道怎么做。
    2. 先分步,一步一步完成,然后分别去想每一步是怎么实现的。第一次写完之后肯定是缺陷的,仔细反复调试,慢慢修改优化,不要看着目标就被吓到。
  3. 函数最主要的作用就是将一个大型程序分解成多个易于管理的小部分,这一过程称为分解(decomposition)。而你所需做的应是将一个高层次问题细分为一系列低层次的函数,每一个函数有其自己独立的功能。然而,找到问题正确的细分方法有很大的挑战,需要不断练习、思考和尝试。编程是一门艺术,好的问题分解策略主要来源于实际经验。
  4. 然而,作为一种通用的规则,问题的分解过程一般从程序的主程序开始。此时,我们将整个程序视为一个整体,并尝试从中分析并抓取出其主要部分。一旦程序的最主要部分被识别出来,就可以将它们定义为一些相互独立的函数。由于某些函数可能本身依然复杂,因此通常需要将他们再分解为更小的部分。我们可以不断重复这一分解过程直到每个问题足够简单明了以便于解决。上述分解过程称为自顶向下的程序设计(top-down design)逐步求精的方法(ste[wose refinement)

深入理解计算机操作系统

过程是软件中一种很重要的抽象

  • 它提供了一种封装代码的方式,用一组指定的参数和一个可选的返回值实现了某种功能.然后,可以再程序中不同的地方调用这个函数,设计良好的软件用过程作为抽象机制,隐藏某个行为的具体实现,同时又提供清晰简洁的接口定义,说明要计算的是哪些值,过程会对程序状态产生什么样的影响.
  • 不同编程语言中,过程的形式多样:函数(function),方法(method),子例程(subroutine),处理函数(handle)等等

数据对齐

  • 许多计算机系统对基本数据类型的合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2,4或8)的倍数,这种对齐限制简化了形成处理器和内存系统之间接口的硬件设计
  • 强制对齐:
    • 对于大多数x86-64指令来说,保持数据对其能够提高效率,但是它不会影响程序的行为
    • 另一方面,如果数据没有对齐,某些型号的Intel和AMD处理器对于有些实现多媒体操作的SSE指令,就无法正确执行.

理解指针

  • 指针以一种统一方式,对不同数据结构中的元素产生引用
  • 每个指针都对应一个类型,这个类型表明该指针指向的是哪一类对象
    • int* ip表示变量ip是一个指向int类型对象的指针,char** cpp表示cpp指针指向的对象自身就是一个指向char类型对象的指针
    • 通常,如果对象类型为T,那么指针的类型为T*;特殊的, void *类型代表通用指针.比如说,malloc函数返回一个通用指针,然后通过显式强制类型转换或者赋值操作那样的隐式强制类型转换,将它转换成一个有类型的指针.
    • 指针类型不是机器代码中的一部分,它们是C语言提供的一种抽象,帮助程序员避免寻址错误
    • 每个指针都有一个值,这个值是某个指定类型的对象的地址.特殊的NULL(0)值表示该指针没有指向任何地方
    • 指针用&运算符创建.这个运算符可以应用到任何lvalue类的C表达式上,lvalue表示可以出现在赋值语句左边的表达式
    • *操作符用于间接引用指针,其结果是一个值,它的类型与该指针的类型一致,间接引用是用内存引用来实现的,要么是存储到一个指定的地址,要么是从指定的地址读取
    • 数组与指针紧密联系.一个数组的名字可以像一个指针变量一样引用(但是不能修改).数组引用(例如a[3])与指针运算和间接引用(例如*(a + 3))有一样的效果.数组引用和指针运算都需要用对象大小对偏移量进行伸缩
    • 将指针从一种类型强制转换成另一种类型,只改变它的类型,而不改变它的值.强制类型转换的一个效果是改变指针运算的伸缩
    • 指针也可以指向函数.这提供了一个很强大的存储和向代码传递引用的功能,这些引用可以被程序的某个其他部分调用
  • 内存越界引用和缓冲区溢出
    1. 模型相关的,b站视频
    2. 多看项目(github),多练
    3. 大厂的代码,main中变量的声明(amlogic, nvidia)
    4. shell 不必花费太多精力,推荐:python,php(现在不要去学,单点击穿,现在主要学习C++)

如何阅读大型项目的代码?

  • 目的性:读代码的时候如果有目的性是最好的。
  • 时间:总体来讲,阅读大型项目的代码需要时间。
  • 方法论:抽象地说,阅读代码有两种方向,自底向上和自顶向下。
    • 自底向上,从具体的文件到子模块,从子模块到功能集,再到整个项目,强调的是从具体实现出发总结出一般抽象。
    • 自顶向下,从项目的顶层设计到责任分发,从责任分发到功能分发,再到具体的实现代码,强调的是从抽象设计出发落实到具体实现。
    • 两种方法是两个不同的视角。实践中经常需要结合两种方向随时切换地采用,当对大方向了解不足的时候自顶向下的看,当对实现细节不够明确的时候自底向上的看

上下文 context

  • 维基百科
    • 在计算机科学中,任务(task)的上下文(context)是一个任务所必不可少的一组数据(该任务可以是进程,线程)。
    • 这些数据允许任务中断,在这之后仍可在同一个地方继续执行。上下文的这一概念在中断的任务的场景下具有重大意义。其中,任务在被中断之后,处理器保存上下文并提供中断处理(interrupt service routine)。因此,上下文越小,延迟越小。
    • 上下文的数据可能存放于处理器寄存器,任务所利用的内存,某些操作系统管理的任务所使用的控制寄存器(control registers)
  • 通俗的理解,上下文,也就是执行任务所需要的相关信息。
  • 这个任务可以是一段代码,一个线程,一个进程,一个函数。当这个“任务”相关的信息需要保存下来时,就可以使用context来记录。

关于业务对象(Business Object)本质的思考

  • 对于采用OO(Object Oriented)思想,并具有N层架构的计算机程序而言,业务对象(Business Object)一般位于业务逻辑层(也叫领域层[1]),作为领域模型元素的一部分,描述了来自于业务域中的一个人、事、物或概念[2],主要用来解决商业逻辑(即业务操作)问题,是为实现当前软件(或系统)的某一(或某些)特定功能而服务的。因此,它仅仅是从当前业务域的角度,对现实世界的一次抽象。

  • 业务对象、业务实体、实体、领域对象在某种程度上是可以互换的术语[此处仅仅说是“某种程度上”,目的在于分析BO三要素的实质,针对于不同的理论,这几个名词确实存在差别]

  • 对象三要素

    • 标识(identity),是唯一区别其他对象的标志;
    • 状态(state),描述对象所蕴含的信息;
    • 行为(behavior),对象所持有的、描述对象如何被使用的方法

状态

  • 相对于字段、变量、属性等词,用“状态”一词来描述对象的“性格特点”最为合适。字段、变量、属性等,无非是描述对象状态的不同表现手法

  • 就目前个人对BO状态的理解而言,BO的状态信息有两类:固有信息和动态信息。
    • 固有信息,是对象从诞生时与生俱来的信息(就像一个新生命的诞生一样,出生年月日、肤色、性别等信息是生来就有的);
    • 动态信息,即随着BO的成长,它会走入某一个人生场景中,扮演了某一个角色,从而在这个场景中被赋予了一些额外的信息,如:小明是个学生(从入学时,学生的基本信息被赋予),某天他去市图书馆借阅图书,此时他在借书这个场景中扮演了借书者这个角色,从而具有了借阅证、借阅信息等动态信息。
  • 固有信息
    • 业务对象在被构建(初始化)后,应达到一个相对比较稳定,且具有一定业务含义的状态,即业务对象的属性应已进行了相应的初始化设置,这样的构建才算合理、完整
    • 与人类世界类似,人在出生时,就已经构造好了婴儿的鼻子、眼睛、手等内容,虽然此时他还没有衣服、母语、身份证等信息,但他在出生时,是一个相对较稳定且有意义的个体,就可以完成婴儿的核心操作,如:哭、呼吸、挥手等。
    • 提前构造好该构造好的信息(注意:不是所有的信息都要一股脑的全构造好)是保证BO不被滥用的第一步
  • 动态信息
    • 进入某一场景,扮演某一角色(我更喜欢用“戴上帽子”这个说法),将拥有额外的动态属性。同“固有属性”的构建,某一业务对象走入特定的场景,扮演了另一角色时,也应该将这些动态属性予以设置
    • 对象应尽量在构建自身的过程中完成自身状态的设置

属性封装

  • 在OO的世界里,封装的概念是最简单的,但却是最关键且最难以把握的

  • 信息不封装(或封装不完整)带来的副作用
    • 内聚,难。业务对象的信息过多的暴露出去,容易滋生强盗逻辑,想捏回去形成一团,难!信息全部都暴露给外界,调用者还需要你BO干嘛?原因很简单:我能够伸手拿到你的任何信息,想实现什么就实现什么,可以为所欲为。如果你还有胆量暴露一些更改BO属性的权限,那我岂不是想怎么改就怎么改
    • 解耦,难。一个成语叫“覆水难收”,放出去的信息将被调用者肆意使用,而且呈现出快速蔓延的趋势,一张复杂的耦合网必然产生。等回头开始重构解耦时,发现堆积如山的代码、耦合似网的结构,已经让你无从下手。动一下,就引起全身阵痛
    • 常见的做法
      • 刨一小块,改改变量名、方法名,移一些代码,用接口再包装一下(隔离嘛),循环的修改,最终发现进展依然是非常缓慢,几乎还没触及到业务核心;
      • 先用方法(1)试试,一阵子后,MD,烦死了,直接推倒重新搞。这些做法还需要考虑一个问题:放出去的接口和信息已被调用者大量使用,怎么办?
  • 针对上述的苦痛,推荐下面的做法:
    • 在对象构建时,把能设置的状态信息尽可能的予以赋值,提前封装;
    • 尽可能的不要公布内部信息。能够private的,尽可能的私有。除非迫不得已,尽量不要将setter() 放出,仅使其read only

行为

  • 业务行为才是软件真正所关注的问题,对象的行为方式是对象价值的重要体现,也是区别于其他对象的重要标志。
  • 因此,我们说“BO因职责而存在!”

  • 贫血VS充血
    • 贫血对象
      • 由不具有任何行为的业务对象形成的领域模型,称为“贫血模型”[2]。对只有属性的getter/setter方法,不具有业务行为的BO,可认为是“贫血对象”。
      • 丧失业务逻辑行为的贫血对象,和Value Oject类似,扮演了Data Container的角色,而在业务域中的逻辑操作方面将失去能力(或被遗弃、边缘化)
    • 充血对象
    • 按大师的说法,与BO直接相关的行为职责将划归到BO中,使其在领域模型中扮演重要的角色
  • 但是二者不能绝对的说谁好、谁不好,应该一分为二的去看,它们各自具有其特点,应用在不同的场景中。特别地,对于那些需求难以完全吃透、明确,或许用贫血模型较充血模型要更好把控局势。
  • 用户需求——业务——领域是一个对知识掌握程度递增的过程,领域模型的建立应基于对客观业务域的透彻掌握,不能偏左,也不可以偏右(不就是博弈么?没有最优秀的东西,只存在考虑诸多因素下的较为合适的东西)

  • business core
    • 它应该是最精炼、纯粹、简单、直接、轻量级的业务核心。
    • 因此,不属于核心业务逻辑范畴的职责和行为(如:持久化操作),尽量抛出去交给该处理它们的对象去处理。
  • 上帝类(God class)
    • 可以认为它就是一个充血充得快爆掉的对象
    • 它几乎可以干所有涉及到它的工作,从而形成代码有几百行乃至上千行的牛X类
    • 有了这么一个牛X类,其他类不就成了浮云和鸡肋了么?这样可能引发很多问题
      • 理解难。几千行的代码,没有几个人有耐心阅读,几乎没人能够完全理解其表达的业务含义。
      • 修改难。没有很好的理解,如何修改?如何重构?
      • 扩展难。丢了它玩不转,不丢它又引来一堆麻烦,对于一个几千行的实现类来说(除非大多都是public static 的方法),吃资源不说,接口隔离、应对扩展方面也是比较吃力的。
  • 谁拥有数据,谁持有行为
    • BO行为的本质是对BO自身状态的改变,以实现业务目标,并且这种状态的更改可能还需要其他协作者的参与(关联关系)。
    • 因此,我们可以说“谁拥有数据,谁就持有更改这些数据(状态)的行为
    • 现实中,跟BO有关系的行为可能较多,全部纳入到BO中,会造成BO的臃肿和污染,违背SRP(职责单一)
      • 可重用度高或是对象固有、与BO状态密切关联的方法放在BO中
      • 可重用度低或者不是对象所固有(而依赖于特定场景)、与BO状态没有密切联系的方法放在BO管理者或服务层
  • 方法属于客户
    • BO就是一个黑盒子,其中包含了逻辑和数据,而对象的使用者不知道里面有什么数据,也不知道实际的运行逻辑。使用者所能做的就是与对象进行交互,以完成当前的业务目标。因此,对象的行为是为客户而定
    • 行为(方法、职责)是BO存在的根本,而行为就是为了交互,为供调用者所使用。
    • 调用者会根据自己的需要,向它认为应该由谁提供行为的BO发出操作申请,一切都是以“客户(调用者)”为中心而服务的

软件工程

  • 程序 = 算法 + 模型 + 数据结构
  • 传统的程序只是算法和数据结构的组合,但是在AI迅猛发展的今天,在程序里几乎都有个“模型”
  • 模型,一般是指机器学习或深度学习训练出来的Model,里面既有逻辑又有数据,所以它既不是单纯的算法,也不是单纯的数据结构。

  • 软件工程师 = 程序员 + 软件工程知识
  • PM(Program Manager) = 项目管理 + 产品管理,直译为程序经理,实际上是项目管理和产品管理的二合一角色
  • 团队 = PM + 软件工程师 + Designer
    • Designer, 直译就是设计师,负责用户界面和交互的设计
    • 通常情况下,PM和软件工程师是长期合作的,而Designer有自己的独立组织,与团队是临时合作
  • 软件工程 = 团队 + 过程定义 + 执行
  • 软件 = 程序 + 软件工程
  • 软件公司 = 软件产品 + 商业模式

商业模式的三个概念:产品, 项目, 服务

  • 产品,其特点由公司决定要做什么,给客户提供什么,具有长期规划,不断迭代

  • 项目,包括两类:
    • 一种是狭义的,在销售给客户产品后,再同时开发/提供一些定制软件,那么会有配套的开发人员进行售后服务
    • 另一种是广义的,其宿主是服务或产品,完成其一个小功能
  • 服务,以云端服务为主,具有战略意义,这种服务的后台通常有超级复杂的架构体系支撑,在性能,可用性,可靠性上下足了功夫,高并发大容量