重构改善既有代码的设计(一)

买了一本《重构改善既有代码的设计》,感觉味道不错,逗比来晒晒。

初品重构实例,就遇到两个没用过的类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()方法字节调用即可。这里代码就不贴了,以上内容基本来自《重构改善既有代码的设计》,个别内容本人添加。虽然原书内容重构还在继续,本人已然明了。欲知该例子重构更多内容自己买书吧,逗比倍懒写不动啦啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值