clean code-代码整洁之道 阅读笔记(第十六章)

第十六章 重构SerialDate

16.1 首先,让它能工作
  1. 利用SerialDateTests来完整的理解和重构SerialDate
  2. 用Clover来检查单元测试覆盖了哪些代码,效果不行
  3. 重新编写自己的单元测试
  4. 经过简单的修改,让测试能够通过
16.2 让它做对

全过程:

  1. 开端注释过时已久,缩短并改进了它
  2. 把全部枚举移到它们自己的文件
  3. 把静态变量(dateFormatSymbols)和3个静态方法(getMontShNames、isLeap Year和lastDayOfMonth)移到名为DateUtil的新类中。
  4. 把那些抽象方法上移到它们该在的顶层类中。
  5. 把Month.make改为Month.fromInt,并如法炮制所有其他枚举。为全部枚举创建了toInt()访问器,把index字段改为私有。
  6. 在plusYears和plusMonths中存在一些有趣的重复,通过抽离出名为
    correctLastDayOfMonth的新方法消解了重复,使这3个方法清晰多了。
  7. 消除了魔术数1,用Month.JANUARY.toInt()或Day.SUNDAY:toInt()做了恰当的替换。在SpreadsheetDate上花了点时间,清理了一下算法
     

细节操作:

  1. 删除修改历史
  2. 导入列表通过使用java.text.*和java.util.*来缩短
  3. 用<pre>标签把整个注释部分包围起来
  4. 修改类名:SerialDate => DayDate
  5. 把MonthConstants改成枚举
  6. 去掉serialVersionUID变量,自动控制序列号
  7. 去掉多余的、误导的注释
  8. EARLIEST_DATE_ORDINAL 和 LATEST_DATE_ORDINAL移到SpreadSheeDate中
  9. 基类不宜了解其派生类的情况,使用抽象工厂模式创建一个DayDateFactory。该工厂将创建我们所需要的DayDate的实体,并回答有关实现的问题,例如最大和最小日期之类。
  10. 删除未使用代码
  11. 数组应该移到靠近其使用位置的地方
  12. 将以整数形式传递改为符号传递
  13. 删除默认构造器
  14. 删除final
  15. 使用枚举整理for循环,并使用||连接for中的if语句
  16. 重命名、简化、重构函数
  17. 使用解释临时变量模式来简化函数、将静态方法转变成实例方法、并删除重复实例方法
  18. 算法本身也该有一小部分依赖于实现,将算法上移到抽象类中
最终代码

DayDate.java

/* ====================================================================
* JCommon : a free general purpose class library forthe Java(tm) platform
* =====================================================================
*
* (C) Copyright 2000-2005, by Object Refinery Limited aand Contributors
...
* /
package org.jfree.date;

import java.io.Serializable;
import java.util.*;

/**
 * An abstract class that represents immutable dates with a precision of
 * one day. The implementation will map each date to an integer that
 * represents an ordinal number of days from some fixed origin.
 *
 * Why not just use java.util.Date? We will, when it makes sense. At times,
 * java.util.Date can be *too* precise - it represents an instant in time,
 * accurate to 1/1000th of a second (with the date itself depending on the
 * time-zone). Sometimes we just want to represent a particular day (e.g. 21
 * January 2015) without concerning ourselves about the time of day, or the
 * time-zone, or anything else. That's what we've defined DayDate for.
 *
 * Use DayDateFactory.makeDate to create an instance.
 *
 * @author David Gilbert
 * @author Robert C. Martin did a lot of refactoring.
 */
public abstract class DayDate implements Comparable, Serializable {
    public abstract int getOrdinalDay();
    public abstract int getYear();
    public abstract Month getMonth();
    public abstract int getDayOfMonth();

    protected abstract Day getDayOfWeekForOrdinalZero();

    public DayDate plusDays(int days) {
        return DayDateFactory.makeDate(getOrdinalDay() + days);
    }

    public DayDate plusMonths(int months) {
        int thisMonthAsOrdinal = getMonth().toInt() - Month.JANUARY.toInt();
        int thisMonthAndYearAsOrdinal = 12 * getYear() + thisMonthAsOrdinal;
        int resultMonthAndYearAsOrdinal = thisMonthAndYearAsOrdinal + months;
        int resultYear = resultMonthAndYearAsOrdinal / 12;
        int resultMonthAsOrdinal = resultMonthAndYearAsOrdinal % 12 + Month.JANUARY.toInt();
        Month resultMonth = Month.fromInt(resultMonthAsOrdinal);
        int resultDay = correctLastDayOfMonth(getDayOfMonth(), resultMonth, resultYear);
        return DayDateFactory.makeDate(resultDay, resultMonth, resultYear);
    }

    public DayDate plusYears(int years) {
        int resultYear = getYear() + years;
        int resultDay = correctLastDayOfMonth(getDayOfMonth(), getMonth(), resultYear);
        return DayDateFactory.makeDate(resultDay, getMonth(), resultYear);
    }

