重构 -改变既有代码的设计 ---- 笔记

目录java

这是一篇《重构 》的总结 ,我在学习的同时并使用它做为参考。这不是一本书的替代品,因此你要想真的想学习里面的内容,买一本书使用这个文章做为参考和指南。git

另外: 建议 评论 还 PR 都是十分欢迎的github

1. TABLE OF CONTENT

3. BAD SMELLS IN CODE(代码的坏味道)

1. Duplicated code (重复的代码)

多个地方使用相同的代码算法

2. Long Method(很长的方法)

一个很长的程序是很难被理解的数据库

3. Large Classes(超级大的类)

当一个类变的愈来愈大的时候,是难以阅读的express

4. Long Parameter List(长参数列表)

长参数是很难去理解,不符合也较难使用编程

5. Divergent Change(发散的改变)

当一个类常常由于不一样缘由发生在不一样的方向发生变化数组

6. Shotgun Surgery(散弹枪修改)

每次你作一小部分修改时,都不的不须要作大量的修改在不少不一样的类中session

7. Feature Envy(依恋情结)

某个方法彷佛在另外的类的兴趣高于本身所处的类架构

8. Data Clumps(数据泥团)

一堆数据杂糅在一块儿(字段, 参数)

9. Primitive Obsession(基本类型偏执)

使用基本类型替代小对象

10. Switch Statements(Switch 惊悚现身)

当程序中出现不少 switch 语句在不少地方,使用多态来进行替换

11. Parallel Inheritance Hierarchies(平行继承类)

每当你为一个类增长一个子类,你不得不为另外一个类增长相应的一个子类

12. Lazy Class(冗余类)

当一个类不足与为其自身买单它就应该被删除

13. Speculative Generality(夸夸其谈将来性)

全部的钩子和特殊状况处理那些不须要的

14. Temporary Field(使人迷惑的临时变量)

一个临时变量仅仅为某种特殊状况作而定,这样的代码让人难以理解

15. Message Chain(过长的消息链)

当一个类请求调用一个对象,可是这个类又在调用其余的方法

16. Middle Man(中间人)

当一个对象委托大部分功能,开发中可能会过分的使用委托模式,致使某个类中的方法大都委托给其余方法处理

17. Inappropriate Intimacy(不恰当的亲密关系)

当两个类过分的亲密,须要将其拆散

18. Alternative Classes with Different Interfaces(殊途同归的类)

类的方法过分类似

19. Incomplete Library Class(不完美的库)

当咱们使用外部依赖库时

20. Data Class(数据类)

不要操做数据类,咱们经过封装它的不变性

21. Refused Bequest(被拒绝的遗赠)

子类不想使用父类的方法

22. Comments(过多没用的注释)

当一个方法使用过分的注释解释其中的逻辑时,说明这个方法应该被重构了。

6. COMPOSING METHODS(从新组织函数))

1. Extract Method(提炼函数)

你能够将一些代码组合起来,而后放到一个方法中

void printOwing(double amount) {
        printBanner();
        //print details
        System.out.println ("name:" + _name);
        System.out.println ("amount" + amount);
    }

to

void printOwing(double amount) {
        printBanner();
        printDetails(amount);
    }

    void printDetails (double amount) {
        System.out.println ("name:" + _name);
    System.out.println ("amount" + amount);
    }

动机

  • 增长代码被复用的机会
    * 阅读方法就像阅读一系列声明同样简单
void printOwing(double previousAmount) {
        Enumeration e = _orders.elements();
        double outstanding = previousAmount * 1.2;
        printBanner();

        // calculate outstanding
        while (e.hasMoreElements()) {
            Order each = (Order) e.nextElement();
            outstanding += each.getAmount();
        }
        printDetails(outstanding);
    }

to

void printOwing(double previousAmount) {
        printBanner();
        double outstanding = getOutstanding(previousAmount * 1.2);
        printDetails(outstanding);
    }

    double getOutstanding(double initialValue) {
        double result = initialValue;
        Enumeration e = _orders.elements();

        while (e.hasMoreElements()) {
            Order each = (Order) e.nextElement();
            result += each.getAmount();
        }
        return result;
    }

2. Inline Method (内联函数)

一个函数本体和函数名称同样容易理解

int getRating() {
        return (moreThanFiveLateDeliveries()) ? 2 : 1;
    }

    boolean moreThanFiveLateDeliveries() {
        return _numberOfLateDeliveries > 5;
    }

to

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

动机

  • 当间接不是必要的时候
  • 当一组方法被严格的分解,会使这个方法变得清晰

3. Inline Temp (内联临时变量)

你申明了一个临时的变量在一段表达里面,而后临时的变量将会阻挡你重构

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

to

return (anOrder.basePrice() > 1000)

Motivation

4. Replace Temp with Query (以查询取代临时变量)

你正在使用临时变量来保存表达式的结果

double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000){
        return basePrice * 0.95;
    }
    else{
        return basePrice * 0.98;
    }

to

if (basePrice() > 1000){
        return basePrice() * 0.95;
    }
    else{
        return basePrice() * 0.98;
    }
    ...
    double basePrice() {
        return _quantity * _itemPrice;
    }

动机

  • 使用方法代替临时变量,类中的任何方法均可以获取信息
  • 这是一个十分重要的步骤在其以前1. Extract Method

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

有一个复杂的表达式

if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
        (browser.toUpperCase().indexOf("IE") > -1) &&
        wasInitialized() && resize > 0 )
    {
        // do something
    }

to

