zoukankan      html  css  js  c++  java
  • 耗尽脑汁的需求分析工作

    摘要:怎么又变了?当初就应该让客户书面签字确认!你可能会经常发这样的牢骚,可是就算客户书面确认,客户还是会“赖账”的!软件项目的其中一项不变真理:人是会死的,需求是会变的!本章将会和你一起来体验软件需求分析工作的风风雨雨,找出需求分析工作的根本之道,了解UML如何帮助我们提升需求分析的水平。(本书已经发售)

    作者:

    张传波 网名:Fireball(火球)
    www.umlonline.org

    2.1  需求分析面面观

    客户需要的是一把梯子,系统分析员了解到的是一张凳子,开发人员做出来的是一张桌子,测试人员以为是一张椅子……很多角色参与项目工作,每种角色会从自身角色出发来理解需求,以致各种角色对需求的理解会不太一样。下表对各种角色的特点进行了分析:

    表 2.1  各种角色的特点

    另外要说明的是:
    客户一方的总倾向是:自己少花钱,让软件公司多做事情。
    而软件公司一方的总倾向是:多拿客户的钱,尽量少做事情。
    影响各人对需求理解的主要因素有两方面:一方面是角色的思考倾向,上表反应了这点;另外一方面是人的需求分析能力,能力越强的人越能把握需求,本书重点讲解的内容就是如何活用UML来提升需求分析能力。
    而更“离谱”的是:每个人嘴巴上说的需求和心目中的需求总是有差异的,所谓的“词不达意”,受表达能力所限,不是每个人都能完整准确地表达自己的想法;有时候客户今天想要这个,明天想要那个,甚至不知道到底想要什么!其实客户的这些表现,说明了客户对需求的认识是持续进化的。

    2.2 持续进化的客户需求

    你可能曾遇到过这样的情况:客户今天想要一个苹果,明天改变主意要一条香蕉,但后天突然又说还是苹果好,到最后他想要一个西瓜!遇到这样的情况,你会抱怨客户吗?你会后悔当初没有让客户签字确认吗?

    楚国有人坐船渡河时,不慎把剑掉入江中,他在舟上刻下记号,说:“这是我把剑掉下的地方。”当舟停驶时,他才沿着记号跳入河中找剑,遍寻不获。这就是刻舟求剑的故事。
    客户的想法总是在变化的,但总体来说是螺旋前进的,客户需求总是持续进化的,不要对此有任何抱怨,否则我们就是“刻舟求剑”之人了!

    某系统已经上线了,以下三种情况,你会更喜欢哪种情况呢?
    A. 客户一直没有提出过任何问题。
    B. 客户开始提了一些问题,但很快就没有其他问题了。
    C. 客户一直在提问题,项目组解决这些问题后,新的问题又来了,如此不断重复。

    情况A,估计客户没有怎样用过这套系统,所以没有提出什么问题。对于项目组来说,似乎不用再被麻烦的需求变更所纠缠,可以爽快地脱离此项目了。但对于客户来说,此系统白白花了他们的钱,对他们没有任何实际价值。而对于你所在的软件公司来说,你们项目组花费很大工作来做出来的软件系统,客户居然没有能用上,而你们公司很可能收不到项目的验收款项,此项目完全实现不了公司的战略。情况A的最终结果就是“双输”。

    情况B,有两种可能:第一种可能,客户试用了一段时间系统,后来由于客户方面的某些原因(原因可能有:更换领导、上了另外一个更重要的系统等)不再使用本系统了;第二种可能,客户试用一段时间,提出了很多问题,而你们项目组不能很好地解决这些问题,甚至认为客户“无理取闹”,所以客户干脆就不再使用本系统了。出现情况B,本项目估计也很难能通过最终验收,软件公司最终也收不到项目验收款项,而最终结果很可能也是“双输”。

    情况C,我曾经比较厌烦这样的情况,每天被客户的各式各样的小问题纠缠。其实这是相当理想的情况,说明客户在不断使用该软件。这些问题最开始会比较多,最开始项目组解决问题的速度会低于问题的产生速度,但后来问题会逐步减少,直到基本消失,软件和用户的“磨合”过程终于完成,系统成为客户日常工作中的一部分。出现情况C,我们项目组千万不要产生厌烦情绪,客户要真正用上软件,项目才算真正成功。软件只有对客户的工作真正有帮助,客户才算“赢”,而在客户能“赢”的基础上,我们软件公司才可能实现自己的“赢”。达致“双赢”是我们每个项目应追求的目标。

    从某种角度来说,需求变更其实是好事,说明客户对需求的理解更进一步,而我们觉得不适应,往往是因为我们对需求理解的进步程度不如客户。请看下图:

     图 2.1  客户VS项目组对需求的理解1

    此图表示项目开始后随着时间的发展,客户和项目组对需求理解程度的上升趋势。
    在时间=0时,客户的曲线并不是从零开始的,而是有一定起点的。这表明,客户在项目刚开始的时候,对需求是有一定认识的。而项目组在项目最开始时,对需求的理解几乎是零。
    随着时间的发展,客户对需求的理解越来越强,尽管项目组对需求的理解同样也变强,但项目组对需求的认识总是落后于客户,这样需求分析工作肯定陷于被动,总会被客户“牵着鼻子走”,很容易出现互相责怪的局面:客户责怪项目组水平太差,而项目组责怪客户需求变来变去!
    要打破这样的局面,项目组需要做到下图的效果: 

    图 2.2  客户VS项目组对需求的理解2

    项目刚开始时,客户对需求的理解确实比项目组强,但项目组在很短时间内对需求的认识超越了客户。从图中的“交点”打后,项目组对需求的认识总领先于客户。项目组应具备超强的业务学习能力,切实理解客户的真正需要,为客户规划出真正符合其需要的软件系统。 

    2.3 给客户带来价值,需求分析之正路

    手机短信订餐系统

    接下来我将会介绍一个手机短信订餐系统的故事,这是一个由真实个案改编的故事,通过这个故事来体会需求分析工作背后的道理。
    某IT公司规模不大,员工100来人。公司有一个简单的定餐系统,员工每天可以在公司内部网站上提交当天午餐定餐,前台汇总各人定餐后,将定餐汇总传真给餐厅,餐厅根据传真送餐。

    可是有这样的问题:部分员工因为上午请假或者外出工作,无法再网站上提交订餐,以至于中午回到公司时没有饭吃。

    于是老板想出了这样的办法:做一个手机短信定餐系统,不在公司的员工可通过手机短信定餐。
    于是成立了手机短信定餐项目小组,购买了手机短信收发的硬件,解决了选餐单、定餐、取消定餐等技术问题。但这个系统一会灵一会不灵,问题是出在软件、硬件,还是中国移动都难以搞清楚!做项目做麻烦的事情之一就是遇到“幽灵问题”,时而出现时而正常,项目小组挥汗如雨地试图解决这些问题,可一直没有办法搞定。
    老板大发雷霆了,怎么这样小的事情,竟搞成这个样子?
    后来有人提出来:不在公司的员工,打电话回公司告诉前台吃什么,不就搞定了?
    于是全世界恍然大悟,天啊!

    需求分析核心的问题就是客户到底想要什么的问题!
    客户往往只会有朦胧的大概的想法,他们提出来的需求,只是表面的,不全面的,甚至是互相矛盾的,我们需要透视它的本质。
    我们做需求分析工作,往往会将需求分析和软件设计混在一起。需求分析核心目的就是解决软件有没有用的问题,而软件设计是解决软件用多大的成本做出来的问题。

    需求分析首要任务是保证软件的价值,我们必须保证做出来的软件是符合客户的利益的。如果我们不能看清楚客户的真正需要而仓促上马,则很可能付出巨大成本仍然不能满足客户的利益。

    手机短信定餐系统要解决的问题其实就是:让不在公司上班的员工也能方便地定餐,手机短信定餐系统本身并不是需求,只是一种解决方案而已。当然因为这个要求是老板提出来的,所以项目组可能就没有进一步思考这个系统的必要性了。我们的客户提出具体要求的时候,我们往往不能思考这些要求背后的需要是什么,而直接将这些客户要求当成客户需求来处理。
    给客户带来切实的价值才是我们真正的任务,而不是盲目听从客户的要求而不加分析。

    需求分析的大道理

    软件需求分析工作到底是一个怎样的工作呢?我们如何才能把握住真正的客户需要,做出给客户带来实在价值的软件系统呢?
    我们说说需求分析的一些大道理:
    首先我们需要明确项目的背景,我们要回答这些问题:也就是为什么会有这个项目?客户为什么想做这样的一个项目?如果没有这个项目会怎样?
    了解背景的基础上,我们需要进一步了解以下内容:
    1) 本项目解决了客户的什么问题?
    2) 本项目涉及到什么人、什么单位?
    3) 本项目的目标是什么?
    4) 本项目的范围是怎样的?
    5) 本项目的成功标准是什么?
    以上这些内容,我们称之为客户的“需要”。

    接下来,就可以定详细的需求规格说明书了,一般我们会对功能性需求和非功能性需求都列出详细的要求,我们把这些要求定义为“需求规格”。

    图 2.3  背景 需要 需求规格

    做需求分析工作时,我们往往只看到“需求规格”这个层面,这是很表面的需求。我们应该透视这些表面的需求,去挖掘客户的“需要”。如果我们不清楚客户的“需要”,就很容易被“需求规格”所“迷惑”,难以做出对客户有实际价值的软件系统。

    我们再回顾一下“需求分析面面观”小节中提到的各种角色的特点,越是基层的客户,他越容易提出“需求规格”级别的需求,越是高层的客户越容易提出“需要”级别的需求,当然有时候连客户中高层也不能很清楚的描述自己的“需要”是什么。
    项目组不应该只将自己定位在软件的制造者,而应该是软件价值的创造者。我们不是为客户提供一套软件系统,而是提供一套能提升客户价值的服务。所以项目组不应该被动地接受需求,而应该主动出击,帮助客户找出真正的需要,整理出符合客户需要的需求规格。
    如果我们能说出客户内心深处真正想要的,而客户又不能表达出来的东西,我们才能真正做到“为客户带来价值” !UML将会帮助我们提升需求分析的能力。 

    2.4 UML助力需求分析

    全面深入理解客户的业务,才能帮助我们准确的把握客户的需要。而在理解客户业务的同时,我们往往需要做业务流程再造(BPR:Business Process Reengineering)的工作。BPR简单说就是过程改进的工作,事实上绝大部分的软件系统都需要面对过程改进这个问题。上一套软件系统,并不是手工工作转变为信息化这么简单,涉及到工作模式、工作习惯、管理思想等的改变,涉及到很多人的利益及利益关系发生变化。
    我们可以利用结构型的UML图来对客户业务进行结构建模,利用行为型的UML图来进行行为建模。对业务概念等静态结构进行系统化的梳理和提炼,叫结构建模;对业务流程等动态内容进行系统化的梳理和提炼,称为行为建模。这些建模活动将帮助我们更好地认识客户的业务和做好业务流程再造的工作。

    图 2.4  UML助力需求分析

    该图展示了需求分析的大致过程,以及在这个过程当中UML所发挥的作用。
    掌握了UML这个有力的工具,将会帮助我们的需求理解曲线上升得更快,实现图2.2的效果,比客户更加理解需求,为客户带来更大的价值。
    本小节只是简单介绍了UML对需求分析工作的帮助,本书后面章节将详细介绍如何活用UML成为需求分析高手。 

    2.5 小结与练习

    小结

    本章最主要的目的其实就是帮你“洗脑”!需求分析的工作其实很复杂,可以足够写一本书的内容。而我希望只通过一个章节能向你讲清楚需求分析工作的基本道理,让你认清需求分析工作的根本,并且明白到要做好需求分析工作并没有捷径,只有切实提高自身水平。下面我们一起来回顾一下本章的主要内容:
    认识清楚需求分析工作中客户方和软件公司一方各种角色的特点,能帮助我们需求分析工作更有针对性。总体来说,客户方的倾向是花小钱办大事,而软件公司一方的倾向是多拿钱少办事。
    “双赢”是我们应该追求的目标,软件只有对客户的工作真正有帮助,客户才算“赢”,而在客户能“赢”的基础上,我们软件公司才可能实现自己的“赢”。
    不要抱怨客户变来变去,客户对需求的理解总是趋向上升的,而项目组也是一样。如果项目组对需求的认识落后于客户,就会陷于“被动”的局面,项目组应该努力提升水平,想办法让自己对需求的认识领先于客户。
    需求分析工作是很复杂难度很高的工作,如果看不清楚客户的真正“需要”,就很可能重犯“手机短信定餐系统”的错误。项目组不应该只将自己定位在软件的制造者,而应该是软件价值的创造者。我们不是为客户提供一套软件系统,而是提供一套能提升客户价值的服务。项目组不应该被动地接受需求,而应该主动出击,帮助客户找出真正的需要,整理出符合客户需要的需求规格。
    我们应当活用UML进行结构建模和行为建模,帮助我们更好地认识客户的业务和做好业务流程再造的工作。

    练习

    1. 如果你有需求分析工作的经验,请你根据你的实际工作体会总结出最少3点最麻烦的问题。如果你还没有具体的需求分析工作经验,那么请你列出最少3点你认为可能是最麻烦的问题。记录这些问题,看看后续章节能不能解决你这些问题。
    2. 请分析下图,说明这是一种怎样的状况?我们应该追求这样的境界吗?

    图 2.5  客户VS项目组对需求的理解3 

    本书样章下载:
    样章含本书1-3章全部内容,有数万字和几十个图。
    http://www.umlonline.org/school/attachment.php?aid=MTgyM3xhZWFjY2Q2ZXwxMzI5NzQyMDc2fGYzNTFsYzdrTjJseVdQYldWcENrN2lwbFV3bEU3QzNsVHFybHdBanljRGhxekx3

    作者:
    张传波,网名:Fireball(火球)
    www.umlonline.org/school/


    如果你觉得本文对你有帮助,麻烦你点击一下“推荐”,谢谢!

    张传波 软件知识原创基地(www.umlonline.org)

    我与WCF有个约会之牵手篇-第一个WCF示例程序

    -我生君未生,君生我已老。 我离君天涯,君隔我海角。

    -就像这首诗一样,WCF是个什么动动我也是从零开始学习,这篇文章主要是介绍如何实现一个WCF的示例,更多是告诉大家怎么做而非为什么这么做。深层次的问题,我们后面再深究。

     

      话说WCF(Windows Communication Foundation)由微软发展的一组数据通信的应用程序开发接口。它为面向服务(Service Oriented)应用程序提供一个分布式编程框架,在.NET3.0引入,可以说是集分布式应用程序开发之大乘者。

      为什么这么说呢,如果是.NET2.0和之前的版本,微软发展了Web Service,.NET Remoting,以及基础的 Winsock 等通信方法。而且每个通信方法的设计方法不同并且相互有重叠,例如 .NET Remoting 可以开发 SOAP, HTTP 通信。自然每一个通信方法都是要从新学习的,这就对像我这样被微软惯坏了的程序猿灰常的不友善了。

      大幸的是,微软重新查看了这些通信方法,并设计了一个统一的程序开发模型,对于数据通信提供了最基本最有弹性的支持,这就是 Windows Communication Foundation。

      PS:请大家不要鄙视我是因为想一劳永逸才学WCF=。=

     

      -------------------------------------------约会开始-------------------------------------------

     

      今天我们的目的是牵手成功,为了够拉风,我们需要VS2008安装NET 3.0以上版本(这样才能创建WCF应用程序模板),不够拉风妹子不出门,你们懂的。

      首先我们新建一个WCF服务应用程序,如果你的座驾够拉风,就会在如图1所示的地方找到WCF服务应用程序的模板。

     

      图1     图2

      完成之后呢,VS已经创建了一个WCF服务应用程序,并且提供给我们了一个简单的示例代码。结构如图2所示。

      没有上述环境的同学,还有一种创建WCF服务应用程序的方法,需要分别创建4个项目来定义服务契约(Contracts),实现服务契约(Services),提供宿主(Hosting)和客户端(Client)。

    这种方式在Artech的WCF之旅的第一篇有详述,我就不画蛇添足了。

      IService1.cs:定义服务契约。VS生成的示例代码如下:

    View Code
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
    [ServiceContract]
    public interface IService1
    {
    
        [OperationContract]
        string GetData(int value);
    
        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);
    
        // TODO: 在此添加您的服务操作
    }
    
    
    // 使用下面示例中说明的数据约定将复合类型添加到服务操作。
    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";
    
        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }
    
        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

      我们可以看到IService1是一个接口,由特性[ServiceContract]来指示这个接口在应用程序中用来定义服务契约。

      接口中的方法使用[OperationContract]特性来指示这个方法是服务契约的一个部份,可供远程调用。

      CompositeType类是服务端和客户端之间要传送的自定义数据类型,使用[DataContract]特性来指示该类的示例对象可以被序列化在服务端和客户端之间传送。

      但成员方法不会被传递。并且默认情况下除非使用[DataMember]特性来指示,不具有该特性的成员都被排除在外,也就是说,客户端程序不会获得被排除在外的成员的任何信息。

      有人看到这肯定会喊,楼主是想拿VS生成的东西当示例?当然,我没准备学腾讯,既然我们是来牵手的,就写个牵手的例子把。

      来修改一下服务契约,命名空间为JohnConnor.TakeYourHand

    [ServiceContract]
        public interface ITakeYourHand
        {
            [OperationContract]
            Result FirstLook();//获取第一眼印象分
            [OperationContract]
            Result ShowTime();//尝试表现下自己
            [OperationContract]
            Result TakeHand(int Point);//尝试牵手
        }
    
        [DataContract]
        public class Result
        {
            [DataMember]
            public int Score { get; set; }
            [DataMember]
            public bool Succeed { get; set; }
            [DataMember]
            public string Message { get; set; }
        }

      ITakeYourHand接口定义了我们这个牵手服务程序的服务契约,Result则定义了一个数据协议,它可以作为客户端和服务端传递数据的载体。

     

      Server1.svc:实现服务契约,当然这个文件还有一个特殊的用途:对WCF服务的调用就体现在对.svc文件的访问上,它本身还载有其他一些服务的属性信息(这个有木有人赐教一下?)。

      现在我们把之前定义的接口在Server1.svc.cs实现一下。命名空间不变。

       public class TakeYourHand : ITakeYourHand
        {
            public Result FirstLook()
            {
                Random num = new Random();
                Result result = new Result()
                {
                    Score = num.Next(0, 10),//返回一个1~9的随机人品
                    Succeed = false
                };
                result.Message = "第一印象" + result.Score + ",Good Luck!";
                return result;
            }
            public Result TakeHand(int Point)
            {
                Random num = new Random();
                Result result = new Result();
                if (num.Next(Point, 11) == 10)//分数越大,成功几率越大
                {
                    result.Succeed = true;
                    result.Score = 0;
                    result.Message = "木有反抗,恭喜恭喜!";
                }
                else
                {
                    result.Succeed = false;//贸然牵手扣3分
                    result.Score = -3;
                    result.Message = "心太急有木有,扣3分!";
                }
                return result;
            }
    
            public Result ShowTime()
            {
                Random num = new Random();//随机的表现大于2则Good否则Bad,
                Result result = new Result();
                if (num.Next(0, 10)>2)
                {
                    result.Score = 1;
                    result.Message = "帅气的表现征服了全场!加1分";
                }
                else
                {
                    result.Score = -1;
                    result.Message = "扣鼻shi被妹子看见了!扣1分";
                }
                result.Succeed = false;
                return result;
                
            }
        }

      现在服务端已经大功告成拉。现在我们可以在解决方案资源管理器里选中Service1.svc,然后F5运行。

       图3

      如图3就会有一个Wcf测试客户端弹出来,服务添加成功后在浏览器输入显示的地址,

      这里是http://localhost:5140/Service1.svc 就可以看到一些关于服务的信息了,这时候服务就是开启状态了。

      这里得提一下WCF的核心ABC了,

    • A:Address Where-WCF的Service都有一个唯一的地址。这个地址给出了Service的地址和传输协议(Transport Protocol)
    • B:Binding How-通信(Communication)的方式
    • C:Contract What-契约,描述了Service能提供的各种服务


      上图中点击一下配置文件就可以看到:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.serviceModel>
            <bindings>
                <basicHttpBinding>
                    <binding>
                        <!--省略绑定的细节-->
                    </binding>
                </basicHttpBinding>
            </bindings>
            <client>
                <endpoint address="http://localhost:5140/Service1.svc" binding="basicHttpBinding"
                    bindingConfiguration="BasicHttpBinding_ITakeYourHand" contract="ITakeYourHand"
                    name="BasicHttpBinding_ITakeYourHand" />
            </client>
        </system.serviceModel>
    </configuration>

      endpoint标签里就描述了这三要素,地址address,绑定binding,契约contract。WCFbinding可以配置成BasicHttpBinding来兼容(或者说变身成)WebService。

      下面我们继续来进行客户端的部份。演示如何来调用WCF服务。首先,我们来建一个控制台应用程序命名为JohnConnor.TakeYourHand.Client

      首先必须保证服务在开启状态,然后右键添加服务引用,会弹出如图4的会话框:

      图4 在地址栏中输入WCF客户测试端显示的地址并前往,就能看到服务的详细信息了。我们把命名空间改为

      TakeYourHandService。确定,然后VS就会给我们生成一个服务引用和一个本地的app.config配置文件。

      这时候 远程对象 就应该存在于JohnConnor.TakeYourHand.Client.TakeYourHandService命名空间之下了。首先我们把这个命名空间引用到Program头中。再来实现客户端:

      

     class Program
        {
            static void Main(string[] args)
            {
                int Point = 0;
                Console.WriteLine("约会开始,按回车键测试你的第一印象分!");
                Console.ReadLine();
                using (TakeYourHandClient client = new TakeYourHandClient())//创建一个远程对象
                {
                    var result = client.FirstLook();//调用远程方法FirstLook
                    Point += result.Score;
                    Console.WriteLine(result.Message);
                    while (!result.Succeed && Point >= 0 && Point < 10) 
                    {
                        Console.WriteLine("当前分数:" + Point.ToString() + ".输入0尝试牵手,输入1来Show一下!回车确定");
                        var key = Console.ReadLine();
                        if (key == "0")
                        {
                            result = client.TakeHand(Point);//调用远程方法TakeHand
                            Point += result.Score;
                            Console.WriteLine(result.Message);
                        }
                        if (key == "1")
                        {
                            result = client.ShowTime();//调用远程方法TakeHand
                            Point += result.Score;
                            Console.WriteLine(result.Message);
                        }
                        else
                        {
                            Console.WriteLine("按规矩出牌,别乱来");
                        }
                    } 
                    if (Point < 0)
                    {
                        Console.WriteLine("当前得分:" + Point.ToString()+",你被妹子甩了...");
                    }
                    if (Point >= 10)
                    {
                        Console.WriteLine("妹子已主动牵起了你的手,恭喜恭喜!");
                    }
                    Console.WriteLine("游戏结束");
                    Console.ReadKey();
                }
            }
        }

      TakeYourHandClient就是远程对象了因为我们实现契约的类名为TakeYourHand,所以远程对象的命名一般是加Client作为后缀。在服务运行的时候,我们就可以调用远程对象提供的方法来做一些事情了。运行控制台程序:

      

      看来我还是个高富帅的得分,8分,两下就让妹子主动牵手了吼吼!不知道各位会不会是屌丝或是穷矮矬的得分。。。。

       ---------------------------------------------------分割线-------------------------------------------------

      我也是刚开始接触WCF,希望这个例子能帮助像我一样的初学者对WCF有一个初步的印象。文中有任何问题,欢迎指正,一起进步。

      

    分类: WCF
    标签: WCF
  • 相关阅读:
    listview 选择后高亮显示
    高德坐标转百度坐标并导航
    android scrollview listview显示不全
    java.lang.UnsatisfiedLinkError: Couldn't load vi_voslib from loader dalvik.system.PathClassLoader
    JAVA Map 和 List 排序方法
    Butter Knife 使用方法
    Volley 结合GSON或FastJson用法
    android-menudrawer 和SlidingMenu 用法
    ueditor 正在读取目录及网络链接错误
    【449】backup get weekly tweets
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2455885.html
Copyright © 2011-2022 走看看