    private int correctLastDayOfMonth(int day, Month month, int year) {
        int lastDayOfMonth = DateUtil.lastDayOfMonth(month, year);
        if (day > lastDayOfMonth)
            day = lastDayOfMonth;
        return day;
    }

	public DayDate getPreviousDayofWeek (Day targetDayofweeek){
		int offsetToTarget = targetDayOfWeek.toInt() - getDayfWeek().toInt();
		if(offsetToTarget>=0)
			offsetToTarget = 7;
		return plusDays (offsetToTarget);
	}

	public DayDate get FollowingDayofWeek (Day targetDayofWeek){
		int offsetToTarget = targetDayOfWeek.toInt() - getDayofweek().toInt();
		if(offsetToTarget<= 0)
			offsetToTarget += 7;
		return plusDays (offsetToTarget);
	}

	public DayDate getNearestDayofWeek (Day targetDayofWeek) {
		int offsetToThisWeeksTarget = targetDayOfWeek.toInt()- getDayOfWeek().toInt();
		int offsetToFutureTarget = (offsetToThisWeeksTarget +7)&7;
		int offsetToPreviousTarget = offsetToFutureTarget - 7
		
		if(offsetToFutureTarget>3)
			return plusDays(offsetToPreviousTarget);
		else
			return plusDays (offsetToFutureTarget);
	}

	public DayDate getEndOfMonth(){
		Month month=getMonth();
		intyear=getYear();
		int lastDay = DateUtil.lastDayOfMonth (month,year);
		return DayDateFactory.makeDate(lastDay,month,year);
	}

	public Date toDate(){
		final Calendar calendar = Calendar.getInstance(); 
		int ordinalMonth = getMonth().toInt() - Month.JANUARY.toInt ();
		calendar.set (getYear(), ordinalMonth, getDayOfMonth(),0,0,0);
		return calendar.getTime();
	}

	public String toString(){
		return String.format("802d-is-8d", getDayofMonth(),getMonth(),getYear());
	}

	publicDaygetDayOfWeek(){
		Day startingDay = getDayOfWeekForordinalzero();
		int startingoffset = startingDay.toint() - Day.SUNDAY.toInt();
		int ordinalofDayOfWeek = (getordinalDay() + startingoffset)7;
		return Day.fromint(ordinalofDayOfWeek + Day.SUNDAY.toint());
	}

	public int daysSince(DayDate date){
		returngetordinalDay() - date.getordinalDay();
	}

	public boolean ison(DayDate other){
		return getordinalDay() = other.getordinalDay();
	}

	public boolean isBefore(DayDate other){
		return getordinalDay()<other.getordinalDay();
	}

	public boolean isonorBefore(DayDate other){
		return getordinalDay()<= other.getordinalDay();
	}

	public boolean isAfter(DayDate other){
		return getordinalDay() > other.getordinalDay();
	}

	public boolean isonorAfter(DayDate other){
		return getordinalDay() >= other.getordinalDay();
	}

	public boolean isInRange(DayDate d1,DayDated2){
		return isInRange(dl,d2,DateInterval.CLOSED);
	}

	public boolean isInRange (DayDate dl, DayDate d2, DateInterval interval){
		int left = Math.min(dl.getordinalDay(),d2.getordina1Day())
		int right = Math.max(dl.getOrdinalDay(), d2.getordinalDay());
		return interval.isIn(getOrdinalDay(),left,right);
	}
}

Month.java

package org.jfree.date;

import java.text.DateFormatSymbols;

public enum Month{
	JANUARY(1), FEBRUARY(2), MARCH(3),
	APRIL(4), MAY(5), JUNE (6),
	JULY(7), AUGUST(8), SEPTEMBER(9),
	OCTOBER(10), NOVEMBER(11), DECEMBER(12);
	private static DateFormatSymbols dateFormatSymbols = netDateFormatSymbols();
	private static final int[] LAST_DAY_OF_MONTH = {0,31,28,31,30,31,30,31,31,31,30,31,30,31,30,31};
	
	private int index;
	
	Month(int index){
		this.index=index;
	}
	
		public static Month fromInt(int monthIndex) {
		for(Monthm:Month.values())(
			if(m.index==monthIndex)
				return m;
		}
		throw new IllegalArgumentException("Invalid month :index " + monthIndex)
	}
	
	public int lastDay(){
		return LAST_DAY_OF_MONTH[index];
	}
	
	public int quarter(){
		return1+(index-1)/3;
	}
	
	public String toString()(
		return dateFormatSymbols.getMonths()[index - 1];
	}
	
	public String toshortString(){
		return date FormatSymbols.getShortMonths()[index -1];
	}
	
	public static Month parse(String s) {
		s = s.trim();
		for(Monthm:Month.values())
			if(m.matches(s))
				return m;
				
		try{
			return fromInt(Integer.parseInt(s));
			}
		catch (NumberFormatException e){}
		throw new IllegalArgumentException("Invalid month"+s);
	}

	private boolean matches (String s){
		return s.equalsIgnoreCase(toString()) || s.equalsIgnoreCase(toShortString());
	}

	public int toInt(){
		return index;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值