• 利用多态重构为带参方法


    《重构之美》之二

    logo_white 我在阅读遗留代码时,经常发现存在这样一种情形。在一个类中存在两个方法,它们做了相似的工作,区别仅在于方法内部某些对象的类型。例如:
    public class WorkSheet{
        public void fillHeader() {
            Header header = createHeader();
            for (String title:header.getTitles()) {
                fillCell(title);
            }
        }
        public void fillBody() {
            CellGroup cellGroup = createCellGroup();
            for (Cell cell:cellGroup.getCells()) {
                fillCell(cell.getText());
            }
        }
    }

    方法fillHeader()和fillBody()的目的都是从对象中获得字符串数组,然后将其填充到单元格中。区别在于,获得字符串数组的对象并不相同。前者为Header对象,后者为CellGroup对象。我们可以为其提供一个抽象的接口,以实现代码的有效重用:
    public interface TextDataSource {
        public String[] getTextArea();
    }

    然后,让Header和CellGroup类均实现该接口。由于CellGroup并没有直接定义返回字符串数组的方法,而是通过返回的Cell对象获得text值,因此,需要将这部分实现封装到getTextArea()方法中:
    public class Header implements TextDataSource {
        @Override
        public String[] getTextArea() {
            //这里的实现即为原有的getTitles()实现
            //可以保留原有的方法,并在本方法中指向该方法
            //也可以利用Rename Method手法,直接更名该方法
        }
    }
    public class CellGroup implements TextDataSource {
        @Override
        public String[] getTextArea() {
            List<String> textArea = new ArrayList<String>();
            for (Cell cell:this.getCells()) {
                textArea.add(cell.getText());
            }
            return textArea.toArray();
        }
    }

    现在,就可以重构原有的WorkSheet类了。
    public class WorkSheet{
        public void fillSheet(TextDataSource dataSource) {
            for (String text:dataSource.getTextArea()) {
                fillCell(text);
            }
        }
    }

    具体需要填充什么内容,可以在调用fillSheet()方法时,根据传入的参数对象来决定。经过重构之后,WorkSheet类中的重复代码得到了移除,且具有了更好的扩展性。

    这一重构手法与Parameterize Method要解决的坏味道相似,同样对相似方法提取了共同的参数,但实现的本质完全不同。它利用了多态的原理,通过对抽象方法体中的相似对象,抹去了不同类型对象之间的差异性,使得方法体中的相似结构能够被抽取出来。

    我将这一重构手法命名为Parameterize Method by Polymorphism。让我仿照Martin Fowler的风格,给出这一重构方式的作法(Mechanics):
    1)新建一个接口,并使原有方法中的差异对象实现该接口。
    2)如果原有对象的方法与该接口定义的方法签名不同,则运用Rename Method。
    3)编译。
    4)新建一个参数为新接口类型的方法,使它可以替换先前所有的重复方法。
    5)编译。
    6)将对旧方法的调用替换为对新函数的调用。
    7)编译,测试。
    8)对所有旧方法重复上述步骤,每次替换后,修改并测试

    如果在调用新方法时,发现创建参数实参对象是一件麻烦事,可以考虑在原有类中引入一个创建新接口对象的工厂方法,从而对复杂的创建逻辑进行封装。

  • 相关阅读:
    Linux负载均衡--LVS(IPVS)主要算法实现分析
    使用alarm控制阻塞connect()超时的示例
    使用select控制非阻塞connect()超时的示例
    再出发
    nulls_hlist原理 和 tcp连接查找
    linux支持大容量硬盘
    Nmap扫描原理(下)
    linux常用命令
    Linux下面自动清理超过指定大小的文件
    Memcached介绍
  • 原文地址:https://www.cnblogs.com/wayfarer/p/1895038.html
Copyright © 2020-2023  润新知