`
dazuiba
  • 浏览: 131430 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

真实项目中的一个需求分析、建模、编码、测试过程(一)

    博客分类:
  • ruby
阅读更多
笔者要完成的,是个《高考志愿填报分析》模块。该模块的输入是用户填报的志愿。输出是业务人员些的一些分析结果。

这里有几个需要说明的:
1 分析算法现在只有三种,分别是:大平行,小平行,和顺序志愿。到底使用哪个分析算法,是根据用户所在的省份、所填报志愿的批次,以及给定批次的分段(可以理解成二级批次,不一定每个省份都有)来定。
  经过分析,确定有三个条件:用户所在的省份(用拼音表示)、志愿批次(用1,2,3表示)、分段(可为空,用A,B,Y表示)

2 分析分为两种,一种是针对对某个批次分析,为用户提供该批次的填报意见,另外一种,是对具体填报的学校进行分析,为用户提供该学校(所添专业)的填报意见

3 分析的根据为录取概率。录取概率分为某学校的录取概率,以及某学校某专业的录取概率。录取概率和用户所在省份的预计分数线,以及用户自己预估的分数来决定。因为篇幅有限,这里不作展开。

好,有了需求,就可以建模了。

#某一批次(的某个分段)的志愿
PriorityWish
has_many   :college_wishes
belongs_to :user
member :priority_code #批次,代码中使用编码来表示
member :segment       #分段,可为空,(A,B,Y)中的一个

#某个学校的志愿
CollegeWish
has_many :major_wish
belongs_to :college
belongs_to :priority_wish
member     :position #第几志愿

#某个专业的志愿
MajorWish
belongs_to :major
belongs_to :college_wish
member     :position #第几志愿

另外:
College has_many majors

好,模型大致就这样了。

接下来现考虑分析算法的选择。这个我选择了硬编码的方式,Ruby的表达能力强,所以很自然选择了这个方案。
当然,有的人偏向用配置文件/数据库方式。我觉得这两种方式都不直观,对这个这里不讨论。

JudgeCond={
    :One=>[:anhui   ,    :hebei,  :hainan,    :guangxi,       :zhejiang, 
                        {:jilin=>{1=>"A"},:guizhou=>1,:sichuan=>1}] ,

    :Two=>[:shandong,    :shanxi_20, :chongqing, {:guizhou=>[2,3],:sichuan=>[2,3],:jilin=>{2=>"A,B",3=>"all"}}],

    :Three=>[:heilongjiang,{:guangxi=>{2=>"Y",3=>"Y"}, :sichuan=>{2=>"Y",3=>"Y"},:jilin=>{1=>"B"}} ]
  }

可以看到,:One,:Tow,:Three 分别代表了三种分析策略。

刚才说了,分析有两种,一种是针对某个批次的分析:
class PriorityStrategyBase
  member :priority_wish
  def analyze
  end
end

底下有三种策略。

class PriorityStrategyOne ....
class PriorityStrategyTow ....

另外一种是针对某个学校的填报分析:

class CollegeStrategyBase
  member :college_wish

代码的结构就这样。

重头在测试:

1 不用rspec。从java转过来的人,还是很崇拜当初kent设计的 Junit(ruby里就是 test/unit喽)的
2 不用rails的fixtures,丑陋,难以复用,难以保证测试覆盖率,维护代价太大。。(此处省略200字)
3 喜欢Mocha,比java 的jmock好用,美观大方,使用简单,功能也很单纯。
  由于精力有限,今天只帖部分测试。等我明天有空,再来个续集。
3 不用windows,windows上跑个测试要等一只烟的功夫。没那个耐心。
  (众里寻他千百度,原来要的是Vim,
   借问好editor何处有,dazuiba遥指vim,
   五岳归来不看山,Vim归来不看簧片
   。。。此处省略1000字)

4 看书上说,TDD,这个我还学不来。也没打算尝试。
  (TDD的书看了不少,最薄的kent Back的《tdd实践》,可惜例子太简单鸟,不能说明问题)
   (说句那心里话,我现在觉得叫喊TDD的又两种人,一种是我崇拜的高手,另外一种跟整天混douban的我一个德行--装X)

6 由于职业限制,只保留必须的实例代码。对于想自己在机器上跑的同学,您失望了,谁让这是真是的项目呢。

7 人家大牛说,好的代码就是注释。我是直接理解成“不些注释”。所以您忍着点,注释,这个‘真没有’
废话不说了。直接上代码。
module Analyze
  JudgeCond={
    :One=>[:anhui   ,    :hebei,  :hainan,    :guangxi,       :zhejiang, 
                        {:jilin=>{1=>"A"},:guizhou=>1,:sichuan=>1}] ,

    :Two=>[:shandong,    :shanxi_20, :chongqing, {:guizhou=>[2,3],:sichuan=>[2,3],:jilin=>{2=>"A,B",3=>"all"}}],

    :Three=>[:heilongjiang,{:guangxi=>{2=>"Y",3=>"Y"}, :sichuan=>{2=>"Y",3=>"Y"},:jilin=>{1=>"B"}} ]
  }

  def self.find_province_cond(province,priority_code,segment=nil)
    assert province
    assert priority_code
    province_cond.find{|e|e.accept?(province, priority_code, segment)}
  end

  def self.province_segments(province, priority_code)
    return [] if priority_code<11
    priority_id = priority_code-10

    province_cond.find_all{|e|
      e.province_id==province.id&&e.options.is_a?(Hash)&&(_v=e.options[priority_id])&&_v!="all"
    }.map{|e|e.options[priority_id].split(",")}.flatten.sort

  end

  def self.analyze_college(wish)
    assert wish.is_a? Wish
    result=Analyze.find_province_cond(wish.user.province, wish.priority_code, wish.segment)
    result = result ? result.strategy : :One
    "CollegeStrategy#{result}".constantize.new(priority_wish).analyze
  end

  def self.analyze_priority(priority_wish)
    assert wish.is_a? PriorityWish
    result=Analyze.find_province_cond(priority_wish.user.province, priority_wish.priority_code, priority_wish.segment)
    result = result ? result.strategy : :One
    "PriorityStrategy#{result}".constantize.new(priority_wish).analyze
  end


  def self.province_cond
    @province_cond||=ProvinceCond.parse(JudgeCond)
  end

  def self.find_province(pinyin)
    pinyin,id=pinyin.split("_")
    province=id ? Array(Province.find(id)) : Province.find_all_by_pinyin(pinyin.to_s)
    assert province.size==1
    province.first
  end

  class ProvinceCond < Struct.new(:strategy,:province_id, :options)
    def self.parse(hash)
      result=[]
      hash.each do|element|
        element.last.each{|e|
          if e.is_a? Hash 
            result+=e.map{|j|self.new(element.first, Analyze.find_province(j.first.to_s).id, j.last)}
          else
            assert e.is_a? Symbol
            result << self.new(element.first,Analyze.find_province(e.to_s).id,nil)
          end
        }
      end
      result.sort_by(&:sort_factor)
    end

    def accept?(province,priority_code,segment)
      return false if province.id!=province_id
      return false if segment&&!options.is_a?(Hash)
      priority_id = priority_code-10
      if options.is_a? Hash
        v=options[priority_id]
        return false if v.nil?
        s=v.split(",")
        s.first=="all"||s.include?(segment)
      elsif options
        Array(options).include? priority_id
      else
        true
      end
    end

    def sort_factor
      2-(options ? (options.is_a?(Hash) ? 2 : 1) :  0)
    end
  end



  class PriorityStrategyBase

    attr_reader :priority_wish

    def initialize(priority_wish)
      @priority_wish = priority_wish
    end


    def analyze(college_wishes)
      #1.1 input
      return t 'not_ok' unless priority_wish.over_control_score? 
      return t 'input_1' if college_wishes[0..1].any?{|e|e.nil?}

      input_count = college_wishes.count{|e|!e.nil?}

      return t('input_2',:all=>college_wishes.size,:input=>input_count) if college_wishes.size - input_count >=2 

      #1.2 risk 
      risk= case college_wishes[0..1].map(&:recruit_possibility).max
            when 0...0.3 then t('risk_1')
            when 0.3..0.6 then t('risk_2')
            else
              safeguard? ? nil : t('risk_3')
            end

      #1.3 order
      order = t('order', :order => college_wishes.compact.sort_by(&:recruit_possibility).map{|e|e.college.name}.join(','))

      [risk,order,t('sum')] 
    end

    protected

    def college_wishes
      @priority_wish.college_wishes
    end

    def t(key,hash={})
      I18n.t("strategy_1.priority.#{key}", hash.merge(:default => key.to_sym, :scope=>"analyze"))
    end

  end

  class PriorityStrategyOne < PriorityStrategyBase

  end

  class PriorityStrategyTwo < PriorityStrategyBase

  end

  class PriorityStrategyThree < PriorityStrategyBase

  end

  class CollegeStrategyBase
    def analyze(wish_majors)
      assert wish_majors.size>1
      return t('input_1') if wish_majors[0..1].any?{|e|e.nil?}

      risk = major_adjustable? ? nil : t('risk_ajust')

      order = [order_reasonable? ? t('order_ok') : t('order_not_ok')]
      major_names = wish_majors.compact.sort_by(&:recruit_possibility).map{|e|e.major.name}.join(',')
      order << t('order', :order=>major_names)
      [risk,order,t('sum')] 
    end

    def order_reasonable?
      !wish_majors.any?{|e|e.position>1&&e.recruit_possibility > wish_majors[e.position-2].recruit_possibility}
    end

    def t(key,hash={})
      I18n.t("strategy_1.college.#{key}", hash.merge(:default => key.to_sym, :scope=>"analyze"))
    end
  end

  class CollegeStrategyOne < CollegeStrategyBase
  end

  class CollegeStrategyTwo < CollegeStrategyBase
  end

  class CollegeStrategyThree < CollegeStrategyBase
  end
end



测试代码:

require "#{File.dirname(__FILE__)}/../test_helper"
class AnalyzeTest < ActiveSupport::TestCase

  def test_province_segements
    assert_equal [], Analyze.province_segments(Province.find_by_pinyin('anhui') ,11)
    assert_equal [], Analyze.province_segments(Province.find_by_pinyin('sichuan') ,11)
    assert_equal ["A","B"], Analyze.province_segments(Province.find_by_pinyin('jilin'),11)
    assert_equal ["A","B"], Analyze.province_segments(Province.find_by_pinyin('jilin'),12)
    assert_equal [], Analyze.province_segments(Province.find_by_pinyin('jilin'),13)
  end

  def test_find_province_cond
    assert_equal :One, find_strategy('anhui',11,nil)
    assert_equal nil, find_strategy('jilin',11,nil)
    assert_equal :One, find_strategy('jilin',11,'A')
    assert_equal :Three, find_strategy('jilin',11,'B')
    assert_equal :Three, find_strategy('heilongjiang',11,nil)
    assert_equal :One, find_strategy('guangxi',11,nil)
    assert_equal :One, find_strategy('guangxi',12,nil)
    assert_equal :Three, find_strategy('guangxi',12,"Y")
    assert_equal :Two, find_strategy('chongqing',11,nil)

    assert_equal :One, find_strategy('guizhou',11,nil)
    assert_equal :Two, find_strategy('guizhou',12,nil)
    assert_equal :Two, find_strategy('guizhou',13,nil)

    assert_equal :Two, find_strategy('shanxi_20',11,nil)
    assert_equal nil, find_strategy('shanxi_8',11,nil)

    assert_equal nil, find_strategy('guizhou',13,"A")
  end

  private
  def find_strategy(pinyin,code=nil,segment=nil)
    s=Analyze.find_province_cond(Analyze.find_province(pinyin),code,segment)
    s ? s.strategy : nil
  end
end


分享到:
评论
6 楼 winteen 2009-06-04  
dazuiba 写道
@winteen

你的理解力很棒!

我的本意是总结一下自己平时工作时的主要工作步骤:
分析需求 => 建立模型 => 编码 => 测试代码

我相信很多朋友分析一个问题,应该不外乎这几个步骤。


@下一站,火星

是啊,ruby中很多问题都可以绕开经典的设计模式来解决。最近一年来一直这么剑走偏锋,反正能够解决实际问题---更快地。

但是带来的一个不好的影响:代码可读性很差,写着写着就成了面条,不如优雅的设计来的干净。



1.分析需求 => 建立模型 => 编码 => 测试代码
这个过程应该是这样的
分析需求 => 建立模型 => 编码 => 测试代码 =>分析需求 => Repeat...
而这个过程中你的问题是:需求分析投入不够多或者抽象能力不够或者经验不够 导致你建立出来的模型是不准确的 似乎你将写代码实现一个模型当成分析需求的工具(而不是正统的实现),建议你使用更"快"的方式帮助你分析需求(比如纸上画图,写个用例?).

2.ruby 经典设计模式
经典设计模式中有很一部分的存在(或某个模式的实现的部分考虑)是为了解决静态语言本身不足而出现的 并不完全适合ruby,而设计模式背后的软件工程原则是适合ruby的.
另外ruby本身是一个DSL能力非常强的语言 可以参考这个 http://www.infoq.com/presentations/vanderburg-state-of-dsl-ruby
5 楼 花花公子 2009-06-02  
dazuiba 写道
@winteen

但是带来的一个不好的影响:代码可读性很差,写着写着就成了面条,不如优雅的设计来的干净。


期待《重构》的ruby版本来解决你的问题了
4 楼 dazuiba 2009-06-01  
@winteen

你的理解力很棒!

我的本意是总结一下自己平时工作时的主要工作步骤:
分析需求 => 建立模型 => 编码 => 测试代码

我相信很多朋友分析一个问题,应该不外乎这几个步骤。


@下一站,火星

是啊,ruby中很多问题都可以绕开经典的设计模式来解决。最近一年来一直这么剑走偏锋,反正能够解决实际问题---更快地。

但是带来的一个不好的影响:代码可读性很差,写着写着就成了面条,不如优雅的设计来的干净。

3 楼 下一站,火星 2009-06-01  
dazuiba 写道
还是沙发舒服。
Ruby版好久没好帖了。最近经济不景气,失业中。把私活中的东西翻出来嚼一嚼,也不枉费混javaeye这么多年。

PS:
可能是我比较自恋,看到这么好看的代码,就跟看簧片一样,直流口水啊。

上次去一家公司面试,人家问我,说一下设计模式在ruby项目中的应用。这就把我给问蒙了。3年前搞的东西,现在还真记不起来了。不过话又说回来,Ruby中我喜欢短小紧凑的代码,浓缩而又有内涵。设计模式的堆彻,只适合在java代码中。再说回来,上面例子中,其实里面有:访问者模式,抽象工厂模式,策略模式。。。。如果我硬说的话。


你去空海面试的吧?
我也看不出ruby和设计模式有啥很明显的关系,MVC模式算不算一个大模式?还有model继承是不是用的策略模式?act_as_**算不算装饰模式?
2 楼 winteen 2009-06-01  
(由于楼主给出的信息量不够)
根据猜测,得出以下:
假设[省份]总数为P,[专业(学校)](这里学校的概念可以被弱化 可以看成一个专业组)总数为M,批次总数为B,分段总数为S
则维护这样一个映射:f:province, major, batch, segment => score;(某省份在某批次某分段下在某专业的录取分数)
可以用一个大数组实现 S[P][M][B][S](存在大量空值 表示不参与)
然后(根据楼主所给信息)存在这一一个函数 get_possibility(student_score, target_score)
以下是需求:
<1>按照批次
首先 学生(叫他学生A)意味着省份确定了 批次又给定了 那么抽取这样一个子集
  S_BY_BATCH_FOR_STUDENT[M][S]
进一步计算概率得到 TARGET_POSSIBILITY[M][S] = get_possibility(A.score, S_BY_BATCH_FOR_STUDENT[M][S])
即学生A在s分段对专业m的录取概率(如果学生A的分数影响他能参与的分段,则处理方式同省份,可能是一个分数可以参与多个但不是所有分段)
有了概率后 根据招生策略(所谓大平行等)可以向用户提供以下服务:
(1)按照概率对专业/分段排序
(2)学生给出所填志愿 计算出录取概率
(3)学生给出部分志愿 计算出最大录取概率(搭配一些容易录取的学校保底)
(4)等等等
<2>按照学校
  无非是一个专业的组合
  分析同上 略
(据我在百度上搜索所谓大平行 小平行 顺序 在概率求值上 似乎没有差别)
不清楚楼主建立出来的所谓模型有什么意义
期待楼主补充信息
1 楼 dazuiba 2009-06-01  
还是沙发舒服。
Ruby版好久没好帖了。最近经济不景气,失业中。把私活中的东西翻出来嚼一嚼,也不枉费混javaeye这么多年。

PS:
可能是我比较自恋,看到这么好看的代码,就跟看簧片一样,直流口水啊。

上次去一家公司面试,人家问我,说一下设计模式在ruby项目中的应用。这就把我给问蒙了。3年前搞的东西,现在还真记不起来了。不过话又说回来,Ruby中我喜欢短小紧凑的代码,浓缩而又有内涵。设计模式的堆彻,只适合在java代码中。再说回来,上面例子中,其实里面有:访问者模式,抽象工厂模式,策略模式。。。。如果我硬说的话。

相关推荐

    需求分析建模介绍的课件

    需求分析建模是软件开发过程中的关键步骤,它旨在理解和表达用户的需求,为后续的设计、编码和测试等活动提供基础。本课件详细介绍了如何进行需求建模,主要分为功能建模、数据建模和行为建模三个部分。 1. 功能...

    需求分析文档--许多项目的需求分析集合

    需求分析是软件开发过程中的关键步骤,它定义了软件或系统的目标和预期功能,为后续的设计、编码和测试提供基础。这份"需求分析文档--许多项目的需求分析集合"压缩包包含的是不同项目的需求分析文档,提供了丰富的...

    软件项目需求分析文档模板

    良好的需求分析不仅有助于确保软件项目的方向正确无误,还能在后续的设计、编码、测试等阶段提供明确的指导,从而提高开发效率,减少返工,最终交付满足用户需求的高质量软件。 根据提供的文件信息,一份完整的软件...

    入门软件需求分析与建模

    在软件开发过程中,入门软件需求分析与建模是至关重要的第一步。需求分析是软件生命周期中的核心环节,它在问题定义和可行性研究之后进行,目的是确保软件团队与用户之间对系统需求有清晰、一致的理解,避免因沟通...

    软件需求分析与建模基础.ppt

    【软件需求分析与建模基础】是软件开发过程中的核心环节,它关乎项目的成功与否。在软件生命周期(SDLC)中,需求分析是软件开发的起点,定义了软件产品的目标和预期功能。这一过程包括了对软件功能和性能的详细理解...

    不错的需求分析资料,共8个部分

    在IT行业中,需求分析是软件开发过程中的关键环节,它为整个项目的成功奠定了基础。这份“不错的需求分析资料”显然是一份系统性、全面性的学习材料,由信息产业部计算机技术培训中心提供,旨在帮助学员深入理解和...

    UML建模的一般过程

    业务过程建模是UML建模中的一个重要组成部分,主要关注于描述和分析业务流程,以便改进业务效率和效果。UML2.x标准提供了丰富的行为模型,其中包括业务过程建模标注(BPMN)和Eriksson-Penker Profile,它们都是业务...

    软件需求分析国家标准

    《软件需求分析国家标准》是指导软件开发过程中的一个重要标准,旨在规范需求获取、分析、定义、验证和管理的流程,确保软件项目能够满足用户和业务的实际需求。这一国家标准的实施对于提升软件质量、减少开发成本、...

    软件需求分析标准

    《软件需求分析标准》是软件开发过程中的关键环节,它为整个项目的成功奠定了坚实的基础。需求分析不仅是理解用户期望的过程,也是确保项目团队对目标有共同理解的关键步骤。以下是对这个主题的详细阐述: 1. **...

    需求分析过程、方法和实践

    需求分析是软件开发过程中的关键步骤,它定义了项目的初始目标和预期功能,为后续的设计、编码和测试奠定了基础。本文将深入探讨需求分析的过程、方法和实践,旨在揭示各个环节中可能遇到的问题及其解决策略。 一、...

    软件需求分析经典教材

    需求建模是需求分析的另一个重要环节,它通过可视化的方式帮助开发团队理解和验证需求。在这一过程中,UML(统一建模语言)是常用的工具。UML包括用例图、活动图、序列图等,这些图可以有效地表达系统的行为和动态...

    软件工程 需求分析 案例

    在软件开发过程中,需求分析是至关重要的第一步,它定义了软件系统的核心功能和特性,为后续的设计、编码和测试提供明确的方向。本案例将详细阐述如何进行需求分析,以PDF文档的形式呈现,文件名为"Demands06-10",...

    软件需求分析学习资料(含一个示例)

    在软件开发过程中,需求分析是至关重要的第一步,它定义了项目的范围、目标和预期功能,为后续的设计、编码和测试奠定基础。这份“软件需求分析学习资料”包含了一个实际的示例,帮助学习者深入理解需求分析的过程和...

    软件开发管理规范(调研、需求分析、设计、编码、测试、部署、测试、维护等过程).pdf

    软件开发管理规范是指软件开发过程中的管理规范,包括调研、需求分析、设计、编码、测试、部署、测试、维护等过程。下面是软件开发管理规范的详细知识点: 一、初始阶段: 1.1 项目约定书(Word):明确甲乙双方...

    需求分析与系统设计 第3版

    在软件开发过程中,需求分析是项目成功的基础,而系统设计则是将需求转化为实际系统的蓝图。下面,我们将深入探讨这两个重要环节的关键知识点。 **需求分析** 1. **需求获取**:需求分析的第一步是与利益相关者...

    软件需求图形建模.docx

    在软件开发过程中,需求分析是至关重要的第一步,它为后续的设计、编码和测试提供清晰的指导。在这个阶段,图形建模是一种有效的工具,能够帮助我们可视化地表达需求,提高沟通效率,减少误解。以下是几种常见的软件...

    软件需求分析课件

    在软件开发过程中,需求分析是至关重要的第一步,它定义了项目的范围、目标和预期功能,为后续的设计、编码和测试奠定基础。李老师的"软件需求分析课件"是一份宝贵的教育资源,包含英文PPT和参考书PDF,旨在帮助学习...

    软件工程项目之需求分析.doc

    总结来说,软件工程中的需求分析是一个系统化的过程,它涉及需求的识别、理解和确认,是软件开发成功的关键。通过有效的管理和控制,可以确保软件项目能够满足用户的真实需求,提高项目的成功率。在面对日益复杂的...

    2021-2022年收藏的精品资料第二部分软件需求分析与建模57.ppt

    《软件需求分析与建模》精品资料主要涵盖了软件开发中至关重要的需求分析阶段,这是确保项目成功的关键步骤。本资料详细介绍了软件需求的定义、分析过程、启动方法以及建模技术,特别是面向数据和面向数据流的建模,...

Global site tag (gtag.js) - Google Analytics