美文网首页
《敏捷软件开发 原则 模式与实践》(1)--敏捷开发

《敏捷软件开发 原则 模式与实践》(1)--敏捷开发

作者: 黎明脚印 | 来源:发表于2019-03-03 16:48 被阅读0次

序言

对用户来说,通过直观,简单的界面呈现出恰当特性的程序就是美的。对软件设计者说,被简单,直观的分割,并具有最小内部耦合的内部结构就是美的。对开发人员和管理者来说,每周都会取得最大进展,并且生产出无缺陷代码的具有活力的团队就是美的。

对于开发者来说,单纯的实现功能,而不去考虑代码结构,性能等一系列问题开发出的软件是没有美感可言的,我们应该从设计原则,软件设计的基本模式去完善代码和设计,从有助于团队融为一个有机整体的敏捷实践中去提高软件开发效率,达成软件之美,这样在日后的维护中可以起到事半功倍的效果。

敏捷开发

敏捷开发(Agile Development) 是一种面临迅速变化的需求快速开发软件的能力。
设计原则,设计模式,敏捷开发实践都是重要的,但是如果项目要想取得成功,就必须构建起具有合作精神的,自组织的团队,因为有凝聚力的团队将具有最强大的软件开发力量。

敏捷实践

软件产品开发是多变的,不确定的。好的实践会让项目开发有条不紊的进行,最终完美交付。不良的实践往往会产生很多问题,进度缓慢延期,预算增加,质量低劣,开发人员长时间的加班缺生产出来更加差的软件产品,开发人员感到沮丧,客户感到失望。要是这个时候去规范过程,可能会导致效率更加低下,成本变高等一系列问题。

这个时候就需要一套方案去解决这个问题,敏捷开发应运而生。

敏捷软件开发宣言是:

  • 个体和沟通胜过过程和工具
    合作,沟通能力默契的平均水平团队>
    沟通少的高水平团队>
    过程和工具的选择
  • 可以工作的软件胜过面面俱到的文档
    优秀的代码和团队>
    短小,主题突出的系统原理和结构文档>
    详细的文档说明(文档不断地同步代码需要大量的时间,因此除非迫切需要时候,再去编制文档)
  • 客户合作胜过合同谈判
    特别是对于软件服务类的公司,市场部门和商务部门进行需求的调研工作,然后形成需求文档,这样有时候对于需求的开展和项目的开发是很困难的,没有客户的参与,很容易做出客户不满意的产品。
  • 响应变化胜过遵循计划
    世界上唯一不变的是变化,在软件项目中,把一个项目吃从头规划到尾,做到事无巨细是比较困难的,因此,我们要做到响应变化,简化长期计划,细化短期计划,不断地进行调整,改进。

下面是敏捷开发的一些原则:

  1. 我们最优先要做的是通过尽早的,持续的交付有价值的软件来使客户满意
  2. 即使到了开发的后期,也欢迎改变需求,敏捷过程利用变化来为客户创造竞争优势
  3. 经常性地交付可以工作的软件,交付的间隔可以从几周到几个月,交付的时间间隔越短越好
  4. 在整个项目开发期间,业务人员和开发人员必须天天都在一起工作
  5. 围绕被激励起来的个人来构建项目,给他们提供所需要的环境和支持,并信任他们能够完成工作
  6. 在团队内部,最具有效果并富有效率的传递信息的方法,就是面对面的交流
  7. 工作的软件是首要的进度度量标准
  8. 敏捷过程提倡可持续的开发速度,责任人,开发者和用户应该能够保持一个长期的,恒定的开发速度
  9. 不断地关注优秀的技能和好的设计会增加敏捷能力
  10. 简单--使未完成的工作最大化的艺术是根本的
  11. 最好的架构,需求和设计出自自组织的团队
  12. 每隔一段时间,团队会在如何才能有效的工作方面进行反省,然后形影的对自己的行为进行调整。

极限编程

作为开发人员,我们应该记住,XP并非唯一选择

极限编程(eXtreme programming,XP)是敏捷方法中最著名的一个,由一系列简单缺互相依赖的实践组成。

