定义

某个实例变量仅为代码中一小部分功能临时所用而创建

影响

  • 通常一个对象会需要它的全部的变量。当一个变量看上去没什么用,却要试图了解他为什么在哪里时,会使类的作用变得更难理解,影响了代码的可读性和可维护性。

改进目标

  • 消除临时字段,提升代码可读性、可维护性

方法

  • 提取类
  • 引入特例

案例1:问题代码

代码背景

  • Account为用户账户类,包含用户名、卡号、余额等信息,以及一些未列举出来的账户信息
  • 日常消费中会频繁调用addToBalance、deductBalance、以及其他一些未列举的账户信息处理方法,更新账户信息
  • 每半年会调用evaluateAccount和evaluateConsumptionLevel方法,对账户进行一次评估

症状/问题

  • name、cardId、balance以及一些未列举的账户信息字段, 在账户对象创建后,频繁会被使用。与对象绑定在一起, 属于Account中的正常字段。
  • maxAsset、usageFrequency、overdueTimes,每半年才被 赋值并使用一次。账户对象创建后,多数情况下是空值, 毫无用处,属于临时字段
/**
 * 账户信息5
 */
public class Account {
    private final String name;

    private final String cardId;

    private int balance;

    // other account info ……

    private int maxAsset;

    private double usageFrequency;

    private int overdueTimes;

    public Account(String name, String cardId, int balance) {
        this.name = name;
        this.cardId = cardId;
        this.balance = balance;
    }

    public String getName() {
        return name;
    }

    public String getCardId() {
        return cardId;
    }

    /**
     * 增加到余额
     * 
     * @param money 增加金额
     */
    public void addToBalance(int money) {
        balance += money;
    }

    /**
     * 扣除余额
     * 
     * @param money 扣除金额
     */
    public void deductBalance(int money) {
        balance -= money;
    }

    public int getBalance() {
        return balance;
    }

    // other account info process

    /**
     * 整体评估
     * 
     * @return 评估结果
     */
    public int evaluateAccount() {
        int evaluatePoint = 60;
        int usageTimes = (int) (usageFrequency * 365) + 1;
        double overdueRate = (double) overdueTimes / usageTimes;
        evaluatePoint -= 10 * overdueRate;
        evaluatePoint += (maxAsset / 100);
        return evaluatePoint;
    }

    /**
     * 消费水平评估
     * 
     * @return 评估结果
     */
    public int evaluateConsumptionLevel() {
        return (int) (usageFrequency * 365) - overdueTimes * 3;
    }

    public void setMaxAsset(int maxAsset) {
        this.maxAsset = maxAsset;
    }

    public void setUsageFrequency(double usageFrequency) {
        this.usageFrequency = usageFrequency;
    }

    public void setOverdueTimes(int overdueTimes) {
        this.overdueTimes = overdueTimes;
    }
}

改进手法

/**
 * 账户信息5
 */
public class Account {
    private final String name;

    private final String cardId;

    private int balance;

    // other account info ……

    public Account(String name, String cardId, int balance) {
        this.name = name;
        this.cardId = cardId;
        this.balance = balance;
    }

    public String getName() {
        return name;
    }

    public String getCardId() {
        return cardId;
    }

    /**
     * 增加到余额
     * 
     * @param money 增加金额
     */
    public void addToBalance(int money) {
        balance += money;
    }

    /**
     * 扣除余额
     * 
     * @param money 扣除金额
     */
    public void deductBalance(int money) {
        balance -= money;
    }

    public int getBalance() {
        return balance;
    }

    // other account info process
}
public class Evaluator {
    int maxAsset;

    double usageFrequency;

    int overdueTimes;

    /**
     * 整体评估
     *
     * @return 评估结果
     */
    public int evaluateAccount() {
        int evaluatePoint = 60;
        int usageTimes = (int) (usageFrequency * 365) + 1;
        double overdueRate = (double) overdueTimes / usageTimes;
        evaluatePoint -= 10 * overdueRate;
        evaluatePoint += (maxAsset / 100);
        return evaluatePoint;
    }

    public Evaluator(int maxAsset, double usageFrequency, int overdueTimes) {
        this.maxAsset = maxAsset;
        this.usageFrequency = usageFrequency;
        this.overdueTimes = overdueTimes;
    }

    /**
     * 消费水平评估
     *
     * @return 评估结果
     */
    public int evaluateConsumptionLevel() {
        return (int) (usageFrequency * 365) - overdueTimes * 3;
    }
}

案例2:问题代码

代码背景

  • RoutingHandler对消息进行路由处理,即我们关注的核心类
  • Message为消息体
  • 路由的目标对象,通过工厂RouterFactory进行创建
  • RouterFactory创建Router接口的实例,即具体的路由对象

症状/问题

  • 在handle方法中,我们需要反复对msg.getPriority()是否为空进行判断,priority为空是一种特例,对空的处理少数情况下的处理逻辑,也可归属于临时字段范畴
/**
 * 路由消息处理5
 */
public class RoutingHandler {
    /**
     * 消息处理
     * 
     * @param messages 消息内容
     * @return 处理结果
     */
    public List<String> handle(Iterable<Message> messages) {
        List<String> handleResults = new ArrayList<>();
        for (Message msg : messages) {
            String handleResult;
            if (msg.getPriority() == null) {
                handleResult = "priority is null, handle failed";
            } else {
                Router router = RouterFactory.getRouterForMessage(msg);
                handleResult = router.route(msg);
            }
            handleResults.add(handleResult);
        }
        return handleResults;
    }
}
/**
 * 路由接口5
 */
public interface Router {
    /**
     * 发送消息
     * 
     * @param body 消息体
     * @return 路由结果
     */
    String route(Message body);
}

改进手法: 我们可以使用“引入特例”的方法,为Router加入一个 新的实现类,用来专门处理这种少见的逻辑。 此时便可将这种“临时的处理”搬移到类外面去。

/**
 * 路由消息处理5
 */
public class RoutingHandler {
    /**
     * 消息处理
     * 
     * @param messages 消息内容
     * @return 处理结果
     */
    public List<String> handle(Iterable<Message> messages) {
        List<String> handleResults = new ArrayList<>();
        for (Message msg : messages) {
            Router router = RouterFactory.getRouterForMessage(msg);
            handleResults.add(router.route(msg));
        }
        return handleResults;
    }
}
/**
 * 路由接口5
 */
public interface Router {
    /**
     * 发送消息
     * 
     * @param body 消息体
     * @return 路由结果
     */
    String route(Message body);
}
public class NullRouter implements Router {
    @Override
    public String route(Message body) {
        return "priority is null, handle failed";
    }
}
/**
 * 路由方式工厂5
 */
public class RouterFactory {
    /**
     * 获取路由消息的方法
     * 
     * @param msg 消息体
     * @return 路由方法实例
     */
    public static Router getRouterForMessage(Message msg) {
        switch (String.valueOf(msg.getPriority())) {
            case "high":
                return new SmsRouter();
            case "medium":
                return new JmsRouter();
            case "null":
                return new NullRouter();
            default:
                return new DefaultRouter();
        }
    }
}

操作手法

操作快捷键(推荐)Ctrl+Alt+Shift+T(或:鼠标右键“Refactor”)
提取类Extract Delegate
提取方法Ctrl+Alt+MExtract Method
移除中间人Remove Middleman
安全删除Alt+DeleteSafe Delete
内联Ctrl+Alt+NInline xxx

补充:时常需要人工判断,分析变量或字段的生命周期。