1.1重构-第一章

重构是指在不改变软件外部行为的情况下,优化其内部结构。一个影片出租店的程序展示了重构的必要性,因为原本的`statement()`方法职责过重,导致扩展困难。当需要输出HTML格式详单或更改计费规则时,复制粘贴代码会带来维护难题。重构旨在应对这种变化,确保代码的可维护性和灵活性。
摘要由CSDN通过智能技术生成

重构是在不改变软件可观察行为的前提下改善其内部结构。

设计模式为重构提供了目标

1.1 起点

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


Movie(影片)- movie只是一个简单的纯数据类

/**
 * 影片
 * @author harry
 */
public class Movie {

	// 影片类型
	public static final int REGULAR = 0;//普通片
	public static final int NEW_RELEASE = 1;//新片
	public static final int CHILDRENS = 2;//儿童片
	
	private String _title;
	private int _priceCode;
	
	public Movie(String _title, int _priceCode) {
		super();
		this._title = _title;
		this._priceCode = _priceCode;
	}
	
	public String get_title() {
		return _title;
	}
	public void set_title(String _title) {
		this._title = _title;
	}
	public int get_priceCode() {
		return _priceCode;
	}
	public void set_priceCode(int _priceCode) {
		this._priceCode = _priceCode;
	}
}
Rental(租赁)- rental表示某个顾客租了一部影片

/**
 * 租赁实体
 * @author harry
 */
public class Rental {
	private Movie _movie;
	private int _dayRented;
	
	public Rental(Movie _movie, int _dayRented) {
		super();
		this._movie = _movie;
		this._dayRented = _dayRented;
	}
	
	public Movie get_movie() {
		return _movie;
	}
	public void set_movie(Movie _movie) {
		this._movie = _movie;
	}
	public int get_dayRented() {
		return _dayRented;
	}
	public void set_dayRented(int _dayRented) {
		this._dayRented = _dayRented;
	}
}
Customer(顾客)-customer表示顾客。与其他类一样,它也拥有数据和相应的访问函数

/**
 * 顾客实体
 * @author harry
 */
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.addElement(arg);
	}
	public String getName() {
		return _name;
	}
	public void setName(String _name) {
		this._name = _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 = rentals.nextElement();
			
			// 计算金额
			switch(each.get_movie().get_priceCode()){
				case Movie.REGULAR:
					thisAmount += 2;
					if (each.get_dayRented() > 2) {
						thisAmount += (each.get_dayRented()-2)*1.5;
					}
					break;
				case Movie.NEW_RELEASE:
					thisAmount += each.get_dayRented()*3;
					break;
				case Movie.CHILDRENS:
					thisAmount += 1.5;
					if (each.get_dayRented() > 3) {
						thisAmount += (each.get_dayRented()-3)*1.5;
					}
					break;
			}
			
			// 常规积分累加
			frequentRenterPoints++;
			// 特殊新书积分计算
			if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
				each.get_dayRented() > 1) {
				frequentRenterPoints++;
			}
			
			// 显示凭条
			result += "\t"+each.get_movie().get_title()+"\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;
	}
}

对起始程序的评价

         Customer里的长长的statement()做的事情太多了,它做了原本应该由其他类完成的事情。

         在这个例子中,如果用户希望以HTML格式输出详情单。上述程序中的statement()无法复用,唯一可以做就是编写一个全新的htmlStatement()。大量重复statement()的行为。当然这个还不是太费力,你可以把statement()复制一份按需求修改就可以了。

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

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

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

         接下来重构技术就该粉墨登场了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值