• Click to hide sidebar Click to show sidebar
  • Click to hide sidebar Click to show sidebar
  • 现代软件开发方法复习

    Revision of Contemporary software development

    现代软件开发方法

    SaaS

    • SaaS通过运行在客户端设备上的瘦程序(如:浏览器),访问在Internet上以服务形式提供的软件和数据
    1. 无需安装,不用担心硬件能力,OS
    2. 无需担心数据丢失(数据远程存储)
    3. 便于团队与相同数据的交互
    4. 如果数据很大或频繁更改,在中心站点保存一份副本更简单
    5. 软件单一拷贝,单一的硬件/OS环境
      • 开发人员没有兼容性问题
      • 只需对1%的用户beta测试新功能
    6. 单一拷贝=>简化了开发人员的升级任务
    7. 持续获得用户的反馈成为可能

    对基础设施需求:通讯、可扩展性和弹性、可靠性

    错误:

    • 如果使用相同类型的硬件和软件,私有数据中心的成本 可以与仓库规模的计算机相媲美

    • 经典的软件体系结构模式/风格

      • 分层架构
      • 主从架构
      • 管道和过滤器架构
      • 点对点架构
      • 事件总线架构
      • 模型-视图-控制器架构
    • 新的适应云的特点的微服务架构

    • 不同的软件体系结构之间并不互斥,可以在同一个软件系统中结合使用

    Ajax: web2.0 Http response 包含data,浏览器将response data 交由一个特别的JS函数处理,JS函数可以使用数据修改页面上展示的内容

    • Web是独立、可组合的服务集合:Service oriented architecture (SOA)

    SOA: 所有组件都设计为服务,以及服务可以组合的软件架构

    API需要指定:

    • Caller如何识别并定位被调用的函数
    • 如何传递必须的/可选的参数
    • Caller如何接收返回值
    • Caller端出现错误时如何应对?

    三层架构:

    (a) 表⽰层, (b) 逻辑层, (c) 持久层

    Web上的SaaS应用中,控制器动作和视图内容使用 HTTP传输

    不正确的说法是:

    • 所有MVC应用都有一个“客户端”部分(如Web浏览器) 和一个“云”部分(如云上的Rails应用程序)。

    Active Record: 每个模型都知道如何使用通用机制进行CRUD操作 ActiveRecord 是一种Object Relational Mapping (ORM)的实现

    • 适用于业务逻辑比较简单,类基本上和数据库中的表一一对应;
    • 当发生跨表的操作时, 往往会配合使用事务脚本,把跨表事务提升到事务脚本中;
    • 简单, 直观。 一个类就包括了数据访问和业务逻辑

    缺点:领域类和表一对一匹配,随着把领域逻辑放入更小的类而失效;关系数据库无法处理继承,因此使用策略模式等面向对象模式非常困难

    DataMapper将单独的映射器与每个模型关联起来,可以使用关系型数据库

    js创建对象要用new

    ajax:在不重新加载页面的情况下与服务器异步地通信,如输入提示

    需求分析

    BDD行为驱动设计

    需求以用户故事记录

    SMART故事:

    • Specific–明确
    • Measurable-可度量
    • Achievable–可达成 (理想情况下,在一个迭 代中实现)
    • Relevant–相关性 (“5个为什么”)
    • Timeboxed-时间框 (知道什么时候放弃)

    SM:每个场景可测试

    低保真(Lo-Fi)

    将用户故事划分为更简单的故事, 组成史诗(epic)

    Cucumber 小结

    • 新特征=>特征的UI,编写新的步骤定义,甚至在Cucumber能使步骤变绿之前就写新方法
    • 通常先让成功路径通过
    • Background可以让同一特征的场景更加精炼
    • BDD/Cucumber测试系统实现后的行为;

    P&D中的三明治集成旨在减少构造存根-stub的工作量,同时尝试尽早获得通用性功能

    • 经理将SRS划分为编程单元

    • 开发人员编写单元代码

    • 开发人员执行单元测试

    • 独立的质量保证(QA)团队进行更高 级别的测试: – 模块、集成、系统、验收测试

    • 自顶向下集成:构建存根让应用运转

    • 自底向上:直到所有代码编写和集成完成才能运转

    • 三明治

    形式化方法从形式规格说明开始,并证明程序行为符 合规格说明。

    软件维护

    code review

    正式到非正式 桌面检查、代码走查、代码审查

    缺陷检查表:

    • 编程规范
    • OOP
    • 性能
    • 资源释放处理
    • 程序流程
    • 线程安全
    • 数据库处理
    • 通讯
    • 对象处理
    • 异常处理
    • 方法
    • 安全
    • 其他(日志、配置)

    软件维护可以分为纠错性维护、适应性维护、改善性 维护和预防性维护四类

    对软件可维护性影响的主要因素有:

    可理解性(understandability) 可测试性(testability) 可修改性(modifiability) 可移植性(portability)

    代码异味(Code Smell)

    SOFA通常捕获那些表示代码异味的症状:

    • 它是短的吗?(Short)
    • 它只做一件事吗? (One)
    • 它有不多的参数吗?(Few)
    • 它有一致的抽象级别吗?(Abstraction)

    最重要做一件事(Do One thing)

    方法级重构的主要目标是提高代码的结构质量、可读性和可维护性,包括减少复杂性、消除代码异味以及提高可测试性。消除错误通常是通过测试和调试来实现,而不是通过重构来解决。

    发散式变化指的是“一个类受多个外界变化的影响”,其基本思想是把相对 不变的和相对变化相隔离,即封装变化。

    霰弹式修改指的是“一种变化引发多个类的修改”,其基本思想是将变化率 和变化内容相似的状态和行为放在同一个类中,即集中变化。

    数据泥潭:

    • 同样的两至三项数据频繁地一起出现在类和参数表中。
    • 代码声明了某些字段,并声明了处理这些字段的方法,然后又声明了更多的字段和更多的方法,如此继续。
    • 各组字段名以类似的子串开头或结束。

    ppt 11有重构例子

    • 比较 P&D 和 Scrum,P&D 项目经理同时担任 Scrum Master 和产品负责人

      • 这是不正确的。在传统的 P&D(计划驱动)方法中,没有 Scrum Master 和产品负责人的角色。通常只有一个项目经理负责监督项目。
    • 团队应尽一切可能避免团队成员之间的冲突

      • 这不一定正确。虽然管理冲突是重要的,但避免所有冲突可能会导致问题得不到解决,阻碍团队进展。
    • P&D 可以拥有比 Scrum 更大的团队,团队成员直接向项目经理汇报

      • 这是正确的。传统的 P&D 方法通常涉及更大的团队,具有层级结构,团队成员直接向项目经理汇报。
    • 由于研究表明 84%-90% 的项目按时并在预算内完成,P&D 经理可以自信地向客户承诺在约定的成本和日期内交付一组功能

      • 这是不正确的。尽管有研究,但许多项目仍然面临按时和按预算完成的挑战。过度承诺可能会导致不切实际的期望和潜在的项目失败。
    • Intended to improve the quality of the software product using the wisdom of the attendees

      • 这是正确的。会议和评审通常旨在利用与会者的集体智慧来提高软件产品的质量。
    • They result in technical information exchange and can be highly educational for junior people

      • 这是正确的。这样的会议可以促进技术信息的交流,并为初级团队成员提供学习机会。
    • Can be beneficial to both presenters and attendees

      • 这是正确的。演示者和与会者都可以获得见解和反馈,使整个过程对所有人都有益。
    • The A’s in SAMOSA stands for Agenda and Action item, which are optional pieces of good meetings

      • 这是错误的。根据 SAMOSAS 的指南,议程和行动项并不是可选的,而是必需的。议程需要提前创建,没有议程就不开会;会议结束时需要明确行动项,以便每个人都知道会议的结果和接下来的任务。

    DevOps

    服务水平目标(Service Level Objectives,SLO)

    缓存整个动作的输出 ‒ 页面缓存:绕过(旁路)控制器动作 - caches_page :index ‒ 动作缓存:会调用控制器,运行过滤器

    • 视图的片段缓存:缓存由渲染页面的一部分(例如.partial)产生的HTML

    SSL能做什么,不能做什么

    • 向浏览器确保bob.com是合法的
    • 防止窃听者读(或破坏)浏览器和bob.com之间流量
    • 给服务器增加了额外的工作

    不能:

    ‒ 向服务器确定用户是谁 ‒ 说明敏感数据到达服务器后发生了什么 ‒ 说明服务器是否容易受到其他服务器的攻击 ‒ 如果服务器上存在恶意软件,保护浏览器免受破坏

    设计模式

    单一职责原则(SRP):一个类只负责一个功能或职责

    模板方法模式&策略模式

    • 模板方法:一组步骤相同,但是步骤实现不同
      • 继承: 子类重载了抽象的“步骤”方法
    • 策略: 任务是一样的,但是有很多途径去实现
      • 组合: 组件类实现整个任务

    OmniAuth是策略模式的一个示例

    里氏替换原则有至少有两种含义

    1. 里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。
    2. 如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合LSP,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。

    子类方法不能违背父类方法对输入输出异常的约定

    1. 前置条件不能被加强

    前置条件即输入参数是不能被加强的,就像上面Cache的示例,Redis子类对输入参数Key的要求进行了加强,此时在调用处替换父类对象为子类对象就可能引发异常。

    也就是说,子类对输入的数据的校验比父类更加严格,那子类的设计就违背了里式替换原则。

    1. 后置条件不能被削弱

    后置条件即输出,假设我们的父类方法约定输出参数要大于0,调用父类方法的程序根据约定对输出参数进行了大于0的验证。而子类在实现的时候却输出了小于等于0的值。此时子类的涉及就违背了里氏替换原则

    1. 不能违背对异常的约定

    在父类中,某个函数约定,只会抛出 ArgumentNullException 异常, 那子类的设计实现中只允许抛出 ArgumentNullException 异常,任何其他异常的抛出,都会导致子类违背里式替换原则。

    子类方法不能违背父类方法定义的功能

    子类必须完全实现父类的抽象方法

    依赖注入

    通过将依赖关系的创建和维护责任转移到外部容器中,使得类不需要自己实例化依赖对象,而是由外部容器动态地注入依赖。

    Languages

    Ruby

    • 局部变量(Local variables):在 Ruby 中,局部变量以小写字母或下划线 _ 开头,而不是以 $ 开头。以 $ 开头的是全局变量(Global variables)。
    • 实例变量(Instance variables):实例变量以 @ 开头。这些变量是对象实例专有的,不能被其他对象访问。
    • 变量(Class variables):类变量以 @@ 开头。这些变量在类的所有实例之间共享,属于类而不是某个具体的对象实例。

    Questions

    根据依赖注入原则(Dependency Injection Principle, DIP),我们应该将高层模块依赖于抽象而非具体实现,以提高代码的测试性、可维护性和模块化程度。

    行为模式:责任链、命令、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者

    • lambda 是 Proc 对象的特殊类型,但它不是 Proc 对象。
    • proc 和 lambda 都会检查参数数量,如果参数数量不匹配会抛出错误。
    • proc 和 lambda 对 return 关键字的含义不同。在 lambda 中,return 会从 lambda 中返回,而在 proc 中,return 会从包含 proc 的方法中返回,而不是从 proc 本身返回。