final boolean isMacOs = platform.toUpperCase().indexOf("MAC") >-1;
    final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") >-1;
    final boolean wasResized = resize > 0;
    if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
        // do something
    }

动机

  • 当一个表达式难以理解时

6. Split Temporary Variable (分解临时变量)

你能有一个临时变量声明不止一次,可是它不是循环体中的变量或者要被存储的变量

double temp = 2 * (_height + _width);
    System.out.println (temp);
    temp = _height * _width;
    System.out.println (temp);

to

final double perimeter = 2 * (_height + _width);
    System.out.println (perimeter);
    final double area = _height * _width;
    System.out.println (area);

动机

  • 变量不该该有屡次的声明
  • 使用临时变量在两次不一样的地方,是阅读者十分迷惑的

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

下的代码将对参数进行了赋值

int discount (int inputVal, int quantity, int yearToDate) {
        if (inputVal > 50) {
            inputVal -= 2;
        }
    }

to

int discount (int inputVal, int quantity, int yearToDate) {
        int result = inputVal;
        if (inputVal > 50) {
            result -= 2;
        }
    }

动机

  • 改变内部的对象时能够的,可是不能将这个对象指向别的对象
  • 参数的做用仅仅是表达传递对象

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

将这个函数放进一个单独的对象,如此一来局部变量就变成对象内部的字段,而后你能够在同一个对象中将这个大型函数分解为多个小型函数

class Order...
        double price() {
            double primaryBasePrice;
            double secondaryBasePrice;
            double tertiaryBasePrice;
            // long computation;
            ...
        }

to

class Order...
        double price(){
            return new PriceCalculator(this).compute()
        }
    }

    class PriceCalculato...
    compute(){
        double primaryBasePrice;
        double secondaryBasePrice;
        double tertiaryBasePrice;
        // long computation;
        return ...
    }

动机

  • 当一个方法有不少的本地变量时进行分解时不容易的

它的样本本不该该这样的重构,可是为显示这样作的方法

Class Account
        int gamma (int inputVal, int quantity, int yearToDate) {
            int importantValue1 = (inputVal * quantity) + delta();
            int importantValue2 = (inputVal * yearToDate) + 100;
            if ((yearToDate - importantValue1) > 100)
            importantValue2 -= 20;
            int importantValue3 = importantValue2 * 7;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }
    }

to

class Gamma...
        private final Account _account;
        private int inputVal;
        private int quantity;
        private int yearToDate;
        private int importantValue1;
        private int importantValue2;
        private int importantValue3;

        Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) {
            _account = source;
            inputVal = inputValArg;
            quantity = quantityArg;
            yearToDate = yearToDateArg;
        }

        int compute () {
            importantValue1 = (inputVal * quantity) + _account.delta();
            importantValue2 = (inputVal * yearToDate) + 100;
            if ((yearToDate - importantValue1) > 100)
            importantValue2 -= 20;
            int importantValue3 = importantValue2 * 7;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }

        int gamma (int inputVal, int quantity, int yearToDate) {
            return new Gamma(this, inputVal, quantity,yearToDate).compute();
        }

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";
            }
            if (people[i].equals ("Kent")){
                return "Kent";
            }
        }
        return "";
    }

to

String foundPerson(String[] people){
        List candidates = Arrays.asList(new String[] {"Don", "John","Kent"});
    for (int i = 0; i<people.length; i++)
        if (candidates.contains(people[i]))
            return people[i];
    return "";
    }

动机

  • 打破一些复杂的概念
  • 使算法更容易修改
  • 替换一个大的复杂的算法是十分困难的,让算法变的简单更容易对算法进行替换

7. Moving features between elements(移动对象)

10. Move method (移动方法)

在进行方法的初始定义的时候要想下之后会不会有其余的类也将会用到它

一个类它一般会建立一个新的简单的方法体, 同时它会将9⃣旧的方法作一个简单的委托或者移除它

class Class1 {
        aMethod()
    }

    class Class2 {  }

to

class Class1 {  }

    class Class2 {
        aMethod()
    }

动机

当一个类作了不少工做,或者这个类过分的耦合

11. Move field (移动字段)

当一个字段被定义的时候,可能不只被不止一个类使用。
建立一个字段在一个目标类中,而后改变全部的拥有者

class Class1 {
        aField
    }

    class Class2 {  }

to

class Class1 {  }

    class Class2 {
        aField
    }

动机

若是一个字段被超过多个类引用

12. Extract Class (提取类)

你有一个类,可是这个类作了它份外的事情

建立一个新的类,而后将相关字段移入到新的类中

class Person {
        name,
        officeAreaCode,
        officeNumber,
        getTelephoneNumber()
    }

to

class Person {
        name,
        getTelephoneNumber()
    }

    class TelephoneNumber {
        areaCode,
        number,
        getTelephoneNumber()
    }

动机

类随着业务的增加在变化

在合适的时候进行分解它

  • 类似的方法组合在一块儿
  • 数据子集一般一块儿变化或者相互依赖

13. Inline Class (一致的类)

一个其实没作多少事情的类

将这个类整合到另一个类中,而后删除这个类

class Person {
        name,
        getTelephoneNumber()
    }

    class TelephoneNumber {
        areaCode,
        number,
        getTelephoneNumber()
    }

to

class Person {
        name,
        officeAreaCode,
        officeNumber,
        getTelephoneNumber()
    }

动机

在重构的时候将这个类的基本信息移入到另一个类中,而后在移除这个类

14. Hide Delegate (隐藏委托)

客户端其实调用的是对象的委托类
在服务端建立一个方法,而后隐藏这个委托类

