Skip to content

ProgramThinking

kcp edited this page Oct 13, 2020 · 3 revisions

title: ProgramThinking date: 2018-11-21 10:56:52 tags: categories: - Engineering

目录 start

  1. 开发思想
    1. 抽象
    2. 编程范式
    3. 编程思想
      1. 面向过程
      2. 面向对象
        1. OOP
      3. 面向过程和面向对象的对比
    4. DDD 领域驱动设计
      1. 聚合
      2. 参考实践项目
    5. 数据的操作
      1. CURD
      2. CQRS
    6. 组件模型
      1. SOA
      2. MSA
    7. Other
      1. 国际化的配置
  2. 设计软件的方法
    1. 契约式设计
    2. 精益思想
  3. 编程习惯
    1. 晓风轻的经验
      1. 接口定义
      2. 日志建议
      3. 异常处理
      4. 工具类规范
    2. 代码质量分析
      1. Checkstyle
      2. FindBugs
      3. 阿里巴巴的代码检查
    3. 配置文件
    4. 软件版本
  4. 编程语言
    1. 类型之分
      1. 解释型和编译型
      2. 强类型和弱类型
      3. 动态和静态类型

目录 end|2020-04-27 23:42|


开发思想

有关开发的理论性思想,编写,测试,部署等

抽象

稍微注意一下就会发现: 抽象层次越高,接口的语意就越模糊,适用的范围就越广,到最后就会变成数学模型或者概念。
但是抽象成数学模型和算法通常是可遇而不可求的, 这种情况下,我们需要退而求其次,试图抽象成若干个正交的概念,来降低复杂度。
你在处理x轴相关的事情时,不用考虑其他的y和z 相关的东西,因为你知道他们不会受到影响, 这样问题的复杂度就从3维一下子下降到1维!更容易把握了。
如果你说了,我的整个系统还没法抽象成正交的概念, 那只好再退一步,在局部使用接口。
其实 一组定义良好的接口一定是正交的,不然的话接口之间的依赖就会让实现非常麻烦。

在著名的《设计模式》一书中,其实在反复强调一点: 发现变化并且封装变化,针对接口编程而不是实现编程。 很多人看书是只关注具体的模式,而忽略了模式的本质目的。

抽象能力的高低,很大程度上反映了一个程序员的能力的高低

  • 计算机科学中抽象的好处与问题—伪共享实例分析计算机科学中的任何问题都可以通过加上一层间接层来解决,这是很正确的,但是也正是因为一层一层的抽象和包装,导致出了问题后很难定位,你都不知道问题究竟是出现在哪一层。所以要想提高技术水平不仅要知其然(看得见最顶层的包装)也要知其所以然(看得见底层的包装),每一层如果都懂或者说了解一些,那么出了问题很大程度上都可以凭直觉定位,即使不能凭直觉也可以通过各种手段debug,只会最顶层的抽象很多时候就只能望bug兴叹了。

编程范式

编程范式详情


编程思想

面向过程

只有数据和函数, 使用函数改变数据状态

一种以过程为中心的编程思想

面向对象

OO Object Oriented

参考: 再见面向对象编程?

思考:

  • 遇到需求时, 先分析需要哪些独立的实体, 然后分析用户的行为, 行为就是API
  • 实体的基本属性和行为确定好, 并且确定好各自的生命周期(一般是属性, 状态的变化)
  • 并且要注意设计时要尽量解耦, 即使需求上是和时间, 天气, 等一些外部状态影响的, 但是也应该在此之上抽象, 解耦, 方便测试和开发
  • 例如 活动 具有 开始时间和结束时间 的需求, 如果只用时间去设计行为的话, 测试的时候就需要去模拟那些时间, 如果引入状态这个属性(开启,关闭)
  • 就可以方便的调试了, 只需更改这个状态即可控制 活动 这个对象的行为, 当然, 变化的时间也是通过控制 状态 来控制 活动 的
  • 面向对象被广泛应用于大型项目, 但是大型项目也不应只有这一种设计思想
  • 编写出完成需求的代码不难, 难的是写出, 优雅, 简洁, 设计良好, 可读性, 扩展性高的代码

