重构 改善既有代码的设计(txt+pdf+epub+mobi电子书下载)


发布时间:2020-10-01 10:40:29

点击下载

作者:[美]马丁·福勒(Martin Fowler)

出版社:信息技术第一出版分社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

重构 改善既有代码的设计

重构 改善既有代码的设计试读:

前言

从前,有位咨询顾问造访客户调研其开发项目。系统核心是个类继承体系,顾问看了开发人员所写的一些代码。他发现整个体系相当凌乱,上层超类对于系统的运作做了一些假设,下层子类实现这些假设。但是这些假设并不适合所有子类,导致覆写(override)工作非常繁重。只要在超类做点修改,就可以减少许多覆写工作。在另一些地方,超类的某些意图并未被良好理解,因此其中某些行为在子类内重复出现。还有一些地方,好几个子类做相同的事情,其实可以把它们搬到继承体系的上层去做。

这位顾问于是建议项目经理看看这些代码,把它们整理一下,但是经理并不热衷于此,毕竟程序看上去还可以运行,而且项目面临很大的进度压力。于是经理说,晚些时候再抽时间做这些整理工作。

顾问也把他的想法告诉了在这个继承体系上工作的程序员,告诉他们可能发生的事情。程序员都很敏锐,马上就看出问题的严重性。他们知道这并不全是他们的错,有时候的确需要借助外力才能发现问题。程序员立刻用了一两天的时间整理好这个继承体系,并删掉了其中一半代码,功能毫发无损。他们对此十分满意,而且发现在继承体系中加入新的类或使用系统中的其他类都更快、更容易了。

项目经理并不高兴。进度排得很紧,有许多工作要做。系统必须在几个月之后发布,而这些程序员却白白耗费了两天时间,干的工作与要交付的多数功能毫无关系。原先的代码运行起来还算正常,他们的新设计看来有点过于追求完美。项目要交付给客户的,是可以有效运行的代码,不是用以取悦学究的完美东西。顾问接下来又建议应该在系统的其他核心部分进行这样的整理工作,这会使整个项目停顿一至二个星期。所有这些工作只是为了让代码看起来更漂亮,并不能给系统添加任何新功能。

你对这个故事有什么感想?你认为这个顾问的建议(更进一步整理程序)是对的吗?你会遵循那句古老的工程谚语吗:“如果它还可以运行,就不要动它。”

我必须承认自己有某些偏见,因为我就是那个顾问。六个月之后这个项目宣告失败,很大的原因是代码太复杂,无法调试,也无法获得可被接受的性能。

后来,项目重新启动,几乎从头开始编写整个系统,Kent Beck受邀做了顾问。他做了几件迥异以往的事,其中最重要的一件就是坚持以持续不断的重构行为来整理代码。这个项目的成功,以及重构在这个成功项目中扮演的角色,启发了我写这本书,如此一来我就能够把Kent和其他一些人已经学会的“以重构方式改进软件质量”的知识,传播给所有读者。

什么是重构

所谓重构(refactoring)是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。重构是一种经千锤百炼形成的有条不紊的程序整理方法,可以最大限度地减少整理过程中引入错误的几率。本质上说,重构就是在代码写好之后改进它的设计。“在代码写好之后改进它的设计”?这种说法有点奇怪。按照目前对软件开发的理解,我们相信应该先设计而后编码:首先得有一个良好的设计,然后才能开始编码。但是,随着时间流逝,人们不断修改代码,于是根据原先设计所得的系统,整体结构逐渐衰弱。代码质量慢慢沉沦,编码工作从严谨的工程堕落为胡砍乱劈的随性行为。“重构”正好与此相反。哪怕你手上有一个糟糕的设计,甚至是一堆混乱的代码,你也可以借由重构将它加工成设计良好的代码。重构的每个步骤都很简单,甚至显得有些过于简单:你只需要把某个字段从一个类移到另一个类,把某些代码从一个函数拉出来构成另一个函数,或是在继承体系中把某些代码推上推下就行了。但是,聚沙成塔,这些小小的修改累积起来就可以根本改善设计质量。这和一般常见的“软件会慢慢腐烂”的观点恰恰相反。

通过重构,你可以找出改变的平衡点。你会发现所谓设计不再是一切动作的前提,而是在整个开发过程中逐渐浮现出来。在系统构筑过程中,你可以学习如何强化设计,其间带来的互动可以让一个程序在开发过程中持续保有良好的设计。

本书有什么