class ClientClass {
        //Dependencies
        Person person = new Person()
        Department department = new Department()
        person.doSomething()
        department.doSomething()
    }

to

class ClientClass {
        Person person = new Person()
        person.doSomething()
    }

    class Person{
        Department department = new Department()
        department.doSomething()
    }

解决方法

class ClientClass{
        Server server = new Server()
        server.doSomething()
    }

    class Server{
        Delegate delegate = new Delegate()
        void doSomething(){
            delegate.doSomething()
        }
    }
    //委托类其实隐藏在客户类里面
    // 改变不会传播到客户端那边,由于它以后影响到服务端这边
    class Delegate{
        void doSomething(){...}
    }

动机

关键在于封装
类应该尽可能的使用其余的类
> manager = john.getDepartment().getManager();

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;
        }
        ...

to

> manager = john.getManager();

class Person {
        ...
        public Person getManager() {
            return _department.getManager();
        }
    }

15. Remove Middle Man (移除中间人)

一个类经过代理干了太多的事情
让客户直接调用委托

class ClientClass {
        Person person = new Person()
        person.doSomething()
    }

    class Person{
        Department department = new Department()
        department.doSomething()
    }

to

class ClientClass {
        //Dependencies
        Person person = new Person()
        Department department = new Department()
        person.doSomething()
        department.doSomething()
    }

动机

当客户类使用过多的中间人调用委托的方法

16. Introduce Foreign Method (引入外加的函数)

一个类是引用的外部开源包,可是不能修改其内部的逻辑
建立一个新的方法在这个类中,并以第一个参数的形式传入一个服务类实例

Date newStart = new Date(previousEnd.getYear(),previousEnd.getMonth(),previousEnd.getDate()+1);

to

Date newStart = nextDay(previousEnd);

    private static Date nextDay(Date date){
        return new Date(date.getYear(),date.getMonth(),date.getDate()+1);
    }

动机

当你使用一个类,这个类你又不能对其进行修改的时候能够采用这样方式

17. Introduce Local Extension (引入本地扩展)

你须要为一个服务类提供一些额外的方法,可是你没法修改这个子类
建立一个新的类,使它包含这些额外的方法。这个扩展的类成为源类的子类或者包装类

class ClientClass(){

        Date date = new Date()
        nextDate = nextDay(date);

        private static Date nextDay(Date date){
            return new Date(date.getYear(),date.getMonth(),date.getDate()+1);
        }
    }

to

class ClientClass() {
        MfDate date = new MfDate()
        nextDate = nextDate(date)
    }
    class MfDate() extends Date {
        ...
        private static Date nextDay(Date date){
            return new Date(date.getYear(),date.getMonth(),date.getDate()+1);
        }
    }

动机

当咱们使用 16. Introduce Foreign Method 咱们须要在这个类中添加额外的方法

8. ORGANIZING DATA (组织数据)

18. Self Encapsulate Field (对字段获取进行封装)

你能够直接获取对象,可是这样的话会变得愈来愈复杂
经过建立setting getting 方法来获取这些字段

private int _low, _high;
    boolean includes (int arg) {
        return arg >= _low && arg <= _high;
    }

to

private int _low, _high;
    boolean includes (int arg) {
        return arg >= getLow() && arg <= getHigh();
    }
    int getLow() {return _low;}
    int getHigh() {return _high;}

动机

容许子类能够覆盖如何get方法,而且这样的话它更加的支持灵活的管理,例如延迟加载

19. Replace Data Value with Object (用对象替换数据值)

当你有个数据项须要进行添加数据或行为
将数据项转换为对象

class Order...{
        private String _customer;
        public Order (String customer) {
            _customer = customer;
        }
    }

to

class Order...{
        public Order (String customer) {
            _customer = new Customer(customer);
        }
    }

    class Customer {
        public Customer (String name) {
            _name = name;
        }
    }

动机
简单的数据对象并不简单

20. Change Value to Reference (将值改成引用)

你有个类拥有不少单个对象,这些对象须要用一个单独的对象替代
将这个对象转换为引用对象

class Order...{
        public Order (String customer) {
            _customer = new Customer(customer);
        }
    }

    class Customer {
        public Customer (String name) {
            _name = name;
        }
    }

to

//Use Factory Method
    //使用工厂方法
    class Customer...
        static void loadCustomers() {
            new Customer ("Lemon Car Hire").store();
            new Customer ("Associated Coffee Machines").store();
            new Customer ("Bilston Gasworks").store();
        }
        private void store() {
            _instances.put(this.getName(), this);
        }
        public static Customer create (String name) {
            return (Customer) _instances.get(name);
        }

动机
引用对象是相似于消费者或者帐单这样的对象,每一个对象表明这一类对象在一个真实的世界,并使用对象表示来测试它们是否相同

21. Change Reference to Value (将引用改成值)

你有一个有一个引用对象是很小,不变,难以管理的
将其转换为值对象

new Currency("USD").equals(new Currency("USD")) // returns false

to

new Currency("USD").equals(new Currency("USD")) // now returns true

动机
使用引用对象是变得越来月复杂,而且引用对象是不变和单一的。尤为在分布式和并发系统中

22. Replace Array with Object (用对象代替数组)

你拥有一个数组,其中这些元素是不一样的
使用一个对象来替换这个数组,将数组的元素赋值在对象的属性上

String[] row = new String[3];
    row [0] = "Liverpool";
    row [1] = "15";

to

Performance row = new Performance();
    row.setName("Liverpool");
    row.setWins("15");

动机

数组应该被用在一些类似的集合对象序列中