下面是极限编程的通用软件开发方法,我们可以在实际项目使用中进行修改,选择适合自己的:
1. 客户作为团队成员
对于现在,客户很难和开发在一起工作,目前就是产品经理应该是项目的一部分,和开发人员始终保持同步
2. 用户故事
客户需求的一种描述方式,他是一个计划工具,产品经理可以根据它的优先级和估算代价来进行计划,并安排该需求的时间
3. 短交付周期
不断迭代,不断修改,不断确认,确保大方向不变。。

  • 迭代计划
  • 发布计划
    4. 验收测试
    5. 结对编程
    6. 测试驱动的开发方法
    7. 集体所有权
    8. 持续集成
    9. 可持续的开发速度
    10. 开放的工作空间
    11. 计划游戏
    划分业务人员和开发人员之间的职责,业务人员决定feature的重要性,开发人员决定实现一个feature所华飞德代价。
    12. 简单的设计
    最小实现该迭代的代价
    去除重复代码
    13. 重构
    在不改变功能的前提下,对代码进行小的改进。
    14. 隐喻
    将整个系统联系在一起的全局视图。

计划

当你能够度量你所说的,并且能够用数字去表达它是,就表示你了解了它;若你不能重复它,不能用数字去表达它,那么说明你的知识就是匮乏的,不能令人满意的。

通过一次次的迭代和发布,项目进入了一种可预测的,舒适的开发节奏。每个人都知道将要做什么,以及如何去做,也能实实在在的看到项目的进展。以提高工作质量
在项目开始时,客户人员和客户会尽量的确定出所有真正的用户故事,也就是真实需求。后面不断地去进行调整。

  • 探究,分解和速度
  • 发布计划
  • 迭代计划
  • 任务计划
    迭代的中点,进行回顾,进行计划的调整。
  • 迭代
    敏捷迭代计划:sprint plan
    对任务难度等级进行评分,1,2,3,5,8…..
    任务板,TODO,doing,done

测试

编写单元测试是一种验证行为,也是一种设计行为,可以对功能进行验证,以及后续对代码的修改起到保护,形成一种良性的循环。

  • 测试驱动的开发方法(TDD)
    在设计测试用例的时候,进行功能代码的设计,对功能进行划分,能设计出更好的软件代码
    测试可作为文档,可以清晰的看到功能调用。
    代码可控,方便后续进行重构

重构

重构是在不改变代码外在行为的前提条件下对代码作出修改,以改进代码的内部结构的过程。

每一个软件模块都有三项职责:

  1. 它运行起来所完成的功能
  2. 他要应对变化
  3. 要和阅读它的人进行沟通

因此我们需要根据一些设计模式和设计原则来设计出易于阅读,易于修改的代码。不断地对之前的代码进行回顾,进行重构。

下面进行一个素数产生程序来示例重构:

初始TestCase:

public class TestGeneratePrimes {

    @Test
    public void testPrimes() {
        int[] nullArray = GeneratePrimes.generatePrimes(0);
        Assert.assertEquals(nullArray.length, 0);

        int[] minArray = GeneratePrimes.generatePrimes(2);
        Assert.assertEquals(minArray.length, 1);
        Assert.assertEquals(minArray[0], 2);

        int[] threeArray = GeneratePrimes.generatePrimes(3);
        Assert.assertEquals(threeArray.length, 2);
        Assert.assertEquals(threeArray[0], 2);
        Assert.assertEquals(threeArray[1], 3);

        int[] centArray = GeneratePrimes.generatePrimes(100);
        Assert.assertEquals(centArray.length, 25);
        Assert.assertEquals(centArray[24], 97);
    }
}

生成素数类(未重构之前):

public class GeneratePrimes {
    /**
     * @param maxValue is the generation limit
     * @return
     */
    public static int[] generatePrimes(int maxValue) {
        if (maxValue >= 2) {
            //声明
            int s = maxValue + 1;
            boolean[] f = new boolean[s];
            int i;

            //初始化数组为ture
            for (i = 0; i < s; i++) {
                f[i] = true;
            }

            //去除已知的非素数
            f[0] = f[1] = false;

            //筛选出素数
            int j;
            for (i = 2; i < Math.sqrt(s) + 1; i++) {
                for (j = 2 * i; j < s; j += i) {
                    f[j] = false;
                }
            }

            //看有多少个素数
            int count = 0;
            for (i = 0; i < s; i++) {
                if (f[i]) {
                    count++;
                }
            }

            //将素数放到结果中
            int[] primes = new int[count];
            for (i = 0, j = 0; i < s; i++) {
                if (f[i]) {
                    primes[j++] = i;
                }
            }

            //返回素数
            return primes;
        } else {
            return new int[0];
        }
    }
}