OOP

维基 OOP | 中文版

面向过程和面向对象的对比

示例 把大象塞进冰箱:

面向过程, 冰箱开门() 冰箱装进( 大象) 冰箱关门() 面向对象 冰箱.开门().装入(大象).关门()

  • 面向过程
    • 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
    • 缺点:没有面向对象易维护、易复用、易扩展
  • 面向对象
    • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
    • 缺点:性能比面向过程低

DDD 领域驱动设计

领域驱动设计(DDD:Domain-Driven Design)入门贴 领域驱动设计:软件核心复杂性应对之道

领域驱动设计 软件核心复杂性应对之道 Eric J. Evans 在线阅读 领域驱动设计精简版

参考博客 | 讨论 | 基础

参考: 危险的DDD聚合根 初步感受是DDD禁不起变化, 必须要在起初就设计好一个完备的体系 参考: DDD应用的思考提出了关于领域设计的困惑

聚合

聚合根的修改行为应该属于聚合根实体对象自己,用聚合根行为守护其内部状态的一致性是DDD设计核心,如果聚合根内部的状态直接暴露给外界(通过领域服务)任意修改,那么会导致状态变化混乱,难以调试和跟踪。

  • 现在书写的这个项目就和这个理念相一致, 但是总说是OOP 没有提及DDD TODO
    • 整个系统中涉及到的实体对象, 需要持久化的属就独立出来作为一个PO对象, 然后Spring Data JPA 接管DAO操作
    • 然后在对象中建立 修改PO对象行为 的方法, 而不是以往 MVC 那样的设计, 业务全在Service里面, 对实体自身属性的基本操作也在Service里面

参考实践项目

enodeC#实现 CQRS


数据的操作

CURD

CQRS

www.cqrs.nuCQRS Guides
event-sourcing| 中文版 微软关于azure的技术性文档

event-sourcing-in-practice 参考: CQRS & Event Sourcing
参考: 领域驱动设计的实践 – CQRS & Event Sourcing 图文并茂的讲解CQRS思想

eventapisJava实现的CQRS CQRS journey 微软团队的项目


组件模型

SOA

参考: SOA面向服务架构

Spring Web 应用的最大败笔

  • 传统意义上的SOA 内部封装的是数据表的DTO 也被称为 失血模型,贫血模型, 从而导致SOA服务内部腐烂堵塞,违背SOA自治和可用性等原则约束
    • 我现在使用Java的SpringMVC进行开发的东西, MVC架构, 然后JavaBean, Dao层或者JPA的Repository, Service层, Controller层, 而且还使用了好几年了
    1. Web层负责处理用户输入,并返回正确的响应返回给用户。 web层与服务层通信。
    2. 服务层作为一个事务边界。它也负责授权和包含我们的应用程序的业务逻辑。服务层管理的域模型对象,并与其他服务和存储库层进行通信。
    3. 存储库/数据访问层负责与所使用的数据的存储进行通信。
    • 正如这个毕业设计的项目 Graduate, 显然的都具有如上提到的各种缺陷,
    • 每一个 DTO 只具有属性, 而没有方法, 一个DTO就要对应一个服务, 服务之间再相互注入, 就会有很有依赖, 甚至循环依赖

MSA

微服务

 参考: SOA 与 MSA(微服务架构) 码农翻身:从SOA到微服务

微服务 MSA

Other

国际化的配置

  • 将配置文件按语言分别配置
  • 然后在加载时设定语言的配置, 然后加载对应文件夹下的配置文件

设计软件的方法

契约式设计

Design by Contract (Dbc)

百度百科解释

精益思想

持续集成、持续交付、持续部署