23. Duplicate Observed Data (监控数据对象)

可能你有一些domain数据是经过GUI控制的与此同时这些domain数据是须要访问的
往一些实体对象复制一些数据,经过设置观察者来同步两部分数据

动机
为了将代码从用户界面分解到业务处理层

24. Change Unidirectional Association to Bidirectional(将单向联系改成双向联系)

你有两个对象,这两个对象须要使用对方的特征属性,可是目前只有一种链接方式
添加返回指针,而后更改修饰符已更改两个对象

class Order...
        Customer getCustomer() {
            return _customer;
        }
        void setCustomer (Customer arg) {
            _customer = arg;
        }
        Customer _customer;
    }

to

class Order...
        Customer getCustomer() {
            return _customer;
        }
        void setCustomer (Customer arg) {
            if (_customer != null) _customer.friendOrders().remove(this);
            _customer = arg;
            if (_customer != null) _customer.friendOrders().add(this);
        }
        private Customer _customer;

        class Customer...
            void addOrder(Order arg) {
                arg.setCustomer(this);
            }
            private Set _orders = new HashSet();

            Set friendOrders() {
                /** should only be used by Order */
                return _orders;
            }
        }
    }

    // Many to Many
    class Order... //controlling methods
        void addCustomer (Customer arg) {
            arg.friendOrders().add(this);
            _customers.add(arg);
        }
        void removeCustomer (Customer arg) {
            arg.friendOrders().remove(this);
            _customers.remove(arg);
        }
    class Customer...
        void addOrder(Order arg) {
            arg.addCustomer(this);
        }
        void removeOrder(Order arg) {
            arg.removeCustomer(this);
        }
    }

动机
当对象引用须要互相引用的时候,你应该采用这种方法

25. Change Bidirectional Association to Unidirectional (将单向改成双向的联系)

当你有个双向联系的类,可是在后期一个类不在须要另外一个类中的属性了
扔掉不须要的联系

动机
当双向联系不在须要,减小复杂度,移除僵尸对象,消除相互依赖

26. Replace Magic Number with Symbolic Constant (使用符号来替代魔法字符串)

你有一个特定含义的字符串

建立一个常量,名称根据它的意思命名而后替换那个数字

double potentialEnergy(double mass, double height) {
        return mass * 9.81 * height;
    }

to

double potentialEnergy(double mass, double height) {
        return mass * GRAVITATIONAL_CONSTANT * height;
    }
    static final double GRAVITATIONAL_CONSTANT = 9.81;

动机
避免使用魔法数字

27. Encapsulate Field (封装字段)

这里有一个公共的字段
将它改成私有的并提供访问函数

public String _name

to

private String _name;
    public String getName() {
        return _name;
    }
    public void setName(String arg) {
        _name = arg;
    }

动机
你应该将你数据公开

28. Encapsulate Collection (封装集合)

一个返回几个的方法
确保返回一个只读的影像对象,而后提供添加和移除方法

class Person {
        Person (String name){
            HashSet set new HashSet()
        }
        Set getCourses(){}
        void setCourses(:Set){}
    }

to

class Person {
        Person (String name){
            HashSet set new HashSet()
        }
        Unmodifiable Set getCourses(){}
        void addCourses(:Course){}
        void removeCourses(:Course){}
    }

动机

  • 封装减小了拥有类和和其客户端的耦合
  • getter 方法不该该返回集合的自己
  • getter方法应返回对集合进行操做的内容并隐藏其中没必要要的细节
  • 这个几个不该该有setter方法,只能添加和移除操做

29. Remove Record with data class (在数据类中移除 记录值)

你必须面对一个记录值在传统的编程环境中
使用一个僵尸数据对象代替记录值

动机

  • 复制一个传奇代码
  • 使用传统的API编程和数据库来代替一个记录值

30. Replace Type Code with Class (使用类来替代类别代码)

一个类拥有数字类别码,不能影响其行为
使用类来代替数字

class Person{
        O:Int;
        A:Int;
        B:Int;
        AB:Int;
        bloodGroup:Int;
    }

to

class Person{
        bloodGroup:BloodGroup;
    }

    class BloodGroup{
        O:BloodGroup;
        A:BloodGroup;
        B:BloodGroup; 
        AB:BloodGroup;
    }

动机
静态类别检查

31. Replace Type Code with Subclasses (使用子类来替代类别代码)

在一个类中拥有一个不变的类型码影响整个类的行为
使用子类来替代这个不变的类型码

class Employee...
        private int _type;

        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        Employee (int type) {
            _type = type;
        }
    }

to

abstract int getType();
    static Employee create(int type) {
        switch (type) {
            case ENGINEER:
                return new Engineer();
            case SALESMAN:
                return new Salesman();
            case MANAGER:
                return new Manager();
            default:
                throw new IllegalArgumentException("Incorrect type code value");
        }
    }

动机

  • 执行不一样的代码逻辑取决于这个type的值
  • 当每一个type对象有着惟一的特征
  • 应用于架构体

32. Replace Type Code with State/Strategy(经过状态模式或者策略模式来代替类型码)

在类中有个类型码,并经过这个类型码来影响行为,可是你不能使用子类
经过状态对象来代替这个类型码

class Employee {
        private int _type;

        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        Employee (int type) {
            _type = type;
        }
        int payAmount() {
            switch (_type) {
                case ENGINEER:
                    return _monthlySalary;
                case SALESMAN:
                    return _monthlySalary + _commission;
                case MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new RuntimeException("Incorrect Employee");
                }
            }
        }
    }

to