看看上面的代码是不是难以阅读,一个方法里面堆满了整个方法的实现。完全的面向过程编程了;

重构后的TestCase:

public class TestGeneratePrimes {

    @Test
    public void testPrimes() {
        int[] nullArray = PrimeGenerate.generatePrimes(0);
        Assert.assertEquals(nullArray.length, 0);

        int[] minArray = PrimeGenerate.generatePrimes(2);
        Assert.assertEquals(minArray.length, 1);
        Assert.assertEquals(minArray[0], 2);

        int[] threeArray = PrimeGenerate.generatePrimes(3);
        Assert.assertEquals(threeArray.length, 2);
        Assert.assertEquals(threeArray[0], 2);
        Assert.assertEquals(threeArray[1], 3);

        int[] centArray = PrimeGenerate.generatePrimes(100);
        Assert.assertEquals(centArray.length, 25);
        Assert.assertEquals(centArray[24], 97);
    }

    @Test
    public void testExhaustive() {
        for (int i = 2; i < 500; i++) {
            verifyPrimeLIst(PrimeGenerate.generatePrimes(i));
        }
    }

    private void verifyPrimeLIst(int[] list) {
        for (int aList : list) {
            verifyPrime(aList);
        }
    }

    private void verifyPrime(int n) {
        for (int factor = 2; factor < n; factor++) {
            assert (n % factor != 0);
        }
    }

}

重构后生成素数的代码:

public class PrimeGenerate {

   private static boolean[] crossedOut;
   private static int[] result;

   /**
    * @param maxValue is the generation limit
    * @return
    */
   public static int[] generatePrimes(int maxValue) {
       if (maxValue < 2) {
           return new int[0];
       } else {
           uncrossIntegersUpTo(maxValue);
           crossOutMultiples();
           putUncrossedIntegerIntoResult();
           return result;
       }
   }

   private static void uncrossIntegersUpTo(int maxValue) {
       crossedOut = new boolean[maxValue + 1];
       for (int i = 2; i < crossedOut.length; i++) {
           crossedOut[i] = false;
       }
   }

   /**
    * 筛选素数
    */
   private static void crossOutMultiples() {
       int limit = determineIterationLimit();
       for (int i = 2; i <= limit; i++) {
           if (notCrossed(i)) {
               crossOutMultiplesOf(i);
           }
       }
   }

   private static int determineIterationLimit() {
       double iterationLimit = Math.sqrt(crossedOut.length);
       return (int) iterationLimit;
   }

   private static void crossOutMultiplesOf(int i) {
       for (int multiple = 2 * i; multiple < crossedOut.length; multiple += i) {
           crossedOut[multiple] = true;
       }
   }

   private static boolean notCrossed(int i) {
       return !crossedOut[i];
   }

   private static void putUncrossedIntegerIntoResult() {
       result = new int[numberOfUncrossedIntegers()];
       for (int j = 0, i = 2; i < crossedOut.length; i++) {
           if (notCrossed(i)) {
               result[j++] = i;
           }
       }
   }

   private static int numberOfUncrossedIntegers() {
       int count = 0;
       for (int i = 2; i < crossedOut.length; i++) {
           if (notCrossed(i)) {
               count++;
           }
       }
       return count;
   }

}

重构后代码阅读起来比之前舒服了,也更容易理解。重构的目的是为了每天清洁你的代码,不要让脏乱累积。定时对代码进行重构整理,这样就能通过最小的努力就能够对我们的系统进行扩展和修改。

相关文章

网友评论

      本文标题:《敏捷软件开发 原则 模式与实践》(1)--敏捷开发

      本文链接:https://www.haomeiwen.com/subject/uyfduqtx.html