编程习惯

晓风轻的经验

接口定义

  • 先有统一的接口定义规范,然后有AOP实现。先有思想再有技术。
  • 现在知道为什么要返回统一的一个ResultBean了:
    • 为了统一格式
    • 为了应用AOP
    • 为了包装异常信息

日志建议

异常处理

  • 所以,我对开发人员的要求就是,绝大部分场景,不允许捕获异常,不要乱加空判断。只有明显不需要关心的异常,如关闭资源的时候的io异常,可以捕获然后什么都不干,其他时候,不允许捕获异常,都抛出去,到controller处理。空判断大部分时候不需要,你如果写了空判断,你就必须测试为空和不为空二种场景,要么就不要写空判断。
  • 强调,有些空判断是要的,如:参数是用户输入的情况下。但是,大部分场景是不需要的(我们的IT系统里面,一半以上不需要),如参数是其它系统传过来,或者其他地方获取的传过来的,99.99%都不会为空,你判断来干嘛?就抛一个空指针到前台怎么啦?何况基本上不会出现。
  • 总结:
    • 开发组长定义好异常,异常继承RuntimeException。
    • 不允许开发人员捕获异常。(异常上对开发人员就这点要求!异常都抛出到controller上用AOP处理)
    • 后台(如队列等)异常一定要有通知机制,要第一时间知道异常。
    • 少加空判断,加了空判断就要测试为空的场景!

工具类规范

  1. 方法参数要抽象(尽量往上找到父类和接口), 返回值要具体
  2. 隐藏实现: 不要在业务代码中直接调用三方工具, 应该自己写一个类, 然后调用三方库的方法, 方便以后修改
  3. 多使用重载编写功能齐全的对外接口和方法
  4. 单独存放, 单独维护,

优先使用组合而不是继承, 继承破坏了封装性, 因为父类的很多细节对子类是可见的, 父类的变化可能极大的影响子类
面向接口编程, 而不是实现编程

代码质量分析

  • 测试对代码的覆盖率
  • 代码的格式是否清晰,有助于差异比较和可读性
  • 是否很可能会出现NPE
  • 是否忘记了域对象中的equals和hashCode方法

Checkstyle

FindBugs

阿里巴巴的代码检查


配置文件

千万业务代码里面不要和读取配置的代码耦合在一起。切记!


  1. 优秀工程师所具备的能力:
    • 将现实问题转换为计算机问题的数学能力
    • 对于语言本身的工具链的熟悉程度(最简单)
    • 计算机体系知识的掌握能力
    • 对所有经验进行形而上的能力

软件版本

语义化版本 SemVer


编程语言

类型之分

弱类型、强类型、动态类型、静态类型语言的区别是什么?

解释型和编译型

  • 在 80 90 年代,边界较为清晰,类C语言是编译型,Perl和Python是解释型。但Java是两者都有
  • 基于JVM来划分的边界是:该语言是否将源码编译为类文件并且执行,不产生类文件的语言会由解释器逐行执行。有些语言既有编译器又有解释器,有些是既有解释器又有产生字节码的即时编译器JIT

强类型和弱类型

  • 强类型 偏向于不容忍隐式类型转换。
    • 例如Python中int就不能直接转为string,Java同理 但是Java有(编译期)语法糖会在需要的时候隐式添加toString
  • 弱类型 偏向于容忍隐式类型转换。
    • 例如C语言中 int 和 char 可以互换

动态和静态类型

  • 动态类型语言,变量在不同的时间可能会有不同的类型 动态类型语言是跟踪变量的值的类型信息,静态类型语言是跟踪变量的类型信息
  • 静态类型 在编译期就确定下变量的类型,因为类型错误而无法做的事情是语法错误
  • 动态类型 在运行期才能确定变量的类型,编译期不能得以确定,因为类型错误而无法做的事情是运行时错误

Summary

Clone this wiki locally