class Employee...
        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        void setType(int arg) {
            _type = EmployeeType.newType(arg);
        }
        class EmployeeType...
            static EmployeeType newType(int code) {
                switch (code) {
                    case ENGINEER:
                        return new Engineer();
                    case SALESMAN:
                        return new Salesman();
                    case MANAGER:
                        return new Manager();
                    default:
                        throw new IllegalArgumentException("Incorrect Employee Code");
                }
            }
        }
        int payAmount() {
            switch (getType()) {
                case EmployeeType.ENGINEER:
                    return _monthlySalary;
                case EmployeeType.SALESMAN:
                    return _monthlySalary + _commission;
                case EmployeeType.MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new RuntimeException("Incorrect Employee");
            }
        }
    }

动机

  • 31. Replace Type Code with Subclasses 是类似的,可是它可使用在类型码发生了改变在对象的生命周期发生了变化或者另外一个缘由阻止了子类的变化,则可使用它
  • 它一般是和状态模式或者策略模式配合使用

32. Replace Subclass with Fields(用字段代替子类)

你的子类仅在返回常数变量数据变量的方法中有所不一样
将这个方法提高到父类中,并移除这个子类

abstract class Person {
        abstract boolean isMale();
        abstract char getCode();
        ...
    }
    class Male extends Person {
            boolean isMale() {
            return true;
        }
        char getCode() {
            return 'M';
        }
    }
    class Female extends Person {
        boolean isMale() {
            return false;
        }
        char getCode() {
            return 'F';
        }
    }

to

class Person{
        protected Person (boolean isMale, char code) {
            _isMale = isMale;
            _code = code;
        }
        boolean isMale() {
            return _isMale;
        }
        static Person createMale(){
            return new Person(true, 'M');
        }
        static Person createFemale(){
            return new Person(false, 'F');
        }
    }

动机

  • 当子类的某个方法不足与继续存在
  • 将这个子类完全删除,并将这个字段上移到父类中
  • 删除额外的子类

9. SIMPLIFYING CONDITIONAL EXPRESSIONS(简化条件表达式)

33. Decompose Conditional (分解条件)

你有一个复杂的条件(大量的if else then )
使用额外的方法代替这个表达式,将then 放在一部分,else 放在一部分

if (date.before (SUMMER_START) || date.after(SUMMER_END))
        charge = quantity * _winterRate + _winterServiceCharge;
    else 
        charge = quantity * _summerRate;

to

if (notSummer(date))
        charge = winterCharge(quantity);
    else 
        charge = summerCharge (quantity);

动机

  • 将条件表达式高亮,这样的话你能清楚将其分开
  • 对分叉以后的结果进行高亮

34. Consolidate Conditional Expression

double disabilityAmount() {
    if (_seniority < 2) return 0;
    if (_monthsDisabled > 12) return 0;
    if (_isPartTime) return 0;
    // compute the disability amount

to

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

35. Consolidate Duplicate Conditional Fragments (合并重复的条件片断)

在条件表达式的每一个分支上有着相同的一片代码
将这段重复代搬移到条件表达式以外

if (isSpecialDeal()) {
        total = price * 0.95;
        send();
    }
    else {
        total = price * 0.98;
        send();
    }

to

if (isSpecialDeal()) {
        total = price * 0.95;
    }
    else {
        total = price * 0.98;
    }
    send();

动机
使得变量清晰并保持相同

36. Remove Control Flag (移除控制标记)

在一系列的布尔表达式中,某个变量带有“控制标记”的做用
已break或者return语句取代控制标记

void checkSecurity(String[] people) {
        boolean found = false;
            for (int i = 0; i < people.length; i++) {
                if (! found) {
                    if (people[i].equals ("Don")){
                        sendAlert();
                        found = true;
                    }
                    if (people[i].equals ("John")){
                        sendAlert();
                        found = true;
                    }
                }
        }
    }

to

void checkSecurity(String[] people) {
        for (int i = 0; i < people.length; i++) {
            if (people[i].equals ("Don")){
                sendAlert();
                break; // or return
            }
            if (people[i].equals ("John")){
                sendAlert();
                break; // or return
            }
        }
    }

动机

  • 控制标记的做用是在于决定是否继续下面流程,可是现代语言注重于使用breakcontinue
  • 确保真实的条件表达式是清晰的

37. Replace Nested Conditional with Guard Clauses (以卫语句取代嵌套的条件表达式)

函数的条件逻辑令人难以看清正常的执行路径
使用卫语句表现全部的特殊状况

double getPayAmount() {
        double result;
        if (_isDead) result = deadAmount();
        else {
            if (_isSeparated) result = separatedAmount();
            else {
                if (_isRetired) result = retiredAmount();
                else result = normalPayAmount();
            };
        }
        return result;
    };

to

double getPayAmount() {
        if (_isDead) return deadAmount();
        if (_isSeparated) return separatedAmount();
        if (_isRetired) return retiredAmount();
        return normalPayAmount();
    };

动机

  • 若是这个条件是非同寻常的条件,检查这条件是否符合而后返回true
  • 这样大度的检查被称为“卫语句”
  • 对某一条分支已特别的重,若是使用if-then-else 结构,你对if分支和else分支的重要性是同等的
  • 各个分支具备同同样的重要性
  • 取代以前的观念 “每一个函数只能有一个入口和一个出口”

38. Replace Conditional with Polymorphism (以多态取代条件表达式)

你手上有一个条件表达式,它根据对象的类型的不一样选择不一样的行为
将条件表达式的全部分支放进一个子类内的覆盖函数中,而后将原始函数声明为抽象函数

class Employee {
        private int _type;

        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        Employee (int type) {
            _type = type;
        }
        int payAmount() {
            switch (_type) {
                case ENGINEER:
                    return _monthlySalary;
                case SALESMAN:
                    return _monthlySalary + _commission;
                case MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new RuntimeException("Incorrect Employee");
                }
            }
        }
    }