本书是一本为专业程序员而写的重构指南。我的目的是告诉你如何以一种可控制且高效率的方式进行重构。你将学会如何有条不紊地改进程序结构,而且不会引入错误,这就是正确的重构方式。

按照传统,图书应该以引言开头。尽管我也同意这个原则,但是我发现以概括性的讨论或定义来介绍重构,实在不是件容易的事。所以我决定用一个实例做为开路先锋。第1章展示了一个小程序,其中有些常见的设计缺陷,我把它重构为更合格的面向对象程序。其间我们可以看到重构的过程,以及几个很有用的重构手法。如果你想知道重构到底是怎么回事,这一章不可不读。

第2章讨论重构的一般性原则、定义,以及进行重构的原因,我也大致介绍了重构所存在的一些问题。第3章由Kent Beck介绍如何嗅出代码中的“坏味道”,以及如何运用重构清除这些坏味道。测试在重构中扮演着非常重要的角色,第4章介绍如何运用一个简单而且开源的Java测试框架,在代码中构筑测试环境。

本书的核心部分——重构列表——从第5章延伸至第12章。它不能说是一份全面的列表,只是一个起步,其中包括迄今为止我在工作中整理下来的所有重构手法。每当我想做点什么(例如Replace Conditional with Polymorphism (255))的时候,这份列表就会提醒我如何一步一步安全前进。我希望这是值得你日后一再回顾的部分。

本书介绍了其他人的许多研究成果,最后几章就是由他们之中的几位所客串写就的。Bill Opdyke在第13章记述他将重构技术应用于商业开发过程中遇到的一些问题。Don Roberts和John Brant在第14章展望重构技术的未来——自动化工具。我把最后一章(第15章)留给重构技术的顶尖大师Kent Beck来压轴。

在Java中运用重构

本书范例全部使用Java撰写。重构当然也可以在其他语言中实现,而且我也希望这本书能够给其他语言使用者带来帮助。但我觉得我最好在本书中只使用Java,因为那是我最熟悉的语言。我会不时写下一些提示,告诉读者如何在其他语言中进行重构,不过我真心希望看到其他人在本书基础上针对其他语言写出更多重构方面的书籍。

为了很好地与读者交流我的想法,我没有使用Java语言中特别复杂的部分。所以我避免使用内嵌类、反射机制、线程以及很多强大的Java特性。这是因为我希望尽可能清楚地展现重构的核心。

我应该提醒你,这些重构手法并不针对并发或分布式编程。那些主题会引出更多的考虑,本书并未涉及。

谁该阅读本书

本书的目标读者是专业程序员,也就是那些以编写软件为生的人。书中的示例和讨论,涉及大量需要详细阅读和理解的代码。这些例子都以Java写成。之所以选择Java,因为它是一种应用范围越来越广的语言,而且任何具备C语言背景的人都可以轻易理解它。Java是一种面向对象语言,而面向对象机制对于重构有很大帮助。

尽管关注对象是代码,但重构对于系统设计也有巨大影响。资深设计师和架构师也很有必要了解重构原理,并在自己的项目中运用重构技术。最好是由老资格、经验丰富的开发人员来引入重构技术,因为这样的人最能够透彻理解重构背后的原理,并根据情况加以调整,使之适用于特定工作领域。如果你使用的不是Java,这一点尤其重要,因为你必须把我给出的范例以其他语言改写。

下面我要告诉你,如何能够在不通读全书的情况下充分用好它。

如果你想知道重构是什么,请阅读第1章,其中示例会让你清楚重构的过程。

如果你想知道为什么应该重构,请阅读前两章。它们告诉你重构是什么以及为什么应该重构。

如果你想知道该在什么地方重构,请阅读第3章。它会告诉你一些代码特征,这些特征指出“这里需要重构”。

如果你想着手进行重构,请完整阅读前四章,然后选择性地阅读重构列表。一开始只需概略浏览列表,看看其中有些什么,不必理解所有细节。一旦真正需要实施某个准则,再详细阅读它,从中获取帮助。列表部分是供查阅的参考性内容,你不必一次就把它全部读完。此外你还应该读一读列表之后其他作者的“客串章节”,特别是第15章。

站在前人的肩膀上

就在本书一开始的此时此刻,我必须说:这本书让我欠了一大笔人情债,欠那些在过去十年中做了大量研究工作并开创重构领域的人一大笔债。这本书原本应该由他们之中的某个人来写,但最后却是由我这个有时间有精力的人捡了便宜。

