重构改善既有代码的设计(一)
2015-12-03 12:01
204 查看
买了一本《重构改善既有代码的设计》,感觉味道不错,逗比来晒晒。
初品重构实例,就遇到两个没用过的类Vector、Enumeration。在这里做个知识的补充,Vector和ArrayList都是继承自AbstractList,ArrayList会比Vector快,他是非同步的,如果设计涉及到多线程安全,还是用Vector比较好一些 。Vector的相关使用方法参照API调用即可,下面来看Vector内部的一段代码:
这里的Enumeration接口源码如下:
两个方法分别是:1.枚举对象中是否还含有元素,如果返回true,则表示还含有至少一个的元素。2.枚举对象还含有元素,该方法得到对象中的下一个元素。
现在来看看需求:影片出租店,计算每位顾客的消费金额和明细,操作者告诉顾客租了那些影片、租期、和根据租期影片类型计算出费用和积分,影片类型分为普通片、儿童片、新片。下面是重构前相关代码:
下面是测试代码和结果:
当你发现自己的程序需要添加一个特性,而代码结构无法使你很容易的达成目的,这时候你就要先重构那个程序,重构的第一步永远都相同:搭建一个可靠地测试环境,并拥有检测自身的能力。就上面的代码来看statement()方法内容过长,处理事情过多,代码块应该越小越好,一个方法一个功能,这样容易管理移动,下面开始第一次重构(Eclipse:alt+shift+M抽取方法快捷键):
租赁影片计算价格并没有与顾客关联,而是用到了Rental ,根据面向对象的知识,这里把把该方法提取到Rental 类里面,并重命名为getCharge():
做完以上修改,回过头来再看statement()方法,就会发现thisAmout都点点多余了,这里可以直接通过方法获取了,修改如下:
对于积分的计算,也没有与顾客关联上,这一和计算价格同理,抽取方法到Rental,statement()方法字节调用即可。这里代码就不贴了,以上内容基本来自《重构改善既有代码的设计》,个别内容本人添加。虽然原书内容重构还在继续,本人已然明了。欲知该例子重构更多内容自己买书吧,逗比倍懒写不动啦啦。
初品重构实例,就遇到两个没用过的类Vector、Enumeration。在这里做个知识的补充,Vector和ArrayList都是继承自AbstractList,ArrayList会比Vector快,他是非同步的,如果设计涉及到多线程安全,还是用Vector比较好一些 。Vector的相关使用方法参照API调用即可,下面来看Vector内部的一段代码:
/** * Returns an enumeration of the components of this vector. The * returned {@code Enumeration} object will generate all items in * this vector. The first item generated is the item at index {@code 0}, * then the item at index {@code 1}, and so on. * * @return an enumeration of the components of this vector * @see Iterator */ public Enumeration<E> elements() { return new Enumeration<E>() { int count = 0; public boolean hasMoreElements() { return count < elementCount; } public E nextElement() { synchronized (Vector.this) { if (count < elementCount) { return elementData(count++); } } throw new NoSuchElementException("Vector Enumeration"); } }; }
这里的Enumeration接口源码如下:
public interface Enumeration<E> { /** * Tests if this enumeration contains more elements. * * @return <code>true</code> if and only if this enumeration object * contains at least one more element to provide; * <code>false</code> otherwise. */ boolean hasMoreElements(); /** * Returns the next element of this enumeration if this enumeration * object has at least one more element to provide. * * @return the next element of this enumeration. * @exception NoSuchElementException if no more elements exist. */ E nextElement(); }
两个方法分别是:1.枚举对象中是否还含有元素,如果返回true,则表示还含有至少一个的元素。2.枚举对象还含有元素,该方法得到对象中的下一个元素。
现在来看看需求:影片出租店,计算每位顾客的消费金额和明细,操作者告诉顾客租了那些影片、租期、和根据租期影片类型计算出费用和积分,影片类型分为普通片、儿童片、新片。下面是重构前相关代码:
/** *电影 **/ 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) { super(); this._title = _title; this._priceCode = _priceCode; } public String getTitle() { return _title; } public void setTitle(String _title) { this._title = _title; } public int getPriceCode() { return _priceCode; } public void setPriceCode(int _priceCode) { this._priceCode = _priceCode; } }
/** *租赁 **/ public class Rental { private Movie _movie; private int _daysRented; public Rental(Movie _movie, int _daysRented) { super(); this._movie = _movie; this._daysRented = _daysRented; } public Movie getMovie() { return _movie; } public void setMovie(Movie _movie) { this._movie = _movie; } public int getDaysRented() { return _daysRented; } public void setDaysRented(int _daysRented) { this._daysRented = _daysRented; } }
/** * 顾客 **/ public class Customer { private String _name; private Vector<Rental> _rentals = new Vector<>(); public Customer(String _name) { super(); this._name = _name; } public void addRental(Rental arg){ _rentals.add(arg); } public String getName(){ return _name; } public String statement(){ double totalAmount=0; int frequentRenterPoints=0; Enumeration<Rental> rentals = _rentals.elements(); String result = "Rental Record for "+getName()+"\n"; while(rentals.hasMoreElements()){ double thisAmount=0; Rental each = (Rental)rentals.nextElement(); switch (each.getMovie().getPriceCode()) { case Movie.CHILDRENS: thisAmount += 1.5; if(each.getDaysRented()>3){ thisAmount += (each.getDaysRented()-3)*1.5; } break; case Movie.NEW_RELEASE: thisAmount += each.getDaysRented()*3; break; case Movie.REGULAR: thisAmount += 2; if(each.getDaysRented()>2){ thisAmount += (each.getDaysRented()-2)*1.5; } break; default: break; } frequentRenterPoints++; if(each.getMovie().getPriceCode()==Movie.NEW_RELEASE && each.getDaysRented()>1)frequentRenterPoints++; result += "\t"+each.getMovie().getTitle()+"\t"+String.valueOf(thisAmount)+"\n"; totalAmount +=thisAmount; } result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points "; return result; } }
下面是测试代码和结果:
public class Test { public static void main(String[] args) { Customer mCustomer=new Customer("Mery Jing"); mCustomer.addRental(new Rental(new Movie("烈日灼心", Movie.NEW_RELEASE), 5)); mCustomer.addRental(new Rental(new Movie("武动乾坤", Movie.CHILDRENS), 2)); mCustomer.addRental(new Rental(new Movie("无极天下", Movie.REGULAR), 1)); System.out.println(mCustomer.statement()); } } 测试结果: Rental Record for Mery Jing 烈日灼心 15.0 武动乾坤 1.5 无极天下 2.0 Amount owed is 18.5 You earned 4 frequent renter points
当你发现自己的程序需要添加一个特性,而代码结构无法使你很容易的达成目的,这时候你就要先重构那个程序,重构的第一步永远都相同:搭建一个可靠地测试环境,并拥有检测自身的能力。就上面的代码来看statement()方法内容过长,处理事情过多,代码块应该越小越好,一个方法一个功能,这样容易管理移动,下面开始第一次重构(Eclipse:alt+shift+M抽取方法快捷键):
double thisAmount=0; Rental each = (Rental)rentals.nextElement(); thisAmount = amountFor(each, thisAmount); frequentRenterPoints++; //.............略......................... /** * 计算价格 * @param each * @param result >变量的命名要具有可读性,要让人能轻易读懂,重命名快捷键(eclipse):alt+shift+R * @return result */ private double amountFor(Rental each, double result) { switch (each.getMovie().getPriceCode()) { case Movie.CHILDRENS: result += 1.5; if(each.getDaysRented()>3){ result += (each.getDaysRented()-3)*1.5; } break; case Movie.NEW_RELEASE: result += each.getDaysRented()*3; break; case Movie.REGULAR: result += 2; if(each.getDaysRented()>2){ result += (each.getDaysRented()-2)*1.5; } break; default: break; } return result; }
租赁影片计算价格并没有与顾客关联,而是用到了Rental ,根据面向对象的知识,这里把把该方法提取到Rental 类里面,并重命名为getCharge():
double thisAmount=0; Rental each = (Rental)rentals.nextElement(); thisAmount = each.getCharge(); frequentRenterPoints++; //.............略......................... /** * 计算价格 * 变量的命名要具有可读性,要让人能轻易读懂,重命名快捷键(eclipse):alt+shift+R * @return result */ public double getCharge() { double result=0; switch (getMovie().getPriceCode()) { case Movie.CHILDRENS: result += 1.5; if(getDaysRented()>3){ result += (getDaysRented()-3)*1.5; } break; case Movie.NEW_RELEASE: result += getDaysRented()*3; break; case Movie.REGULAR: result += 2; if(getDaysRented()>2){ result += (getDaysRented()-2)*1.5; } break; default: break; } return result; }
做完以上修改,回过头来再看statement()方法,就会发现thisAmout都点点多余了,这里可以直接通过方法获取了,修改如下:
while(rentals.hasMoreElements()){ Rental each = (Rental)rentals.nextElement(); frequentRenterPoints++; if(each.getMovie().getPriceCode()==Movie.NEW_RELEASE && each.getDaysRented()>1)frequentRenterPoints++; result += "\t"+each.getMovie().getTitle()+"\t"+String.valueOf(each.getCharge())+"\n"; totalAmount +=each.getCharge(); }
对于积分的计算,也没有与顾客关联上,这一和计算价格同理,抽取方法到Rental,statement()方法字节调用即可。这里代码就不贴了,以上内容基本来自《重构改善既有代码的设计》,个别内容本人添加。虽然原书内容重构还在继续,本人已然明了。欲知该例子重构更多内容自己买书吧,逗比倍懒写不动啦啦。
相关文章推荐
- java:从消息机制谈到观察者模式
- 定时任务中的 Timer的schedule和scheduleAtFixedRate方法的区别
- RxJava的简单学习(学习自扔物线)
- 火狐浏览器下请求两次(C#)
- 用Python抢枪过年的火车票
- 用Python抢枪过年的火车票
- python 科学计算Numpy的自学(一)
- 如何让一个类不能被继承
- Java并发编程:volatile关键字解析
- javaday2-java语言基础
- 【C语言提高20】字符串反转(两头堵的变形)
- Java反射机制详解
- 详解php比较操作符的安全问题
- java中String的常用方法
- java基础学习——循环结构(while,do-while,for)笔试题
- C++中placement new详解
- 基于双向链表的增删改查和排序(C++实现)
- java必学必会之GUI编程
- yii2 RESTful 接口 api -6: 写一个自己的api
- java中常用的字符串的截取方法