to

class Employee...
        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        void setType(int arg) {
            _type = EmployeeType.newType(arg);
        }
        int payAmount() {
            return _type.payAmount(this);
        }
    }

    class Engineer...
        int payAmount(Employee emp) {
            return emp.getMonthlySalary();
        }
    }

动机

  • 若是对象的行为因其类型而异,请避免编写显示的条件
  • Switch 声明在面向对象语言中应该尽可能少的被使用

39. Introduce Null Object (引入Null 对象)

你不得不检查对象是否为Null对象
将null值替换为null对象

if (customer == null){
        plan = BillingPlan.basic();
    } 
    else{
        plan = customer.getPlan();
    }

to

class Customer {

    }

    class NullCusomer extends Customer {

    }

动机

  • 对象根据其类型作正确的事情,Null对象也应该遵照这个规则

40. Introduce Assertion (引入断言)

某段代码须要对程序状态作出某种假设
已断言明确表现这种假设

double getExpenseLimit() {
        // should have either expense limit or a primary project
        return (_expenseLimit != NULL_EXPENSE) ?
            _expenseLimit:
            _primaryProject.getMemberExpenseLimit();
    }

to

double getExpenseLimit() {
        Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null);
        return (_expenseLimit != NULL_EXPENSE) ?
            _expenseLimit:
            _primaryProject.getMemberExpenseLimit();
    }

动机

  • 断言是一种明确表达会为true的行为
  • 当断言失败时每每是一个非受控的异常
  • 断言每每在生产代码中移除
  • 交流层面 : 断言能使程序的阅读者理解代码所作的假设
  • 在调试的角度 : 断言能使编程者尽量近的接触bug

10. MAKING METHOD CALLS SIMPLER (简化函数的调用)

41. Rename method (函数更名)

函数的名称不能表达函数的用途
修改函数名称

getinvcdtlmt()

to

getInvoiceableCreditLimit

动机
函数的名称最好能表达函数的意图

42. Add Parameter (添加参数)

某个函数须要从调用端获得更多的信息
为此函数添加一个对象函数,让改对象带进函数所须要信息

getContact()

to

getContact(:Date)

动机
在改变方法以后,你得到更多的信息

43. Remove Parameter(移除参数)

一个参数不在函数中使用了
移除它

getContact(:Date)

to

getContact()

动机
一个参数再也不使用还留着它干吗?

44. Separate Query from Modifier (将查询函数和修改函数分离)

某个函数即返回函数的状态值,又修改对象的状态
建立两个不一样的函数,其中一个负责查询,另外一个负责修改

getTotalOutStandingAndSetReadyForSummaries()

to

getTotalOutStanding()
    SetReadyForSummaries()

动机
将有反作用的方法和没有反作用的方法分开

45. Parameterize Method (令函数携带参数)

若干函数作了相似的工做,但在函数本体中却包含了不一样的值
建立单一函数,已参数表达那些不一样的值

fivePercentRaise()
    tenPercentRaise()

to

raise(percentage)

动机
移除重复的代码提升灵活度

46. Replace Parameter with Explicit Methods (已明确函数取代参数)

🈶一个函数,其中彻底取决于参数值而采起不一样的行为
针对该函数的每个可能值,创建一个独立函数

void setValue (String name, int value) {
        if (name.equals("height")){}
            _height = value;
            return;
        }
        if (name.equals("width")){
            _width = value;
            return;
        }
        Assert.shouldNeverReachHere();
    }

to

void setHeight(int arg) {
        _height = arg;
    }
    void setWidth (int arg) {
        _width = arg;
    }

动机

  • 避免条件行为
  • 在编译器进行检查
  • 清晰的接口

47. Preserve Whole Object (保持对象的完整)

你从某个对象支行取出若干值,将他们做为某一次函数调用时的参数
改成传递一整个对象

int low = daysTempRange().getLow();
    int high = daysTempRange().getHigh();
    withinPlan = plan.withinRange(low, high);

to

withinPlan = plan.withinRange(daysTempRange());

动机

  • 使参数列表在变化的时候具备鲁棒性
  • 使代码更与易读
  • 在代码中移除类似的重复的代码
  • 消极的 : 增长了函数参数在调用时的依赖

48. Replace Parameter with Method (已函数取代参数)

对象调用某个函数,并将其所得的结果做为参数,传递给另外一个函数。而接受该参数的函数自己也可以调用钱一个函数
将函数接受者去除该项参数,并直接调用前一个函数

int basePrice = _quantity * _itemPrice;
    discountLevel = getDiscountLevel();
    double finalPrice = discountedPrice (basePrice, discountLevel);

to

int basePrice = _quantity * _itemPrice;
    double finalPrice = discountedPrice (basePrice);

动机

  • 若是一个函数能够经过其余的途径得到参数值,那么它就不该该经过参数取得该值
  • 若是你能有其余的方法或者相同的计算值
  • 若是要在对调用对象的引用的其余对象上调用方法

49. Introduce Parameter Object (引入参数对象)

某些参数老是很天然的同时出现
以一个对象取代这些参数

class Customer{
        amountInvoicedIn (start : Date, end : Date)
        amountReceivedIn (start : Date, end : Date)
        amountOverdueIn (start : Date, end : Date)
    }

