65.9K
CodeProject 正在变化。 阅读更多。
Home

通过向现有代码添加方法来逐步现代化 COBOL

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2010 年 5 月 28 日

CC (ASA 2.5)

5分钟阅读

viewsIcon

10321

现代化过程式 COBOL 可以分小步进行,每一步都能带来一些不同。

背景

写得好的代码应该看起来很优雅。在这里,我正在寻找一种优雅的方式,可以轻松地将遗留的基于段落/章节的 COBOL 迁移到使用方法,并获得所有相关的好处。

在讨论 COBOL 时,我从真正的 COBOL 程序员那里听到的最常提到的第一大缺点(与 C、C# 和 Java 等相比)是缺乏局部数据。编写大型 COBOL 程序时,由于程序的所有部分共享同一个工作存储区,因此要弄清楚发生了什么变得越来越困难。可以使用局部存储,但 COBOL 程序中自然细分单元是段落或章节(取决于内部风格)。编写一个包含数百甚至数千个微小、独立程序的系统是一种糟糕的风格,并且很可能导致性能不佳。这意味着工作存储区变得巨大、复杂且难以管理。

Micro Focus 的 Managed COBOL 针对此问题提供了完整的解决方案。它以一种非常直观的方式支持类、对象和方法。

但是,如果我们不想一次性完全转向面向对象编程会怎样?如果我们只想摆脱全局存储并逐步现代化会怎样?

虽然这是一个庞大而引人入胜的主题,但我的目标只是迈出第一步,展示一些技巧和想法。也许这可以作为更大讨论的起点,或者我将来可能会再次探讨这个主题。

可以一次一小步地利用 Managed COBOL 语法。

方法比执行范围甚至调用提供了许多优势。但是,我现在不想对此大肆宣道。我只想展示第一步——从纯过程式程序迁移到过程和类的混合体。为此,让我们来看一个示例 COBOL 过程式程序。我写了一个,它故意写得不太好。我们可以想象这个程序是某人在几分钟内写出来的,然后又被另一个不太了解其功能的人扩展了几次。

我们可以从标题注释中大致了解程序的含义及其历史。

********************************************
  * Yield table creator
  * ===================
  *
  * This program creates a yield table to show the
  * yield from our investment policy type Qr24.
  * It does this using the old computational system and
  * the new one as mandated by reg 123456 ordinance 988765.
  * Author: A.N. Other (Retired)
  *
  * Altered to produce several tables:
  * Author: A. Newbie
  ********************************************

此外,我们可以看到 A. Newbie 使用了“perform through”风格,而 A.N. Other 则没有。这导致了编程风格的混合和不可避免的混乱。为了让我的例子成为一个有故事性的例子,我们需要假设这是一个需要数天才能弄清楚的庞大程序。但是——我们不想花几天时间来写这个帖子,所以短小的例子和一点想象力就足够了。

  ********************************************
  * Yield table creator
  * ===================
  * 
  * This program creates a yield table to show the 
  * yield from our investment policy type Qr24.
  * It does this using the old computational system and
  * the new one as mandated by reg 123456 ordinance 988765.
  * Author: A.N. Other Retired-Programmer
  *
  * Altered to produce several tables:
  * Author: A. Newbie
  ********************************************
  
   program-id. yield-table-creator.
   
   working-storage section.
   01 money-info.
     03 yield-old        pic 9(9)v99 comp.
     03 yield-new        pic 9(9)v99 comp.
     03 money            pic 9(9).99.
     
   01 calc-internals.
     03 n-current-balance pic 9(9)v9(7)  comp.
     03 n-interest-c      pic 9(9)v9(7)  comp.
     03 n-intermediate    pic 9(9)v9(7)  comp.
     03 o-current-balance pic 9(9)v9(5)  comp.
     03 o-interest-c      pic 9(9)v9(5)  comp.
     03 o-intermediate    pic 9(9)v9(5)  comp.
     03 calc-day          pic 9(9)       comp.
         
   01 yield-info.         
     03 start-balance     pic 9(9)v9(2)  comp.
     03 yield             pic 9(9)v9(2)  comp.
     03 interest          pic     v9(4)  comp.
     03 days              pic 9(9)       comp.
     03 years             pic 9(2)       comp.
     
   01 calc-info.
     03 start-at          pic 9(9)v9(2)  comp.
     03 end-at            pic 9(9)v9(2)  comp.
     03 step-by           pic 9(9)v9(2)  comp.
      
   procedure division.
   
       perform table-01 through table-04
       display "---== END RUN ==---"
       goback.
   
   table-01 section.
       move 0.0625 to interest
       move 9132   to days
       move 25     to years
       move 1      to start-at
       move 100    to end-at
       move 1      to step-by
       perform make-table
       .
   table-02 section.
       move 0.05   to interest
       move 9132   to days
       move 25     to years
       move 1      to start-at
       move 100    to end-at
       move 1      to step-by
       perform make-table
       .
   table-03 section.
       move 0.0625 to interest
       move 9132   to days
       move 25     to years
       move 100000 to start-at
       move 110000 to end-at
       move 100    to step-by
       perform make-table
       .
   table-04 section.
       move 0.05   to interest
       move 9132   to days
       move 25     to years
       move 100000 to start-at
       move 110000 to end-at
       move 100    to step-by
       perform make-table
       .
       
   make-table section.
       display "Conversion for:"
       multiply interest by 100 giving money
       display "Interest= " money "%"
       display "Days    = " days
       display "Years   = " years
       move start-at to money
       display "Start at= " money
       move end-at   to money
       display "End at  = " money
       move step-by  to money
       display "Step by = " money
       
       display "+--------------+--------------+--------------+--------------+"
       
       perform varying start-balance from start-at by step-by
                            until start-balance = end-at
          
           perform comp-yield-old
           move yield to yield-old
           
           perform comp-yield-new
           move yield to yield-new
           
           if yield-new not = yield-old 
                   move start-balance to money
                   display "| " money " | " with no advancing
                   move yield-old to money
                   display money " | " with no advancing
                   move yield-new to money 
                   display money " | " with no advancing
                   compute money = yield-old - yield-new
                   display money " |"
           end-if
               
       end-perform
       
       display "+--------------+--------------+--------------+--------------+"
       display " "
       .
       
   comp-yield-new section.
       move    start-balance  to n-current-balance
       move    interest       to n-interest-c
       compute n-interest-c   rounded = (n-interest-c * years) / days
       compute n-intermediate rounded = n-interest-c
       move    n-intermediate to n-interest-c
       move    days to calc-day
       
       perform varying calc-day from 1 by 1 until calc-day greater days
           
           compute n-intermediate rounded
                   = n-current-balance + (n-current-balance * n-interest-c) 
           move    n-intermediate to n-current-balance
       
       end-perform
       move n-current-balance to yield
       .
       
   comp-yield-old section.
       move    start-balance  to o-current-balance
       move    interest       to o-interest-c
       compute o-interest-c   rounded = (o-interest-c * years) / days
       compute o-intermediate rounded = o-interest-c
       move    o-intermediate to o-interest-c
       move    days to calc-day
       
       perform varying calc-day from 1 by 1 until calc-day greater days
           
           compute o-intermediate rounded
                   = o-current-balance + (o-current-balance * o-interest-c) 
           move    o-intermediate to o-current-balance
   
       end-perform
       move o-current-balance to yield
       .
       
   end program yield-table-creator.

这会产生如下输出:

Conversion for:
Interest= 000000006.25%
Days    = 000009132
Years   = 25
Start at= 000000001.00
End at  = 000000100.00
Step by = 000000001.00
+--------------+--------------+--------------+--------------+
| 000000001.00 | 000000004.72 | 000000004.77 | 000000000.05 |
| 000000002.00 | 000000009.44 | 000000009.54 | 000000000.10 |
| 000000003.00 | 000000014.16 | 000000014.31 | 000000000.15 |
| 000000004.00 | 000000018.88 | 000000019.08 | 000000000.20 |
| 000000005.00 | 000000023.61 | 000000023.85 | 000000000.24 |
| 000000006.00 | 000000028.33 | 000000028.62 | 000000000.29 |
| 000000007.00 | 000000033.05 | 000000033.39 | 000000000.34 |
| 000000008.00 | 000000037.77 | 000000038.16 | 000000000.39 |
| 000000009.00 | 000000042.50 | 000000042.93 | 000000000.43 |
| 000000010.00 | 000000047.22 | 000000047.70 | 000000000.48 |
| 000000011.00 | 000000051.94 | 000000052.47 | 000000000.53 |
| 000000012.00 | 000000056.66 | 000000057.24 | 000000000.58 |
| 000000013.00 | 000000061.39 | 000000062.01 | 000000000.62 |
| 000000014.00 | 000000066.11 | 000000066.78 | 000000000.67 |
| 000000015.00 | 000000070.83 | 000000071.55 | 000000000.72 |
| 000000016.00 | 000000075.55 | 000000076.32 | 000000000.77 |
| 000000017.00 | 000000080.27 | 000000081.09 | 000000000.82 |

进行更改——方法来救援!

为了继续我们的故事,我们接到的任务是更新新算法,因为我们发现“reg 123456 ordinance 988765”要求每第 7 天不支付利息。这是解决方案:

   comp-yield-new section.
       move    start-balance  to n-current-balance
       move    interest       to n-interest-c
       compute n-interest-c   rounded = (n-interest-c * years) / days
       compute n-intermediate rounded = n-interest-c
       move    n-intermediate to n-interest-c
       move    days to calc-day
       move    1 to day-flagger
       
       perform varying calc-day from 1 by 1 until calc-day greater days
           if day-flagger = 7 
               move 1 to day flagger
           else
               compute n-intermediate rounded
                       = n-current-balance + (n-current-balance * n-interest-c) 
               move    n-intermediate to n-current-balance
               add 1 to day-flagger
           end-if
       end-perform
       move n-current-balance to yield           .

我在算法中加入了“day-flagger”。但问题是我现在必须去工作存储区定义它。在实际程序中——该工作存储区可能定义在 copy book 中;我应该将 day-flagger 添加到那个 copy book 中,还是通过将其添加到代码源文件中来弄乱该程序的格式?这只是一个简单的例子,在一个庞大的程序中,不得不反复回到工作存储区并添加内容会很麻烦,并且即使添加的项目没有被使用,也会增加程序的内存占用。

解决方案是现在付出一点努力,以便将来获得很多收益。解决方案是将 comp-yield-new 的核心部分移到一个方法中,并使用“invoke”来进行计算。

       comp-yield-new section.
           invoke comp-class::comp-yield-new(yield-info)

这是 com-class 和其中的 comp-yield-new 方法的代码。

   class-id comp-class public.
   
   method-id comp-yield-new public static.
   
   local-storage section.
   01 calc-internals.
     03 n-current-balance pic 9(9)v9(7)  comp.
     03 n-interest-c      pic 9(9)v9(7)  comp.
     03 n-intermediate    pic 9(9)v9(7)  comp.
     03 calc-day          pic 9(9)       comp.
     
   linkage section.
   01 yield-info.         
     03 start-balance     pic 9(9)v9(2)  comp.
     03 yield             pic 9(9)v9(2)  comp.
     03 interest          pic     v9(4)  comp.
     03 days              pic 9(9)       comp.
     03 years             pic 9(2)       comp.
     
   procedure division using yield-info.
       move    start-balance  to n-current-balance
       move    interest       to n-interest-c
       compute n-interest-c   rounded = (n-interest-c * years) / days
       compute n-intermediate rounded = n-interest-c
       move    n-intermediate to n-interest-c
       move    days to calc-day
       
       perform varying calc-day from 1 by 1 until calc-day greater days
           
           compute n-intermediate rounded
                   = n-current-balance + (n-current-balance * n-interest-c) 
           move    n-intermediate to n-current-balance
   
       end-perform
       move n-current-balance to yield
   end method.
   
   end class.

这实际上只是从 perform 中复制粘贴的代码,所以我只付出了最少量的努力。我可以将 'yield-info' 组放入一个 copy 文件中,这样我就不必在 linkage section 中付出任何努力了!

   linkage section.
       copy "yield-info.cpy" 

为了证明程序的行为完全没有改变,这是输出的截图:

comp-class 和 comp-yield-new 方法都被标记为 `public`,以便可以从任何地方访问它们。更复杂的 OO 风格,如 `protected` 和 `private` 成员,在现代化早期阶段不是必需的。还值得注意的是,comp-yield-new 方法被标记为 `static`。这意味着它的调用方式类似于 COBOL 入点。如果它不是 `static`,我们就需要创建一个 comp-class 实例并在该实例上调用方法。这是另一种更复杂的 OO 风格,我们在这个阶段不需要考虑。

现在我们得到回报了!

好的,我们付出了(少量的)努力将我们的章节(或段落)变成了一个方法。现在我们必须添加这个 7 天规则。****——我们不需要修改主程序的 working-storage,甚至不需要修改类;我们可以处理方法中的 local-storage!

   method-id comp-yield-new public static.
   
   local-storage section.
   01 calc-internals.
     03 n-current-balance pic 9(9)v9(7)  comp.
     03 n-interest-c      pic 9(9)v9(7)  comp.
     03 n-intermediate    pic 9(9)v9(7)  comp.
     03 calc-day          pic 9(9)       comp.
     
   01 day-flagger         pic 9.
     
   linkage section.
       copy "yield-info.cpy"
       
   procedure division using yield-info.
       move    start-balance  to n-current-balance
       move    interest       to n-interest-c
       compute n-interest-c   rounded = (n-interest-c * years) / days
       compute n-intermediate rounded = n-interest-c
       move    n-intermediate to n-interest-c
       move    days to calc-day
       move    1 to day-flagger
       
       perform varying calc-day from 1 by 1 until calc-day greater days
           if day-flagger = 7 
               move 1 to day flagger
           else
               compute n-intermediate rounded
                       = n-current-balance + (n-current-balance * n-interest-c) 
               move    n-intermediate to n-current-balance
               add 1 to day-flagger
           end-if
       end-perform
       move n-current-balance to yield
   end method.

COBOL 最大的问题就这么解决了!

我希望我已经展示了从过程式 COBOL 过渡到 Managed OO COBOL 可以是多么温和、安全和轻松。现代化能够立即带来实际改变也同样非常重要。

一旦掌握了逐步现代化过程式 COBOL 的技术,所有锁定在 COBOL 程序中的知识产权都可以以新的令人兴奋的方式重新利用。许多小步骤带来巨大的回报。

© . All rights reserved.