精华内容
下载资源
问答
  • 敏捷软件开发原则

    2019-10-06 05:40:18
    敏捷软件开发原则----《敏捷软件开发原则、模式与实践》学习笔记 最近在系统地学习并且有意地在工作中实践敏捷软件开发,文章乍看起来,都是一些说教性、理论性,比较无聊的东西。  但是如果静下心来结合自己...
      敏捷软件开发原则 ----《敏捷软件开发原则、模式与实践》学习笔记
     
          最近在系统地学习并且有意地在工作中实践敏捷软件开发,文章乍看起来,都是一些说教性、理论性,比较无聊的东西。
      但是如果静下心来结合自己自身的经历、思考地去阅读,可能会发现,有的观点确实很赞同,然而有的可能会有自己的想法。
      以下是在《敏捷软件开发 原则、模式与实践》一些读书笔记,斜体字是直接摘录于书本,非斜体字是自己的一些理解。
     
    一、尽早的,持续地交互有价值的软件来使客户满意。初期交付的系统功能越少,最终交付的系统的质量越好。逐渐增加功能的方式,经常地交付系统和最终质量之间有非常强的相关性。交付得越频繁,最终产品的质量越高。
    关于交付对象,这里指的交付给客户,我们能做的不一定是交付给客户,还可以是提交给上级、开发、产品、测试同事看一下,看是否有不合理的地方,可以及时更正。如果等到差不多完了才交付,可能出现了问题也不好改了。
    关于交付的时间点,可以是软件有一部分完整可以展示的功能即可。
     
    二、欢迎需求的变化,即使到了开发后期,敏捷过程能够驾驭变化,为客户创造竞争力。
    其实欢迎需求变化,个人觉得只是一种无奈、坦然的说法,有谁闲着没事希望天天改需求丫。这里的意思只是说,开发软件的时候,要努力的保持软件结构的灵活性,当需求有变化的时候,让系统改动最小化。
     
    三、经常地交付可以工作的软件,从几个星期到几个月,时间越短越好,不赞成交付大量的文档与计划,我们关注的目标是交付满足顾客使用的软件。
    这个和第一点是类似的,尽早、经常交互,尽早发现各自对需求理解的差异、集思广益地提出对系统改进的意见。之前在一个公司,开发提交代码到SVN,每天服务器会自动把提交的代码自动编译到开发环境,这样就可以很方便地看到最新的系统。好的工具和流程,对推动交付也是很有帮助的。
     
    四、在整个项目开发期间,开发人员和业务人员必须朝夕在一起工作。客户、开发人员、利益的相关者,必须进行有意义的,频繁的交互。软件项目不像发射出去了,就能够自动导航的武器。必须对软件项目持续不断地进行导航。
     
    五、依靠斗志高昂的人构建项目。给他们提供所需要的环境和支持,并相信他们能够完成任务。人是项目取得成功的重要因素,其他因素(过程、环境、管理等)都被认为是次要的,当他们对人有负面影响的时候,就要对他们进行改变。
    对于这点的理解,我很认同强调人的影响,看重人的作用。过程、环境、管理,并不是没有用,也不是随便被抛弃,而是,不能够一成不变,要根据人的正确反馈,不断地被改进。因为,一定的规范、管理、约束并不是一开始就是最完善的,而且也不适用于所有的场景和项目。
     
    六、在团队内部,最有效率,也是最有效果的信息传达方式,就是面对面交谈。书面文档会按照和软件一样的时间来编写和更新,但是仅在需要的时候才这样做。
     
    七、可以工作的软件是进度的主要度量标准。仅当30%的功能可以工作时,才确定完成了进度的30%。
     
    八、敏捷过程提倡可持续地开发。出资人、开发者和用户应该总是保持稳定的开发速度。敏捷项目不是50米短跑而是马拉松长跑。团队不是全速启动,并且在开发的时候保持这个速度,相反,他们以快速,但是可持续的速度进行。
    项目开发前期,就死命加班,可能坚持不了几天就身心疲惫,这样的话对项目也不好,这样到后期可能变得很松散。不过在为了完成阶段性目标,做阶段性冲刺还是可以的。
     
    九、对卓越的技术和良好设计的不断追求,有助于提高软件的敏捷性。高的产品质量是获得高的开发速度的关键。不要制造了混乱后,对自己说,等有更多的时间的时候再来清理它。今天制造了混乱就必须今天清理干净。
    对于这点的理解,主要有两方面:
    第一、对于,花不是很长时间就能改进的东西,不要想着下次有时间再改,因为,一方面,要相信程序员的时间永远都不够用,另一方面,下次来改,可能会生疏了,花费的成本会更大。
    第二、快就是慢。不要为了赶进度,而忽略了产品质量,连自己的那关也过不了。否则和测试人员的反复交互,反而会使得效率更低,最终只是眼前快而已,进度最终反而变得更慢。
     
    十、简单,尽量减少工作量的艺术是相关重要的。敏捷团队不会去构建那些华而不实的系统,他们总是更愿意采用跟目标一致的最简单的方法。
    这里说的有三个层面的意思,第一,是投入与产出的问题。不要花很大功夫去开发一些对于实际应用没多大意义的功能;第二,是要多思考,看看有无更简单的方法实现需求;第三,对于系统不要过度设计。
     
    十一、最好的架构、需求和设计都源于自我组织的团队。敏捷团队都是自我组织的团队,责任不是从外部分配给某个团队成员,而是分配给某个团队,然后由团队来确定履行职责的最好方法。每个成员都能够共同解决项目中涉及各个方面的问题。
    在实际项目中,具体的工作可能由团队不同的人来负责。虽然,即使是某一项工作不是由自己负责,但是自己有责任去提供帮助和提出意见。
     
    十二、每隔一段时间,团队都要总结如何更有效率地完成工作,然后相应调整自己的行为。敏捷团队应该知道所处的环境是不断变化的,而自己也应该随着环境一起变化。
     
     
    以上十二个原则,有一个大的原则,就是强调人、团队和沟通的重要性。在日常的工作中,其实可能都在有意或无意地实践着这些原则。
     
    在往后的工作中,将会有计划地实践一下以上十二个原则, 并且将实践过程、效果记录下来。
     
    如果有人也有兴趣一起践行这些原则,欢迎一起督促、交流、探讨。
     

    转载于:https://www.cnblogs.com/still-windows7/p/PrincipleOfAgileDev.html

    展开全文
  • 敏捷软件开发 原则 模式 有目录书签,PDF格式,bob大叔的经典作品。
  • 敏捷软件开发原则模式与实践 c++
  • 敏捷软件开发原则、模式与实践.rar Uncle Bob的名著,敏捷的经典名著,这本书比较特别,与其说是讲软件开发过程的书,不如说讲软件架构的书,本书用了很大篇幅讲各种面向对象软件开发的各种模式,个人以为看了这本书...
  • 本节书摘来华章计算机《软件工艺师:专业、务实、自豪》...2.2 面向技术的敏捷软件开发原则 这些原则主要与开发软件、完善软件设定、维护软件及交付软件时遇到的挑战有关。它们通常针对技术实践与技术本身设定规范...

    本节书摘来华章计算机《软件工艺师:专业、务实、自豪》一书中的第2章 ,第2.2节,[英]桑德罗·曼卡索(Sandro Mancuso)著 爱飞翔 译, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

    2.2 面向技术的敏捷软件开发原则

    这些原则主要与开发软件、完善软件设定、维护软件及交付软件时遇到的挑战有关。它们通常针对技术实践与技术本身设定规范或给出建议。举例来说,这些实践方式与技术包括:测试驱动开发、结对编程、持续集成、简洁设计等。面向技术的敏捷原则使团队专注于软件产品的质量,并帮助确保开发出来的软件品质优良(building the thing right,把事情做对)。

    展开全文
  • 敏捷软件开发原则,模式与实践书摘。 拙劣设计的症状: 僵化性(Rigidity):很难对系统进行改动,因为每个改动都会迫使许多对系统其它部分的其它改动脆弱性(Fragility):对系统的改动会导致系统中和改动的地方在...

    敏捷软件开发原则,模式与实践书摘。

    拙劣设计的症状:

    • 僵化性(Rigidity):很难对系统进行改动,因为每个改动都会迫使许多对系统其它部分的其它改动
    • 脆弱性(Fragility):对系统的改动会导致系统中和改动的地方在概念上无关的许多地方出现问题
    • 牢固性(Immobility):很难解开系统的纠结,使之成为一些可在其他系统中重用的组件
    • 粘滞性(Viscosity):做正确的事情比做错误的事情要困难
    • 不必要的复杂性(Needless Complexity):设计中包含不具任何直接好处的基础结构
    • 不必要的重复(Needless Repetition):设计中包含有重复的结构,而该重复的结构本可以使用单一的抽象进行统一
    • 晦涩性(Opacity):很难阅读,理解。没有很好地表现出意图

    面向对象设计原则:

    • 单一职责原则(The Single Responsibility Principle, 简称SRP)
      • 就一个类而言,应该仅有一个引起它变化的原因(一个类一个职责)
      • 例子1:有两个不同的应用程序使用Rectangle类。一个有关计算几何学方面的,Rectangle类会在几何形状计算方面为它提供帮助,它从来不会在屏幕上绘制矩形。另外一个应用程序实质上是有关图形绘制方面的,它可能也会进行一些计算几何方面的工作,但是它肯定在屏幕上绘制矩形。


                                                                      例子1:图示

      • 例子2:Modem接口显示出两个职责。第一个职责是连接管理;第二个职责是数据通信。dial和hangup函数进行调制解调器的连接处理,而send和recv函数进行数据通信
        interface Modem
        {
          public void dial(String pno);
          public void hangup();
          public void sned(char c):
          public void recv();
        }
        这两个职责应该被分开吗?这依赖于应用程序的变化方式。如果应用程序的变化会影响连接函数的签名(signature),那么这个设计就具有僵化的臭味,因为调用send和recv的类必须重新编译。在这种情况下,这两个职责应该被分离,如下图所示。
        另一方面,如果应用程序的变化方式总是导致这两个职责同时变化,那么就不应该分离它们。实际上,分离它们就会具有不必要的复杂性臭味

                                                  例子2:图示
      • 例子3:下图展示了一种常见的违反SRP的情形。Employee类包含了业务规则和对于持久化的控制。这两个职责在大多数情况下绝不应该混合在一起。业务规则往往会频繁的变化,而持久化的方式却不会如此频繁的变化,并且变化的原因也是完全不同的。把业务规则和持久化子系统绑定在一起的做法是自讨苦吃。

                                            例子3:图示
    • 开放——封闭原则(The Open-Close Pinciple, 简称OCP)
      • 软件实体(类,模块,函数等等)应该是可以扩展的,但是不可修改的。如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味。OCP建议我们应该对系统进行重构,这样以后对系统再进行那样的改动时,就不会导致更多的修改。如果正确地应用OCP,那么以后再进行同样的改动时,就只需要添加新的代码,而不必改动已经正常运行的代码。
        遵循open-close原则设计的模块具有两个主要的特征。它们是:
        1. “对于扩展是开放的”(Open for extension)
          这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,是其具有满足那些改变的新行为。换句话说,我们可以改变模块的功能。
        2. “对于更改时封闭的”(Close for modification)
          对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接库,DLL或Java的.jar文件,都无需改动。
        3. 如何做到以上两点,关键是“抽象”。模块应该选择依赖于抽象,而不是实现。模块依赖于一个抽象体,所以它对于更改时关闭的。同时,通过从这个抽象体派生,也可以扩展此模块的行为。但请注意,in reality,模块很难做到100%的封闭,一般都会存在一些无法对其封闭的变化(没有对所有情况都贴切的模型)。此时就必须有策略地对待这个问题。也就是说设计人员必须对于他设计的模块应该对哪些变化封闭作出选择。他必须先猜测出最有可能发生的变化种类(HOW? 测试驱动或许是个好的开始),然后构造抽象来隔离那些变化(记住,抽象是为了哪些易变的东西(用抽象隔离变化))。另外一种经常使用的方法是使用hook(C语言常用, 至少对我来说是这样的)技术,在我们认为可能发生变化的地方install hook。然而我们放置的hook常常是错误的。更糟糕的是,即使不使用这些hook,也必须去支持和维护它们(这点确实是深有体会),从而就具有了不必要的复杂性味道。这不是什么好事,因为我们不希望设计背着许多不必要的抽象。通常,我们更愿意一直等到确实需要那些抽象时再把它放置进去。
    • Liskov替换原则(The Liskov Subsitution Principle, 简称LSP)
      • 字类型(subtype)必须能够替换掉它们的基类型(base type)。
      • 什么时候知道我们自己违反了LSP?
        1. 假设有一个函数f,它的参数为指向某个基类B的指针或引用。假设有B的某个派生类D,如果把D的对象作为B类型传递给f,会导致f出现错误的行为。那么D就违反了LSP。显然,D对于f来说是脆弱的。
        2. 如果我们code里面显示地使用RTTI方式去确定一个对象的类型,然后用if/else做dispatch(有一部分code必须知道所有可能的类型,将来新增类型是,这处code也必须modify),这有可能是一个明显违反OCP的例子(Double dispatching有可能解决这种问题)。
        3. 如果我们试图写一个以某种方式从其基类中去除功能的派生类(完成的功能少于其基类的派生类通常是不能替换其基类的),这暗示这我们在违反LSP。
        4. 如果我们试图在派生类的方法中添加其基类不会抛出的异常,那么我们正在违反LSP。
      • 例子1:有一个third party的类库,包含有一些容器类,这些容器有两个Set类的变体。第一个变体是“有限的(bounded)”,基于数组实现。第二个变体是“无限的”,基于链表实现。不希望自己的程序代码依赖于这些容器类,因为以后会用更好的来替换它们。因此,可以把它们包装在自己的抽象接口之下。如下图1所示。问题:如果在该层次中加入PersistentSet,但是这个类不是模板类,只接受虚基类PersistentObject的派生对象,于是创建了图2所示的类层次。注意,PersitentSet包含了一个ThirdParty 持久性集合的实例,它把它的所有方法都delegate给该实例。这样,如果调用了PersistentSet的Add方法,它就简单地把该调用委托给ThirdParty的持久性集合中包含的方法。表面看起来,好像没有什么问题。其实隐藏着一个别扭的设计问题。加入到ThirdParty持久性集合中的元素必须得从PersistentObject派生。由于PersistentSet只是把调用委托给ThirdParty持久性集合,所以任何要加入PersistentSet的元素也必须得从PersistentObject派生。可以,Set接口没有这样的限制。从程序代码示例可以看出,任何客户企图向PersistentSet中添加不是从PersistentObject派生的对象,将会发生运行时错误(这个例子也间接地暗示我们,使用了dynamic_cast的地方或多或少的可能存在设计缺陷)。dynamic_cast会抛出bad_cast异常。但是抽象基类Set的所有现存的客户都不会预计到调用Add时抛出异常。由于Set的派生类会导致这些函数出现错误,所以对类层次的这种改动违反了LSP。

        • 解决方案1:通过约定(convention)的方式解决。约定不让PersistentSet和PersistentObject暴露给整个应用程序。它们只被一个特定的模块使用。该模块负责从持久性存储设备读出所有容器,也负责把所有容器写入到持久性存储设备。在写入容器时,该容器的内容先被复制到对应的PersistentObject的派生对象中,再加入到PersistentSets,然后存入流中。在从流中读入容器时,过程是相反的。先把信息从流读到PersistentSet中,再把PersistentObjects从PersistentSet中移出并复制到常规的(非持久化)对象中,然后再加入到常规的Set中。

        • 解决方案2:首先得承认PersistentSet和Set之间不是ISA的关系,它不应该派生自Set。因此可以分离这个层次结构,但不是完全的分离。Set和PersistentSet之间有一些公有的特性。事实上,仅仅是Add方法致使在LSP原则下出了问题。因此,可以创建一个层次结构,其中Set和PersistentSet是兄弟关系(siblings),统一在一个具有测试成员关系,遍历等操作的抽象接口之下,如例子1:图示3所示。这样可以对PersistentSet对象进行遍历以及测试成员关系等操作。但是它不能把不是派生自PersistentObject的对象加入到PersistentSet中。


                                                       例子1: 图示1

                                                           例子1:图示2
      程序示例:
      template<typename T>
      void PersistentSet::Add(const T& t)
      {
        PersistentObject& p = dynamic_cast<PersistentObject&>(t);
        itsThirdPartyPersistentSet.Add(p);
      }

                                                                       例子1:图示3

      • 例子2:Line和LineSegment的例子。如代码段1。最初看到这两个类时,会觉得它们之间有自然的公有继承关系。LineSegment需要Line中的每一个成员变量和成员函数。此外,LineSegment新增了一个自己的成员函数GetLength,并override了IsOn函数。但是这两个类还是以微妙的方式违反了LSP。Line的使用者可以期望和该Line具有线性对应关系的所有点都在该Line上。例如由Intercept函数返回的点就是线和y轴的交点。由于这个点和线具有线性对于关系,所以Line的使用这可以期望IsOn(Intercept()) == true。然而,对于许多LineSegment的实例,这条声明会失效。这为什么是一个重要的问题呢?为什么不简单地让LineSegment从Line派生并忍受这个微妙的问题呢?这是一个需要进行判断的问题。在大多数情况下,接受一个多态行为中的微妙错误都不会比试着修改设计使之完全符合LSP更为有利。接受缺陷而不是去追求完美这是一个工程上的权衡问题。好的工程师知道何时接受缺陷比追求完美更有利。不过,不应该轻易放弃对于LSP的遵循。总是保证子类可以代替它的基类是一个有效的管理复杂性的方法。一旦放弃了这一点,就必须要单独来考虑每个子类。

        • 解决方案:用提取公共部分的方法代替继承。有一个简单的方案可以解决Line和LineSegment的问题,该方案也阐明了一个OOD的重要工具。如果既要使用类Line又要使用类LineSegment,那么可以把这两个类的公共部分提取出来作为一个抽象基类。如代码2所示。

        • 提取公共部分是一个设计工具,最好在代码不是很多的时候应用。当然,如果Line已经存在很多clients,那么提取出LinearObject就不会这么轻松(绝对需啊测试套件的支持)。不过在有可能时,它仍然是一个有效的工具。
          #ifndef GEOMETRY_LINE_H
          #define GEOMETRY_LINE_H
          #include "geometry/point.h"

          class Line
          {
          public:
            Line(const Point& p1, const Point& p2);

            double GetSlope() const;
            double GetIntercept() const;
            double GetP1() const;
            double GetP2() const;
            virtual bool IsOn(const Point&) const;
           
          private:
            Point itsP1;
            Point itsP2;
          };

          #endif

          //

          #ifndef GEOMETRY_LINESEGMENT_H
          #define GEOMETRY_LINESEGMENT_H

          class LineSegment: public Line
          {
          public:
            LineSegment(const Point& p1, const Point& p2);
            double GetLength() const;
            virtual bool IsOn(const Point&) const;
          };

          #endif

          代码段1 - 有问题的代码

        • #ifndef GEOMETRY_LINEAR_OBJECT_H
          #define GEOMETRY_LINEAR_OBJECT_H
          #include "geometry/point.h"

          class LinearObject
          {
          public:
            LinearObject(const Point& p1, const Point& p2);

            double GetSlope() const;
            double GetIntercept() const;
            double GetP1() const;
            double GetP2() const;
            virtual bool IsOn(const Point&) const = 0; // Abstract
           
          private:
            Point itsP1;
            Point itsP2;
          };

          #endif

          #ifndef GEOMETRY_LINE_H
          #define GEOMETRY_LINE_H

          class LineSegment: public LinearObject
          {
          public:
            Line(const Point& p1, const Point& p2);
            virtual bool IsOn(const Point&) const;
          };

          #endif

          #ifndef GEOMETRY_LINESEGMENT_H
          #define GEOMETRY_LINESEGMENT_H

          class LineSegment: public LinearObject
          {
          public:
            LineSegment(const Point& p1, const Point& p2);
            double GetLength() const;
            virtual bool IsOn(const Point&) const;
          };

          #endif


          #ifndef GEOMETRY_RAY_H
          #define GEOMETRY_RAY_H

          class Ray: public LinearObject
          {
          public:
            Ray(const Point& p1, const Point& p2);
            virtual bool IsOn(const Point&) const;
          };

          #endif

          代码段2 - 重构后的代码
    • 依赖倒置原则(The Dependency Inversion Principle, 简称DIP)
      1. 高层模块不应该依赖于底层模块。二者都应该依赖于抽象。
      2. 抽象不应该依赖于细节。细节应该依赖于抽象。
      • 一个设计良好的面向对象程序,其依赖程序结构相对于传统的过程式方法设计的通常结构而言是被“倒置了。
              考虑一下当高层模块依赖于底层模块时意味着什么。高层模块包含了一个应用程序中的重要的策略选择和业务模型。正是这些高层模块才使得其所在的应用程序区别于其它。然而,如果这些高层模块依赖于底层模块,那么对底层模块的改动就会直接影响到高层模块,从而迫使它们依次做出改动。
              这种情形是非常荒谬的!本应该是高层的策略设置模块去影响底层的细节实现模块的。包含高层业务规则的模块应该优先并独立于包含实现细节的模块。无论如何高层模块不应该依赖于底层模块。
              此外,我们更希望能够重用的是高层的策略设置模块。我们已经非常擅长于通过子程序库的形式来重用底层模块。如果高层模块依赖于底层模块,那么在不同的上下文中重用高层模块就会变得非常困难。然而,如果高层模块独立于底层模块,那么高层模块就可以非常容易地被重用。该原则是framework设计的核心原则。
      • 下图1显示了一个简单的层次化设计。图中高层次的Policy Layer使用了低层的Mechanism Layer,而Mechanism Layer又使用了更细节的Utility Layer。这看起来似乎是正确的,然而它存在一个潜伏的错误特征,那就是:Policy Layer对于其下一直到Utility Layer的改动都是敏感的。这种依赖关系是传递的。Policy Layer依赖于某些依赖于Utility Layer的层次;因此Policy Layer传递性地依赖于Utility Layer。这是非常糟糕的。
        下图2展示了一个更为合适的模型。每个较高层次都为它所需要的服务声明一个抽象接口,较低层次实现了这些抽象接口,每个高层次类都通过该抽象接口使用下一层,这样高层就不依赖于低层。底层反而依赖于高层中声明的抽象服务接口。这不仅解除了PolicyLayer对于UtilityLayer的传递依赖关系,甚至解除了PolicyLayer对于MechanismLayer的依赖关系。请注意这里的倒置不仅仅是依赖关系的倒置,它也是接口所有权的倒置。我们通常认为工具库应该拥有它们自己的接口。但是当应用了依赖DIP时,我们发现往往是客户拥有抽象接口,而它们的服务者则从这些抽象接口派生。

                             图1 简单的层次化方案

                                            图2 倒置的关系
      • 依赖于抽象。,这个启发式规则建议不应该依赖于具体类——也就是说,程序中所有的依赖关系都应该终止于抽象类或这接口。根据这个启发式规则,可以推断出下面三条规则。
        1. 任何变量都不应该持有一个指向具体类的指针或这引用
        2. 任何类都不应该从具体类派生
        3. 任何方法都不应该override它的任何基类中的已经实现了的方法
        • 当然,每个程序中都会有违反该启发规则的情况。有时必须要创建具体类的实例,而创建这些实例的模块将会依赖它们。此外,该启发规则对于那些虽是具体但却稳定(nonvolatile)的类来说似乎不太合理。如果一个具体类不太会改变,并且也不会创建其它类似的派生类,那么依赖于它并不会造成损害(其实到时,进行合理的重构还是可以解决这一问题)。比如,在大多数系统中,描述字符串的类都是具体的。但该类是稳定的,不太会改变。因此,直接依赖于它不会造成损害。
        • 然而,我们在应用程序中所编写的大多数具体类都是不稳定的。我们不想直接依赖于这些不稳定的具体类。通过把它们隐藏在抽象接口的后面,可以隔离它们的不稳定性。但这并不是一个完美的解决放啊。常常,如果一个不稳定类的接口必须要变化时,这个变化一定会影响到表示该类的抽象接口。这种变化破坏了由抽象接口维系的隔离性。
        • 由此可知,该启发规则对问题的考虑有点简单了。另外一方面,如果看到更远一点,认为是有客户类来声明他们需要的服务接口,那么仅当客户需要时才会对接口进行改变。这样,改变实现抽象接口的类就不会影响到客户。
      • 例子1:依赖倒置可以应用于任何存在一个类向另一个类发送消息的地方。例如,Button对象和Lamp对象之间的情形。Button对象感知外部环境的变化。但接收到Poll消息时,它会判断是否被用户“按下”。它不关心是通过什么样的机制去感知的。可能是GUI上的按钮图标,也可能是一个能够用手指按下的真正按钮,甚至可能是一个家庭安全系统中的运动检测器。Button对象可以检测到用户激活或关闭它。
                Lamp对象会影响外部环境。当接收到TurnOn消息时,它显示某种灯光。当接收到TurnOff消息时,它把灯光熄灭。它可以是计算机控制台的LED,也可以是停车场的水银灯,甚至是激光打印机中的激光。
               该如何设计一个用Button对象控制Lamp对象的系统呢?下图1展示了一个不成熟的设计。Button对象接收Poll消息,判断按钮是否被按下,接着简单地发送TurnOn或者TurnOff消息给Lamp对象。这个模型的相应代码如下代码段1所示。请注意Button类直接依赖于Lamp类。这个依赖意味着,当Lamp类改变时,Button类会受到影响。此外,想要重用Button来控制一个Motor对象是不可能的。在这个设计中,Button对象控制着Lamp对象,并且也只能控制Lamp对象。这个方案违反了DIP。应用程序的高层策略没有和底层实现分离。抽象没有和具体细节分离。没有这种分离,高层策略就自动地依赖于底层模块,抽象就会自动地依赖于具体细节。
        • 解决方案。什么是高层策略呢?它是应用背后的抽象,是那些不随具体细节的改变而改变的真理。它是系统内部的系统——它是隐喻(metaphore)。在Button/Lamp例子中,背后的臭显是检测用户的开/关指令并将指令传给目标对象。用什么样的机制检测用户的指令呢?无关紧要!目标对象是什么?无关紧要!这些都是不会影响到抽象的具体细节。通过倒置对Lamp对象的依赖关系,可以改进图1中的设计,如下图2所示。在本例中,接口没有所有者,可以被多个不同的客户使用,并被许多不同的服务者实现。这样,接口就需要被放置在一个单独的组(group)中。在C++中,可以把它放在一个单独的namespace和库中。在Java中,可以把它放在一个单独的package中。

               图1  不成熟的Button和Lamp模型

                 图2 对Lamp应用依赖倒置原则
    • 接口隔离原则(The Interface Segregation Principle, 简称ISP)
      1. 不应该强迫客户依赖于它们不用的方法(胖接口)
      2. 如果强迫客户程序依赖于那些它们不使用的方法,那么这些客户程序就面临着由于这些未使用方法的改变所带来的变更。这无意中导致了所有客户程序之间的耦合。换种说法,如果一个客户程序依赖于一个包含它不使用的方法的类,但是其它客户程序却要使用这些方法,那么当其它客户要求这个类改变时,就会影响到这个客户程序。应该尽可能避免这种耦合,因此需要分离接口(拆分为多个具有内聚接口的抽象基类)。
      • 例子1:在一个安全系统中,有一些Door对象,它们可以被加锁和解锁,并且Door对象知道自己是开着还是关着。如程序片段1所示。现在考虑这样的实现,TimedDoor,如果门开着的时间过长,它就会发出警报声。为了做到这一点,TimedDoor对象需要和另一个名为Timer的对象交互,如代码片段2所示。如果一个对象希望得到超时通知,可以调用Timer的Register函数。该函数有两个参数,一个超时时间和一个指向TimerClient的对象的指针,该对象的TimeOut函数会在超时到达时被调用。怎样将TimerClient类和TimedDoor类联系起来,才能在超时时通知到TimedDoor中相应的处理代码呢?
        class Door
        {
        public:
          virtual void Lock() = 0;
          virtual void Unlock() = 0;
          virtual bool IsDoorOpen() = 0;
        };
        代码片段1

        class Timer
        {
        public:
          void Register(int timeout, TimerClient* client);
        };

        class TimerClient
        {
        public:
          virtual void Timeout() = 0;
        };
        代码片段2

        • 方案1:让Door继承TimedDoor,如下图1所示。该方案的问题:Door类依赖于TimerClient类了,可是并不是所有种类的Door都需要定时功能。事实上,最初的Door抽象类和定时功能没有任何关系。如果创建了无需定时功能的Door的派生类,那么这些派生类中就必须提哦那个TimeOut方法的退化(degenerate)实现——这就有可能违反LSP。这是一个接口污染的例子。

                                         图1
        • 方案2:使用委托分离接口。如下图2所示。该方案解决方案遵循ISP原则,并且避免了Door的客户程序和Timer之间的耦合。这是一个非常通用的解决方案。缺点是:需要多创建一个对象,委托处理可能导致一些性能和内存的损失。

                                                                          图2
        • 方案3:使用多重继承分离接口。优先选用的方案。

                                                                         图3

    展开全文
  • 本节书摘来华章计算机《软件工艺师:专业、务实、自豪》...2.1 面向流程的敏捷软件开发原则 这些原则会影响团队与组织的工作方式、协作方式以及架构方式。它们通常对工作中的许多方面设定规范或给出建议,其中包括...

    本节书摘来华章计算机《软件工艺师:专业、务实、自豪》一书中的第2章 ,第2.1节,[英]桑德罗·曼卡索(Sandro Mancuso)著 爱飞翔 译, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

    2.1 面向流程的敏捷软件开发原则

    这些原则会影响团队与组织的工作方式、协作方式以及架构方式。它们通常对工作中的许多方面设定规范或给出建议,其中包括:团队会议的形式、工作人员的角色、捕获需求的方式、衡量工作效率的方法、怎样进行迭代开发、怎样规划并分割工作量,以及如何展示工作进度、如何取得业务反馈,等等。
    面向流程的敏捷原则帮助团队专注于那些特别重要而且对业务确实有价值的事情。采用这些方式,能够很好地确保团队构建出符合需求的软件(building the right thing,做正确的事)。

    展开全文
  • 最近在读BOB大叔的敏捷软件开发,特别是TDD那一章节,启示真的不少,从测试驱动开发,讲到驱动表明程序设计的意图,从设计意图讲到对象依赖的解耦,从解耦建立Mock对象。 其实是对每个模块都编写单元测试,因为很多...
  • 书名:《敏捷软件开发原则,模式与实践》agile software development principles,patterns,and practices 作者:Robert C.Martin 敏捷软件开发宣言 我们正在通过亲身实践以及帮助他人实践,揭示更好的软件开发...
  • 利用敏捷软件开发原则重构游戏商店系统 【背景】功夫已经成功公测近3个月,庞大的代码量使得项目的维护成本呈现数量级的上升,修改BUG、添加新的功能、去除一些不再需要的功能等等修改,都需要不同程度的影响到其他...
  • 在robbin的那个贴下回了一下,问我要电子书的tx陆续有几个了,本来想通过邮件发的,但是无奈太大,一一发邮件太费神了,所以想了...《敏捷软件开发 原则、模式与实践》 Uncle Bob的名著,敏捷的经典名著,这本书比
  • 重视团队面授高于文档对于稳定的团队,以及持续投资,开发迭代的项目是有效的。毕竟程序员们的写作表达水平有限,时间有限。但是对于人员流动大,交接时间短的项目如果没有文档,又没人给你面授,那对于新人来说接收...
  • Bob大叔的书太经典了,拜读过大名鼎鼎的《企业应用架构模式》和《重构》,感觉相当经典,今天找到《敏捷软件开发 原则、模式与实践》,抽空好好品味一下。...
  • 在robbin的那个贴下回了一下,问我要电子书的tx陆续有几个了,本来想通过邮件发的,但是无奈太大,一一发邮件太费神了,所以...《敏捷软件开发 原则、模式与实践》 Uncle Bob的名著,敏捷的经典名著,这本书比...
  • 根本不存在充分分析这种东西,无论花多少时间去找出完美的软件结构,kehu
  • 看了一下夹在书中的发票,2010年在当当网购买的。断断续续的也看过几次,一直没有看完过。这次试着写写读书笔记。看看能不能坚持住。 转载于:https://blog.51cto.com/12788053/1912662...
  • 2019独角兽企业重金招聘Python工程师标准>>> ...敏捷软件开发原则、模式与实践——第1章 敏捷实践 》 转载于:https://my.oschina.net/zhaolin/blog/2993314
  • 今天刚开始读Martin的敏捷软件开发
  • 这对于开发和部署太友好了. 当然满足这个原则的前提是能够抽象出不变的需求, 分离出可能变化的功能. 这样新功能就不会改变原有不变的逻辑,只增加新代码就可以了. 但是如果新功能就是要原来我们认为不变那部分需求...
  • 包的内聚性原则。所谓内聚性就是一个模块或者类只执行一项功能,并只执行一项功能。但是本书扩展了这个概念,并通过三个原则对内聚性进行检验 1、重用发布等价原则。  包的设计必须以,使用者的角度进行考虑,第...
  • 今日翻阅了:Robert C.Martin著,邓辉翻译的这本书,本书简述了敏捷宣言和遵循的原则,另外重述了面向对象设计的原则. 敏捷宣言: 1 个体和交互 胜过 过程和工具 2 可以工作的软件 胜过 面面俱到的文档 3 客户...
  • 头五项原则是关于类设计的,它们是:  ◆ SRP,单一职责原则,一个类应该有且只有一个改变的理由。  ◆ OCP,开放封闭原则,你应该能够不用修改原有类就能扩展一个类的行为。  ◆ LSP,Liskov替换原则,...
  • 每一个软件模块都具有三个职责: 1. 运行起来所完成的功能; 2. 应对变化; 3. 能够使其阅读者理解; (P76) “在准备战役时,我发现计划本身总是无用的,但是做计划却是绝对必要的。” —— 艾...
  • 1. 前言 1. 敏捷开发(Agile Development)是一种面临迅速变化的需求快速开发软件的能力。2. 第一章 敏捷实践 P2 1. 不断增加更多的约束和人为制品... 敏捷软件开发宣言: 1. 个体和交互 胜过 过程和工具 
  • 第一章 敏捷实践 单纯的增加过程方法并不能保证项目的成功,项目没有简单到使用一些约束和制品就可以排除错误的地步,一个大而笨重的过程会产生它原来企图避免的问题,降低团队的开发效率,降低了团队的响应能力。...
  • 一、敏捷联盟宣言(The Manifesto of the Agile Alliance)● 个体和交互 胜过 过程和工具● 可以工作的软件 胜过 面面俱到的文档● 客户合作 胜过 合同谈判● 响应变化 胜过 遵循计划以前自己接到课程设计,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,072
精华内容 828
关键字:

敏捷软件开发原则