to

class Customer{
        amountInvoicedIn (: DateRange)
        amountReceivedIn (: DateRange)
        amountOverdueIn (: DateRange)
    }

动机

  • 减小参数列表的长度
  • 使代码看的更加简洁

50. Remove Setting Method (移除设值函数)

类中的某个字段应该在对象建立时被设值,而后就再也不改变
去掉该字段的全部设值函数

class Employee{
        setImmutableValue()
    }

to

class Employee{
        ¯\_(ツ)_/¯
    }

动机
确保你的清晰的目的 : 若是你想你的字段在建立以后就不要被改变了,你就不该该提供一个setting方法用于确保你的字段是否不被改变的

51. Hide Method (隐藏函数)

有一个函数,历来没有被其余任何类用到
将这个函数修改成 private

class Employee{
        public method()
    }

to

class Employee{
        private method()
    }

动机
若是一个方法不须要被外部调用,那么就应该讲这个方法隐藏

52. Replace Constructor with Factory Method (已工厂函数取代构造函数)

在建立对象时不只仅是作简单的健够动做
将构造函数替换为工厂函数

Employee (int type) {
        _type = type;
    }

to

static Employee create(int type) {
        return new Employee(type);
    }

建立一个对象依赖于其子类,构造函数只能返回单一类型的对象,所以你须要将构造函数替换为一个工厂函数

53. Encapsulate Downcast (封装向下转型)

某个函数返回的对象,须要由调用者执行向下转型
将向下转型动做转移到函数中

Object lastReading() {
        return readings.lastElement();
    }

to

Reading lastReading() {
        return (Reading) readings.lastElement();
    }

动机
将一个方法最有效的返回值进行返回给函数的调用者
若是类型是准确的,检查使用这个对象的方法并提供一个更为有效的方法

54. Replace Error Code with Exception (以异常取代错误码)

某个函数返回一个特定的代码,用以表示某种错误状况
改用异常将其抛出去

int withdraw(int amount) {
        if (amount > _balance)
            return -1;
        else {
            _balance -= amount;
            return 0;
        }
    }

to

void withdraw(int amount) throws BalanceException {
        if (amount > _balance)
            throw new BalanceException();
        _balance -= amount;
    }

动机
当一个程序在发生了一个不可处理的错误时,你须要使这个函数的调用者知道。向上抛出异常,让上层调用者知道

55. Replace Exception with Test (已测试取代异常)

面对一个调用者能够预先检查的条件,你抛出了一个异常
修改调用者,使它在调用函数以前先作检查

double getValueForPeriod (int periodNumber) {
        try {
            return _values[periodNumber];
        } catch (ArrayIndexOutOfBoundsException e) {
            return 0;
        }
    }

to

double getValueForPeriod (int periodNumber) {
        if (periodNumber >= _values.length)
            return 0;
        return _values[periodNumber];
}

动机

  • 不要过分使用异常,它们应该被用在检查异常上
  • 不要用它来代替条件测试
  • 检查异常错误条件在调用方法以前

11. DEALING WITH GENERALIZATION (处理归纳关系)

56. Pull up field (字段上移)

两个子类拥有相同的字段
将该字段移到超类中

class Salesman extends Employee{
        String name;
    }
    class Engineer extends Employee{
        String name;
    }

to

class Employee{
        String name;
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

动机

  • 删除重复的代码
  • 容许将子类的行为转移到父类中

57. Pull Up Method (构造函数本体上移)

你在各个子类中拥有一些构造函数,他们的本体几乎彻底一致
在超类中新建一个构造函数,并在子类构造函数中调用它

class Salesman extends Employee{
        String getName();
    }
    class Engineer extends Employee{
        String getName();
    }

to

class Employee{
        String getName();
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

动机

  • 消除重复的行为

58. Pull Up Constructor Body (构造函数本体上移)

在子类中的构造函数与父类中的构造函数是相同的
在超类中建立一个构造函数,并在子类构造函数中调用它

class Manager extends Employee...
        public Manager (String name, String id, int grade) {
        _name = name;
        _id = id;
        _grade = grade;
    }

to

public Manager (String name, String id, int grade) {
        super (name, id);
        _grade = grade;
    }

动机

  • 构造函数与方法不一样
  • 消除重复的代码

59. Push Down Method (方法下移)

父类的某个方法至于某个子类相关
将其移到子类中

class Employee{
        int getQuota();
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

to

class Salesman extends Employee{
        int getQuota();
    }
    class Engineer extends Employee{}

动机
当方法只在子类中显现

60. Push Down Field (字段下移)

超类的字段只在某个子类中用到
将这个字段移到须要它的那些子类中去

class Employee{
        int quota;
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

to

class Salesman extends Employee{
        int quota;
    }
    class Engineer extends Employee{}

动机
当一个字段只在子类中使用时

61. Extract Subclass (提炼子类)

类中的某些特性只被某些实例用到
新建一个子类,将上面所说的那一部分特性移到子类中去

class JobItem   {
        getTotalPrices()
        getUnitPrice()
        getEmployee()
    }

to

JobItem {
        getTotalPrices()
        getUnitPrice()
    }
    class class LabotItem extends JobItem   {
        getUnitPrice()
        getEmployee()
    }

动机
当一个类的行为只用在某些实例中而不用在其余类中

62. Extract Superclass (提炼超类)

两个类具备类似特性
建立一个父类,而后将这两个类中相同的部分移到父类中,而后在继承这个父类

class Department{
        getTotalAnnualCost()
        getName()
        getHeadCount
    }
    class Employee{
        getAnnualCost()
        getName()
        getId
    }

to

class Party{
        getAnnualCost()
        getName()
    }
    class Department {
        getAnnualCost()
        getHeadCount
    }
    class Employee {
        getAnnualCost()
        getId
    }

动机
当两个类有过多类似的地方的时候,就须要考虑下是否须要将这个类进行下抽象了

63. Extract Interface (提炼接口)

若干客户使用类接口中的同一个子类,或者两个类的接口有相同的部分
将相同的子集提炼到一个独立接口中

class Employee {
        getRate()
        hasSpecialSkill()
        getName()
        getDepartment()
    }

to

interface Billable  {
        getRate()
        hasSpecialSkill()
    }
    class Employee implements Billable  {
        getRate
        hasSpecialSkill()
        getName()
        getDepartment()
    }

动机