重构技术的两位最早倡导者是Ward Cunningham和Kent Beck。他们很早就把重构作为开发过程的一个核心成分,并且在自己的开发过程中运用它。尤其需要说明的是,正因为和Kent的合作,才让我真正看到了重构的重要性,并直接激励了我写出这本书。

Ralph Johnson在UIUC(伊利诺伊大学厄巴纳—尚佩恩分校)领导了一个小组,这个小组因其在对象技术方面的实际贡献而声名远扬。Ralph很早就是重构技术的拥护者,他的一些学生也一直在研究这个课题。Bill Opdyke的博士论文是重构研究的第一份详细的书面成果。John Brant和Don Roberts则早已不满足于写文章了,他们写了一个工具叫Refactoring Browser(重构浏览器),对Smalltalk程序实施重构工程。

致谢

尽管有这些研究成果可以借鉴,我还是需要很多协助才能写出这本书。首先,并且也是最重要的,Kent Beck给了我巨大的帮助。Kent在底特律的某个酒吧和我谈起他正在为Smalltalk Report撰写一篇论文[Beck,hanoi],从此播下本书的第一颗种子。那次谈话不但让我开始注意到重构技术,而且我还从中“偷”了许多想法放到本书第1章。Kent也在其他很多方面帮助我,想出“代码味道”这个概念的是他,当我遇到各种困难时,鼓励我的人也是他,常常和我一起工作助我完成这本书的,还是他。我常常忍不住这么想:他完全可以自己把这本书写得更好。可惜有时间写书的人是我,所以我也只能希望自己不要做得太差。

写这本书的时候,我希望能把一些专家经验直接与你分享,所以我非常感激那些花时间为本书添砖加瓦的人。Kent Beck、John Brant、William Opdyke和Don Roberts编撰或合写了本书部分章节。此外Rich Garzaniti和Ron Jeffries帮我添加了一些有用的文中注解。

在任何一本此类技术书里,作者都会告诉你,技术审阅者提供了巨大的帮助。一如既往,Addison-Wesley出版社的Carter Shanklin和他的团队组织了强大的审稿人阵容,他们是:

Ken Auer,Rolemodel软件公司

Joshua Bloch,Sun公司Java软件部

John Brant,UIUC

Scott Corley,High Voltage软件公司

Ward Cunningham,Cunningham&Cunningham公司

Stéphane Ducasse

Erich Gamma,对象技术国际公司

Ron Jeffries

Ralph Johnson,伊利诺伊大学

Joshua Kerievsky,Industrial Logic公司

Doug Lea,纽约州立大学Oswego分校

Sander Tichelaar

他们大大提高了本书的可读性和准确性,并且至少去掉了一些任何手稿都可能会藏有的错误。在此我要特别感谢两个效果显著的建议,它们让我的书看上去耳目一新:Ward和Ron建议我以重构前后效果并列对照的方式写第1章,Joshua Kerievsky建议我在重构列表中画出代码草图。

除了正式审阅小组,还有很多非正式的审阅者。这些人或看过我的手稿,或关注我的网页并留下对我很有帮助的意见。他们是Leif Bennett、Michael Feathers、Michael Finney、Neil Galarneau、Hisham Ghazouli、Tony Gould、John Isner、Brian Marick、Ralf Reissing、John Salt、Mark Swanson、Dave Thomas和Don Wells。我相信肯定还有一些被我遗忘的人,请容我在此向你们道歉,并致上我的谢意。

有一个特别有趣的审阅小组,就是“恶名昭彰”的UIUC读书小组。本书反映出他们的众多研究成果,我要特别感谢他们用录音记录的意见。这个小组成员包括Fredrico“Fred”Balaguer、John Brant、Ian Chai、Brian Foote、Alejandra Garrido、Zhijiang“John”Han、Peter Hatch、Ralph Johnson、Songyu“Raymond”Lu、Dragos-Anton Manolescu、Hiroaki Nakamura、James Overturf、Don Roberts、Chieko Shirai、Les Tyrell和Joe Yoder。

