zoukankan      html  css  js  c++  java
  • 重构-改善既有代码的设计完整笔记系列之6、7

    目录

    • 6.1 Extract Method(提炼函数)
    • 6.2 Inline Method(内联函数)
    • 6.3 Inline Temp(内联临时变量)
    • 6.4 Replace Temp with Query(以查询取代临时变量)
    • 6.5 Introduce Explaining Variable(引入解释性变量)
    • 6.6 Split Temporary Variable(分解临时变量)
    • 6.7 Remove Assignments to Parameters(移除对参数的赋值)
    • 6.8 Replace Method with Method Object(以函数对象取代函数)
    • 6.9 Substitute Algorithm(替换算法)
    • 7.1 Move Method(搬移函数)
    • 7.2 Move Field(搬移字段)
    • 7.3 Extract Class(提炼类)
    • 7.4 Inline Class(将类内联化)
    • 7.5 Hide Delegate(隐藏“委托关系”)
    • 7.6 Remove Middle Man(移除中间人)
    • 7.7 Introduce Foreign Method(引入外加函数)
    • 7.8 Introduce Local Extension(引入本地扩展)


    6.1 ExtractMethod(提炼函数)

    将一段代码放进一个独立函数中,并让函数名称解释该函数的用途。
    增加可读性,函数粒度小更容易被复用和覆写。

    void printOwing() {
            Enumeration e = _orders.elements();
            double outstanding = 0.0;
            // print banner
            System.out.println("**************************");
            System.out.println("***** Customer Owes ******");
            System.out.println("**************************");
            // calculate outstanding
            while (e.hasMoreElements()) {
                Order each = (Order) e.nextElement();
                outstanding += each.getAmount();
            }
            // print details
            System.out.println("name:" + _name);
            System.out.println("amount" + outstanding);
    }
    改
        void printOwing(double previousAmount) {
            printBanner();
            double outstanding = getOutstanding(previousAmount * 1.2);
            printDetails(outstanding);
        }
    
        void printBanner() {
            // print banner
            System.out.println("**************************");
            System.out.println("***** Customer Owes ******");
            System.out.println("**************************");
        }
    
        double getOutstanding(double initialValue) {
            double result = initialValue;
            Enumeration e = _orders.elements();
            while (e.hasMoreElements()) {
                Order each = (Order) e.nextElement();
                result += each.getAmount();
            }
            return result;
        }
    
        void printDetails(double outstanding) {
            System.out.println("name:" + _name);
            System.out.println("amount" + outstanding);
        }

    6.2 Inline Method(内联函数)

    在函数调用点插入函数本体,然后移除该函数。
    函数的本体与名称同样清楚易懂,间接层太多反而不易理解。

    int getRating() {
        return (moreThanFiveLateDeliveries()) ? 2 : 1;
    }
    
    boolean moreThanFiveLateDeliveries() {
        return _numberOfLateDeliveries > 5;
    }
    
    改
    int getRating() {
        return (_numberOfLateDeliveries > 5) ? 2 : 1;
    }


    6.3 Inline Temp(内联临时变量)

    将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。

    double basePrice = anOrder.basePrice();
    return (basePrice > 1000)
    改
    return (anOrder.basePrice() > 1000)

    6.4 Replace Temp with Query(以查询取代临时变量)
    将一个表达式提炼到一个独立函数中,并将临时变量的引用点替换为对函数的调用。
    临时变量扩展为查询函数,就可以将使用范围扩展到整个类。
    减少临时变量,使函数更短更易维护。让原来的不容易理解的长函数分段化。

        double getPrice() {
            int basePrice = _quantity * _itemPrice;
            double discountFactor;
            if (basePrice > 1000)
                discountFactor = 0.95;
            else
                discountFactor = 0.98;
            return basePrice * discountFactor;
        }
    改
    
        double getPrice() {
            return basePrice() * discountFactor();
        }
    
        private double discountFactor() {
            if (basePrice() > 1000)
                return 0.95;
            else
                return 0.98;
        }
    
        private int basePrice() {
            return _quantity * _itemPrice;
        }

    6.5 Introduce Explaining Variable(引入解释性变量)

    将该复杂表达式的结果放进一个临时变量,以变量名来解释其用途。让冗长的表达式更加可读。

        double price() {
            // price is base price - quantity discount + shipping
            return _quantity * _itemPrice - Math.max(0, _quantity - 500) * _itemPrice * 0.05
                    + Math.min(_quantity * _itemPrice * 0.1, 100.0);
        }
    
    改
        double price() {
            return basePrice() - quantityDiscount() + shipping();
        }
    
        private double quantityDiscount() {
            return Math.max(0, _quantity - 500) * _itemPrice * 0.05;
        }
    
        private double shipping() {
            return Math.min(basePrice() * 0.1, 100.0);
        }
    
        private double basePrice() {
            return _quantity * _itemPrice;
        }

    6.6 Split Temporary Variable(分解临时变量)

    针对每次赋值,创造一个独立、对应的临时变量。
    临时变量会被多次赋值,容易产生理解歧义。
    如果变量被多次赋值(除了“循环变量”和“结果收集变量”),说明承担了多个职责,应该分解。

        double getDistanceTravelled (int time) {
            double result;
            double acc = _primaryForce / _mass;
            int primaryTime = Math.min(time, _delay);
            result = 0.5 * acc * primaryTime * primaryTime;
            int secondaryTime = time - _delay;
            if (secondaryTime > 0) {
            double primaryVel = acc * _delay;
                acc = (_primaryForce + _secondaryForce) / _mass;
                result += primaryVel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;
            }
            return result;
            }
    //acc被赋值了2次,代码比较乱
    改
        double getDistanceTravelled(int time) {
            double result;
            final double primaryAcc = _primaryForce / _mass;
            int primaryTime = Math.min(time, _delay);
            result = 0.5 * primaryAcc * primaryTime * primaryTime;
            int secondaryTime = time - _delay;
            if (secondaryTime > 0) {
                double primaryVel = primaryAcc * _delay;
                final double secondaryAcc = (_primaryForce + _secondaryForce) / _mass;
                result += primaryVel * secondaryTime + 0.5 * secondaryAcc * secondaryTime * secondaryTime;
            }
            return result;
        }

    使用secondaryAcc变量来区分

    6.7 Remove Assignments to Parameters(移除对参数的赋值)

    以一个临时变量取代该参数的位置。
    对参数赋值容易降低代码的清晰度;
    容易混淆按值传递和按引用传递的方式。

        int discount(int inputVal, int quantity, int yearToDate) {
            if (inputVal > 50)
                inputVal -= 2;
            if (quantity > 100)
                inputVal -= 1;
            if (yearToDate > 10000)
                inputVal -= 4;
            return inputVal;
        }
    
    改
        int discount(final int inputVal, final int quantity, final int yearToDate) {
            int result = inputVal;
            if (inputVal > 50)
                result -= 2;
            if (quantity > 100)
                result -= 1;
            if (yearToDate > 10000)
                result -= 4;
            return result;
        }

    加上final,让它遵循不对参数赋值。

    6.8 Replace Method with Method Object(以函数对象取代函数)

    一个大型函数如果包含了很多临时变量,用Extract Method很难拆解,
    可以把函数放到一个新创建的类中,把临时变量变成类的实体变量,再用Extract Method拆解。

    感觉原书的例子不大贴切,略过。

    6.9 Substitute Algorithm(替换算法)

    复杂的算法会增加维护的成本,替换成较简单的算法实现,往往能明显提高代码的可读性和可维护性。

    string foundPerson(string[] people){
        for (int i = 0; i < people.length; i++){
            if (people[i].equals("Don")){
                return "Don";
            }
            if (people[i].equals("John")){
                return "John";
            }
        }
        return "";
    }
    重构后
    string foundPerson(string[] people){
        list oList = Arrays.asList(new String[]{"Don", "John"});
        for (int i = 0; i < people.length; i++){
            if (oList.contains(people[i]))
                return people[i];
        }
        return "";
    }

    感觉也有点不大贴切。

    7.1 Move Method(搬移函数)

    如果一个函数在另一个类中用的更多。在另一个类中添加一个有着类似行为的新函数,将旧函数变成一个单纯的委托函数或者将旧函数删除。
    类的行为做到单一职责,不要越俎代庖。
    如果一个类有太多行为,或一个类与另一个类有太多合作而形成高度耦合,就需要搬移函数。
    观察调用它的那一端、它调用的那一端,已经继承体系中它的任何一个重定义函数。
    根据“这个函数不哪个对象的交流比较多”,决定其移动路径。

    我不大同意作者将透支函数搬到账户类型类,代码略。

    7.2 Move Field(搬移字段)

    某个字段被别一个类更多的用到,把这个类移到另一个类中,修改源字段的所有用户,令它们引用新的字段。

    class A{
    public:
    string m_field;
    }
    class B{
    
    }
    重构后
    
    class A{
    
    }
    class B{
    publuc:
    string m_field;
    }

    7.3 Extract Class(提炼类)

    某个类做了应该由两个类做的事,建立一个新类,将相关字段和函数从旧类搬移到新类中。
    职责单一,松耦合原则。

    class Person {
        public String getName() {
            return _name;
        }
    
        public String getTelephoneNumber() {
            return ("(" + _officeAreaCode + ") " + _officeNumber);
        }
    
        String getOfficeAreaCode() {
            return _officeAreaCode;
        }
    
        void setOfficeAreaCode(String arg) {
            _officeAreaCode = arg;
        }
    
        String getOfficeNumber() {
            return _officeNumber;
        }
    
        void setOfficeNumber(String arg) {
            _officeNumber = arg;
        }
    
        private String _name;
        private String _officeAreaCode;
        private String _officeNumber;
    }
    
    改
    
    class Person {
        public String getName() {
            return _name;
        }
    
        public String getTelephoneNumber() {
            return _officeTelephone.getTelephoneNumber();
        }
    
        TelephoneNumber getOfficeTelephone() {
            return _officeTelephone;
        }
    
        private String _name;
        private TelephoneNumber _officeTelephone = new TelephoneNumber();
    
    }
    
    class TelephoneNumber {
        public String getTelephoneNumber() {
            return ("(" + _areaCode + ") " + _number);
        }
    
        String getAreaCode() {
            return _areaCode;
        }
    
        void setAreaCode(String arg) {
            _areaCode = arg;
        }
    
        String getNumber() {
            return _number;
        }
    
        void setNumber(String arg) {
            _number = arg;
        }
    
        private String _areaCode;
        private String _number;
    }

    7.4 Inline Class(将类内联化)

    正好于Extract Class (提炼类)相反。如果一个类不再承担足够责任、不再有单独存在的理由。将这个类的所有特性搬移到另一个类中,然后移除原类。

    class Person {
        Department _department;
    
        public Department getDepartment() {
            return _department;
        }
    
        public void setDepartment(Department arg) {
            _department = arg;
        }
    }
    
    class Department {
        private String _chargeCode;
        private Person _manager;
    
        public Department(Person manager) {
            _manager = manager;
        }
    
        public Person getManager() {
            return _manager;
        }
    }

    7.5 Hide Delegate(隐藏“委托关系”)

    class Person {
        Department _department;
    
        public Department getDepartment() {
            return _department;
        }
    
        public void setDepartment(Department arg) {
            _department = arg;
        }
    }
    
    class Department {
        private String _chargeCode;
        private Person _manager;
    
        public Department(Person manager) {
            _manager = manager;
        }
    
        public Person getManager() {
            return _manager;
        }
    }
    
    //如果要获取某个员工的经理:
    manager = john.getDepartment().getManager();
    
    //这就暴露了Department的原理,可以在Person类增加方法来减少耦合
    public Person getManager() {
        return _department.getManager();
    }
    
    //这样就可以直接调用了
    manager = john.getManager();

    7.6 Remove Middle Man(移除中间人)

    某个类做了过多的简单委托动作,让客户直接调用受托类。

    A->B->C
    
    重构后
    
    A->B
    A->C

    7.7 Introduce Foreign Method(引入外加函数)

    你需要为提供服务的类增加一个函数,但你无法修改这个类。
    在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
    客户类使用Date类的接口,但Date类没有提供nextDay()的接口,也不能改Date的源码:

    Date newStart = new Date(pre.getYear(), pre.getMonth(), pre.getDate() + 1);
    //重构后
    Date newStart = nextDay(pre);
    
    private static Date nextDay(Date arg){
        return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
    }

    7.8 Introduce Local Extension(引入本地扩展)

    你需要为服务类提供一些额外函数,但你无法修改这个类。
    建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类戒包装类。

  • 相关阅读:
    javascript调用applet
    mysql“Access denied for user 'root'@'localhost'”问题的解决
    VS2010 加载Dll文件
    预处理符号
    什么是lib文件,lib和dll的关系如何[转]
    git常用命令
    VC项目配置基础[转]
    [转]Linux ftp命令的使用方法
    [转]JavaScript创建Applet 标签的属性介绍 以及 Applet调用JavaScript
    When you publish a workflow in Microsoft Dynamics CRM 4.0 after you install Update Rollup 2, you receive Error message
  • 原文地址:https://www.cnblogs.com/starcrm/p/12526877.html
Copyright © 2011-2022 走看看