  • 若一个类的子集明确被一系列的客户使用
  • 若是一个类须要和多个类处理并能处理肯定的请求

64. Collapse Hierarchy (折叠继承体系)

超类和子类无太大区别
将它们合为一个

class Employee{ }
    class Salesman extends Employee{    }

to

class Employee{ }

动机
该子类没有带来任何价值

65. Form Template Method (塑造模板函数)

有些子类,其中对应的某些函数以相同顺序执行相似的操做,但各个操做的细节上有所不一样
将这些操做分别放进独立函数中,并保持它们都有相同的签名,因而原函数也就变得相同了。而后将原函数上移到超类

class Site{}
    class ResidentialSite extends Site{
        getBillableAmount()
    }
    class LifelineSite extends Site{
        getBillableAmount()
    }

to

class Site{     
        getBillableAmount()
        getBaseAmount()
        getTaxAmount()
    }
    class ResidentialSite extends Site{
        getBaseAmount()
        getTaxAmount()
    }
    class LifelineSite extends Site{
        getBaseAmount()
        getTaxAmount()
    }

动机

66. Replace Inheritance with Delegation (以委托取代继承)

某个子类只使用了超类接口中的一部分,或是根本不须要继承而来的数据
_在子类中建立一个字段用以保存超类,调整子类函数,令它改2而委托超类:而后去除二者之间的继承关系

class Vector{
        isEmpty()
    }

    class Stack extends Vector {}

to

class Vector {
        isEmpty()
    }

    class Stack {
        Vector vector
        isEmpty(){
            return vector.isEmpty()
        }
    }

动机

  • 当你使用委托类的时候会对你使用的数据字段更加的清晰
  • 在你控制以外的方法都会被忽略,你只须要关注于自身

67. Replace Delegation with Inheritance (以继承取代委托)

你在两个类之间使用简单的委托关系,并常常为整个接口编写许多不少简单的委托函数
让委托类继承委托类

class Person {
        getName()
    }

    class Employee {
        Person person
        getName(){
            return person.getName()
        }
    }

to

class Person{
        getName()
    }
    class Employee extends Person{}

动机

  • 若是你使用全部的方法在委托类中
  • 若是你不能使用委托类的全部函数,那么久不该该使用它

12. BIG REFACTORINGS (大型重构)

68. Tease Apart Inheritance (梳理并分解继承体系)

某个继承体系同时承担两项责任
创建两个继承体系,并经过委托关系让其中一个能够调用另一个

class Deal{}
    class ActiveDeal extends Deal{}
    class PassiveDeal extends Deal{}
    class TabularActiveDeal extends ActiveDeal{}
    class TabularPassiveDeal extends PassiveDeal{}

to

class Deal{
        PresentationStyle presettationStyle;
    }
    class ActiveDeal extends Deal{}
    class PassiveDeal extends Deal{}

    class PresentationStyle{}
    class TabularPresentationStyle extends PresentationStyle{}
    class SinglePresentationStyle extends PresentationStyle{}

动机

  • 过分的继承会致使代码重复
  • 若是继承体系中的某一特定层级上的全部类,其子类名称都已相同的形容词开始,那么这个体系可能肩负着两项不一样的责任。
    | |

69. Convert Procedural Design to Objects (过程化设计转为对象设计)

你手上有些传统过程化风格的代码
将数据记录变成对象,将大块的行为分红小块,并将行为移入相关对象中

class OrderCalculator{
        determinePrice(Order)
        determineTaxes(Order)
    }
    class Order{}
    class OrderLine{}

to

class Order{
        getPrice()
        getTaxes()
    }
    class OrderLine{
        getPrice()
        getTaxes()
    }

动机
使用面向对象思想进行变成

70. Separate Domain from Presentation (将领域和表述/显示分离)

某些GUI类中包含了领域逻辑
将领域逻辑分离出来吗,为他们建立独立的领域类

class OrderWindow{}

to

class OrderWindow{
        Order order;
}

动机

  • 分裂两个过于复杂的代码,使他们更易于修改
  • 容许多层风格编写的程序
  • 这是值得被使用的

71. Extract Hierarchy (提炼继承体系)

有某个类作了太多的工做其中一部分工做是以大量的条件表达式完成的
建立一个继承体系,已一个子类来表达某一种特殊的状况

class BillingScheme{}

to

class BillingScheme{}
    class BusinessBillingScheme extends BillingScheme{}
    class ResidentialBillingScheme extends BillingScheme{}
    class DisabilityBillingScheme extends BillingScheme{}

动机

  • 一个类实现一个概念演变成实现多个概念
  • 保持单一责任

GitHub地址欢迎Star Fork Follower

相关文章
相关标签/搜索