任何好想法都需要在严酷的生产环境中接受检验。我看到重构对于克莱斯勒综合薪资系统(Chrysler Comprehensive Compensation,C3)发挥了巨大的作用。我要感谢那个团队的所有成员:Ann Anderson、Ed Anderi、Ralph Beattie、Kent Beck、David Bryant、Bob Coe、Marie DeArment、Margaret Fronczak、Rich Garzaniti、Dennis Gore、Brian Hacker、Chet Hendrickson、Ron Jeffries、Doug Joppie、David Kim、Paul Kowalsky、Debbie Mueller、Tom Murasky、Richard Nutter、Adrian Pantea、Matt Saigeon、Don Thomas和Don Wells。和他们一起工作所获得的第一手数据,巩固了我对重构原理和作用的认识。他们使用重构技术所取得的进步极大程度地帮助我看到:重构技术应用于历时多年的大型项目中,可以起到何等的作用!

再提一句,我得到了Addison-Wesley出版社的J.Carter Shanklin及其团队的帮助,包括Krysia Bebick、Susan Cestone、Chuck Dutton、Kristin Erickson、John Fuller、Christopher Guzikowski、Simone Payment和Genevieve Rajewski。与优秀出版商合作是一个令人愉快的经历,他们为我提供了大量的支持和帮助。

谈到支持,为一本书付出最多的,总是距离作者最近的人。那就是现在已成为我妻子的Cindy。感谢她,当我埋首工作的时候,还是一样爱我。即使在我投入写书时,也总会不断想起她。Martin Fowler于马萨诸塞州Melrose市fowler@acm.orghttp://www.martinfowler.comhttp://www.refactoring.com第1章重构,第一个案例

我该从何说起呢?按照传统做法,一开始介绍某个东西时,首先应该大致讲讲它的历史、主要原理等等。可是每当有人在会场上介绍这些东西,总是诱发我的瞌睡虫。我的思绪开始游荡,我的眼神开始迷离,直到主讲人秀出实例,我才能够提起精神。实例之所以可以拯救我于太虚之中,因为它让我看见事情在真正进行。谈原理,很容易流于泛泛,又很难说明如何实际应用。给出一个实例,就可以帮助我把事情认识清楚。

所以我决定从一个实例说起。在此过程中我将告诉你很多重构的道理,并且让你对重构过程有一点感觉。然后我才能向你展开通常的原理介绍。

但是,面对这个介绍性实例,我遇到了一个大问题。如果我选择一个大型程序,那么对程序自身的描述和对整个重构过程的描述就太复杂了,任何读者都不忍卒读(我试了一下,哪怕稍微复杂一点的例子都会超过100页)。如果我选择一个容易理解的小程序,又恐怕看不出重构的价值。

和任何立志要介绍“应用于真实世界中的有用技术”的人一样,我陷入了一个十分典型的两难困境。我只能带引你看看如何在一个我所选择的小程序中进行重构,然而坦白说,那个程序的规模根本不值得我们那么做。但是如果我给你看的代码是大系统的一部分,重构技术很快就变得重要起来。所以请你一边观赏这个小例子,一边想象它身处于一个大得多的系统。1.1起点

实例非常简单。这是一个影片出租店用的程序,计算每一位顾客的消费金额并打印详单。操作者告诉程序:顾客租了哪些影片、租期多长,程序便根据租赁时间和影片类型算出费用。影片分为三类:普通片、儿童片和新片。除了计算费用,还要为常客计算积分,积分会根据租片种类是否为新片而有不同。

我用了几个类来表现这个例子中的元素。图1-1是一张UML类图,用以显示这些类。

我会逐一列出这些类的代码。图1-1 本例一开始的各个类。此图只显示最重要的特性。图中所用符号是UML([Fowler,UML])

Movie(影片)

Movie只是一个简单的纯数据类。

public class Movie {

public static final int CHILDRENS = 2;

public static final int REGULAR = 0;

public static final int NEW_RELEASE = 1;

private String _title;

private int _priceCode;

public Movie(String title,int priceCode){

_title = title;

_priceCode = priceCode;

}

public int getPriceCode(){

return _priceCode;

}

public void setPriceCode(int arg){

_priceCode = arg;

}

public String getTitle (){

return _title;

};

}

Rental(租赁)

Rental表示某个顾客租了一部影片。

class Rental {

private Movie _movie;

private int _daysRented;

public Rental(Movie movie,int daysRented){

_movie = movie;

_daysRented = daysRented;

}

public int getDaysRented(){

return _daysRented;

}

public Movie getMovie(){

return _movie;

}

}

Customer(顾客)

Customer类用来表示顾客。就像其他类一样,它也拥有数据和相应的访问函数:

class Customer {

private String _name;

private Vector _rentals = new Vector();

public Customer (String name){

_name = name;

};

public void addRental(Rental arg){

_rentals.addElement(arg);

}

public String getName (){

return _name;

};

Customer还提供了一个用于生成详单的函数,图1-2显示这个函数带来的交互过程。完整代码显示于下一页。图1-2 statement()的交互过程

public String statement(){

double totalAmount = 0;

int frequentRenterPoints = 0;

Enumeration rentals = _rentals.elements();

String result = "Rental Record for " + getName()+ "\n";

while (rentals.hasMoreElements()){

double thisAmount = 0;

Rental each = (Rental)rentals.nextElement();

//determine amounts for each line

switch (each.getMovie().getPriceCode()){

case Movie.REGULAR:

thisAmount += 2;

if (each.getDaysRented()> 2)

thisAmount += (each.getDaysRented()- 2)* 1.5;

break;

case Movie.NEW_RELEASE:

thisAmount += each.getDaysRented()* 3;

break;

case Movie.CHILDRENS:

thisAmount += 1.5;

if (each.getDaysRented()> 3)

thisAmount += (each.getDaysRented()- 3)* 1.5;

break;

}

// add frequent renter points

frequentRenterPoints ++;

// add bonus for a two day new release rental

if ((each.getMovie().getPriceCode()== Movie.NEW_RELEASE)&&

each.getDaysRented()> 1)frequentRenterPoints ++;

//show figures for this rental

result += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(thisAmount)+ "\n";

totalAmount += thisAmount;

}

//add footer lines

result += "Amount owed is " + String.valueOf(totalAmount)+ "\n";

result += "You earned " + String.valueOf(frequentRenterPoints)+

" frequent renter points";

return result;

}

对此起始程序的评价

这个起始程序给你留下什么印象?我会说它设计得不好,而且很明显不符合面向对象精神。对于这样一个小程序,这些缺点其实没有什么大不了的。快速而随性地设计一个简单的程序并没有错。但如果这是复杂系统中具有代表性的一段,那么我就真的要对这个程序信心动摇了。Customer里头那个长长的statement()做的事情实在太多了,它做了很多原本应该由其他类完成的事情。

即便如此,这个程序还是能正常工作。所以这只是美学意义上的判断,只是对丑陋代码的厌恶,是吗?如果不去修改这个系统,那么的确如此,编译器才不会在乎代码好不好看呢。但是当我们打算修改系统的时候,就涉及了人,而人在乎这些。差劲的系统是很难修改的,因为很难找到修改点。如果很难找到修改点,程序员就很有可能犯错,从而引入bug。

在这个例子里,我们的用户希望对系统做一点修改。首先他们希望以HTML格式输出详单,这样就可以直接在网页上显示,这非常符合时下的潮流。现在请你想一想,这个变化会带来什么影响。看看代码你就会发现,根本不可能在打印HTML报表的函数中复用目前statement()的任何行为。你唯一可以做的就是编写一个全新的htmlStatement(),大量重复statement()的行为。当然,现在做这个还不太费力,你可以把statement()复制一份然后按需要修改就是了。

但如果计费标准发生变化,又会如何?你必须同时修改statement()和htmlStatement(),并确保两处修改的一致性。当你后续还要再修改时,复制粘贴带来的问题就浮现出来了。如果你编写的是一个永不需要修改的程序,那么剪剪贴贴就还好,但如果程序要保存很长时间,而且可能需要修改,复制粘贴行为就会造成潜在的威胁。

现在,第二个变化来了:用户希望改变影片分类规则,但是还没有决定怎么改。他们设想了几种方案,这些方案都会影响顾客消费和常客积分点的计算方式。作为一个经验丰富的开发者,你可以肯定:不论用户提出什么方案,你唯一能够获得的保证就是他们一定会在六个月之内再次修改它。

为了应付分类规则和计费规则的变化,程序必须对statement()做出修改。但如果我们把statement()内的代码复制到用以打印HTML详单的函数中,就必须确保将来的任何修改在两个地方保持一致。随着各种规则变得越来越复杂,适当的修改点越来越难找,不犯错的机会也越来越少。

你的态度也许倾向于尽量少修改程序:不管怎么说,它还运行得很好。你心里牢牢记着那句古老的工程谚语:“如果它没坏,就不要动它。”这个程序也许还没坏掉,但它造成了伤害。它让你的生活比较难过,因为你发现很难完成客户所需的修改。这时候,重构技术就该粉墨登场了。

如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达成目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。1.2重构的第一步

每当我要进行重构的时候,第一个步骤永远相同:我得为即将修改的代码建立一组可靠的测试环境。这些测试是必要的,因为尽管遵循重构手法可以使我避免绝大多数引入bug的情形,但我毕竟是人,毕竟有可能犯错。所以我需要可靠的测试。

由于statement()的运作结果是个字符串,所以我首先假设一些顾客,让他们每个人各租几部不同的影片,然后产生报表字符串。然后我就可以拿新字符串和手上已经检查过的参考字符串做比较。我把所有测试都设置好,只要在命令行输入一条Java命令就把它们统统运行起来。运行这些测试只需几秒钟,所以你会看到我经常运行它们。

测试过程中很重要的一部分,就是测试程序对于结果的报告方式。它们要么说“OK”,表示所有新字符串都和参考字符串一样,要么就列出失败清单,显示问题字符串的出现行号。这些测试都能够自我检验。是的,你必须让测试有能力自我检验,否则就得耗费大把时间来回比对,这会降低你的开发速度。

进行重构的时候,我们需要依赖测试,让它告诉我们是否引入了bug。好的测试是重构的根本。花时间建立一个优良的测试机制是完全值得的,因为当你修改程序时,好测试会给你必要的安全保障。测试机制在重构领域的地位实在太重要了,我将在第4章详细讨论它。

重构前,先检查自己是否有一套可靠的测试机制。这些测试必须有自我检验能力。1.3分解并重组statement()

第一个明显引起我注意的就是长得离谱的statement()。每当看到这样长长的函数,我就想把它大卸八块。要知道,代码块越小,代码的功能就愈容易管理,代码的处理和移动也就越轻松。

本章重构过程的第一阶段中,我将说明如何把长长的函数切开,并把较小块的代码移至更合适的类。我希望降低代码重复量,从而使新的(打印HTML详单用的)函数更容易撰写。

第一个步骤是找出代码的逻辑泥团并运用Extract Method (110)。本例一个明显的逻辑泥团就是switch语句,把它提炼到独立函数中似乎比较好。

和任何重构手法一样,当我提炼一个函数时,我必须知道可能出什么错。如果提炼得不好,就可能给程序引入bug。所以重构之前我需要先想出安全做法。由于先前我已经进行过数次这类重构,所以我已经把安全步骤记录于后面的重构列表中了。

首先我得在这段代码里找出函数内的局部变量和参数。我找到了两个,each和thisAmount,前者并未被修改,后者会被修改。任何不会被修改的变量都可以被我当成参数传入新的函数,至于会被修改的变量就需格外小心。如果只有一个变量会被修改,我可以把它当作返回值。thisAmount是个临时变量,其值在每次循环起始处被设为0,并且在switch语句之前不会改变,所以我可以直接把新函数的返回值赋给它。

下面两页展示了重构前后的代码。重构前的代码在左页,重构后的代码在右页。凡是从函数提炼出来的代码,以及新代码所做的任何修改,只要我觉得不是明显到可以一眼看出,就以粗体字标示出来特别提醒你。本章剩余部分将延续这种左右比对形式。

public String statement(){

double totalAmount = 0;

int frequentRenterPoints = 0;

Enumeration rentals = _rentals.elements();

String result = "Rental Record for " + getName()+ "\n";

while (rentals.hasMoreElements()){

double thisAmount = 0;

Rental each = (Rental)rentals.nextElement();

//determine amounts for each line

switch (each.getMovie().getPriceCode()){

case Movie.REGULAR:

thisAmount += 2;

if (each.getDaysRented()> 2)

thisAmount += (each.getDaysRented()- 2)* 1.5;

break;

case Movie.NEW_RELEASE:

thisAmount += each.getDaysRented()* 3;

break;

case Movie.CHILDRENS:

thisAmount += 1.5;

if (each.getDaysRented()> 3)

thisAmount += (each.getDaysRented()- 3)* 1.5;

break;

}

// add frequent renter points

frequentRenterPoints ++;

// add bonus for a two day new release rental

if ((each.getMovie().getPriceCode()== Movie.NEW_RELEASE)

&& each.getDaysRented()> 1)frequentRenterPoints ++;

//show figures for this rental

result += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(thisAmount)+ "\n";

totalAmount += thisAmount;

}

//add footer lines

result += "Amount owed is " + String.valueOf(totalAmount)+ "\n";

result += "You earned " + String.valueOf(frequentRenterPoints)

+ " frequent renter points";

return result;

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载