博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
重构代码(应如写诗)
阅读量:6864 次
发布时间:2019-06-26

本文共 10922 字,大约阅读时间需要 36 分钟。

背景

最近公司做了个项目,深深体会到架构设计以及代码优化有多么的重要。 回头看自己的代码都觉得特别混乱,有时候还要看很久才能看懂,可扩展性特别差,完全是为了完成需求而编码的。说得形象一点就像修水管,最后全部都漏水了。 个人觉得代码重构非常有必要,写程序不但要给机器运行,更让人看的明白。 写代码如写诗一样才行。

实例

  • 一个图书馆出租书的程序。
  • 计算每一个读者的消费金额并且打印详情清单。
  • 打印信息:
  • 读者租了哪些书、租期多长、根据租借时间和书的类型算出费用。
  • 书分类:普通读本、少儿读本、新书
  • 计算费用,以及计算积分。积分根据书的种类是否为新书而又有所不同。
常见代码

按照实例需求,经常都是类似这样子写代码的,如下: Book书本类 主要是关于书名称和分类信息。

/** * 书本 */public class Book {    public static final int CHILDRENS = 2;    public static final int REGULAR =  0;    public static final int NEW_RELEASE = 1;    private String title;    private int priceCode;    public Book() {    }    public Book(String title, int priceCode) {        this.title = title;        this.priceCode = priceCode;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public int getPriceCode() {        return priceCode;    }    public void setPriceCode(int priceCode) {        this.priceCode = priceCode;    }}复制代码

Rental 租借信息 主要是写租借信息,包括书和租借天数的关系。

/** * 租借信息 */public class Rental {     private Book book;     private int daysRented;//租借天数    public Rental() {    }    public Rental(Book book, int daysRented) {        this.book = book;        this.daysRented = daysRented;    }    public Book getBook() {        return book;    }    public void setBook(Book book) {        this.book = book;    }    public int getDaysRented() {        return daysRented;    }    public void setDaysRented(int daysRented) {        this.daysRented = daysRented;    }}复制代码

Customer 读者类 主要写租借费用计算以及租借的书的关系。

/** * 读者 */public class Customer {    private String name;    private List
rentals = new ArrayList(); public Customer() { } public Customer(String name) { this.name = name; } //添加租书信息 public void addRental(Rental rental) { rentals.add(rental); } //生成订单 public String generateOrder() { double total = 0;//计算租借总数量 int frequentRenterPoints = 0;//计算积分 String result = "Rental Record for "+getName()+"\n"; for (Rental rental : rentals) { double thisAmount = 0; switch (rental.getBook().getPriceCode()){ case Book.REGULAR: thisAmount += 2; if (rental.getDaysRented() > 2){ thisAmount += (rental.getDaysRented() - 2) *1.5; } break; case Book.NEW_RELEASE: thisAmount += rental.getDaysRented()*3; break; case Book.CHILDRENS: thisAmount += 1.5; if (rental.getDaysRented() > 3){ thisAmount += (rental.getDaysRented() - 3) *1.5; } break; } frequentRenterPoints++; if ((rental.getBook().getPriceCode() == Book.NEW_RELEASE) && rental.getDaysRented() >1){ frequentRenterPoints++; } if ((rental.getBook().getPriceCode() == Book.NEW_RELEASE) && rental.getDaysRented() >1) { frequentRenterPoints++; } result += "\t"+rental.getBook().getTitle() + "\t"+String.valueOf(thisAmount)+"\n"; total +=thisAmount; } result += "Amount owed is "+ String.valueOf(total) +"\n"; result += "You earned "+ String.valueOf(frequentRenterPoints) +"frequent renter points"; return result; } public String getName() { return name; } public void setName(String name) { this.name = name; }}复制代码

测试类:

/** * 一个图书馆出租书的程序。 * 计算每一个读者的消费金额并且打印详情清单。 * 打印信息: * 读者租了哪些书、租期多长、根据租借时间和书的类型算出费用。 * 书分类:普通读本、少儿读本、新书 * 计算费用,以及计算积分。积分根据书的种类是否为新书而又有所不同。 * */public class Test {    public static void main(String[] args) {        Customer customer = new Customer();        Book book = new Book("Java入门到放弃", Book.NEW_RELEASE);        Book book1 = new Book("python入门到放弃", Book.CHILDRENS);        Book book2 = new Book("golang入门到放弃", Book.REGULAR);        customer.addRental(new Rental(book,8));        customer.addRental(new Rental(book1,4));        customer.addRental(new Rental(book2,6));        customer.setName("zero");        System.out.println(customer.generateOrder());    }}复制代码
第一次重构

**首先:**分析一下上面的代码

    1. 结构:

  • 2.假如某天产品跑过来(弄死她吧),需要你增加书的分类规则或者计费规则的时候,上面的代码你怎么做呢。估计又写个差不多方法,然而你会发现其实逻辑跟上面的代码非常相似的。还有更好的办法不?

**接着:**直接看下面的代码重构呗 Book类: 将按照书的不同类型,按照不同价格统计的方法移动到Book类中,因为这个按理应该属于Book类中的。

public class Book {    public static final int CHILDRENS = 2;    public static final int REGULAR = 0;    public static final int NEW_RELEASE = 1;    private String title;    private int priceCode;    public Book() {    }    public Book(String title, int priceCode) {        this.title = title;        this.priceCode = priceCode;    }    //1.提取统计钱的方法    public double getCharge(int daysRented) {        double result = 0;        switch (getPriceCode()) {            case Book.REGULAR:                result += 2;                if (daysRented > 2) {                    result += (daysRented - 2) * 1.5;                }                break;            case Book.NEW_RELEASE:                result += daysRented * 3;                break;            case Book.CHILDRENS:                result += 1.5;                if (daysRented > 3) {                    result += (daysRented - 3) * 1.5;                }                break;        }        return result;    }    //1.提取计算会员积分的方法    public int getFrequentRenterPoints(int daysRented) {        if ((getPriceCode() == Book.NEW_RELEASE) && daysRented > 1) {            return 2;        }        return 1;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public int getPriceCode() {        return priceCode;    }    public void setPriceCode(int priceCode) {        this.priceCode = priceCode;    }}复制代码

Rental 类: 主要是调用提取统计钱和积分的方法。

public class Rental {    private Book book;    private int daysRented;    public Rental() {    }    public Rental(Book book, int daysRented) {        this.book = book;        this.daysRented = daysRented;    }    //1.提取统计钱的方法    public double getCharge() {        return book.getCharge(daysRented);    }    //1.提取计算会员积分的方法    public int getFrequentRenterPoints() {        return book.getFrequentRenterPoints(daysRented);    }    public Book getBook() {        return book;    }    public void setBook(Book book) {        this.book = book;    }    public int getDaysRented() {        return daysRented;    }    public void setDaysRented(int daysRented) {        this.daysRented = daysRented;    }}复制代码

Customer 读者类 主要是去掉多余的临时变量total,frequentRenterPoints等。

public class Customer {    private String name;    private List
rentals = new ArrayList(); public Customer() { } public Customer(String name) { this.name = name; } //添加租书信息 public void addRental(Rental rental) { rentals.add(rental); } //生成订单 public String generateOrder() { String result = "Rental Record for " + getName() + "\n"; for (Rental rental : rentals) { result += "\t" + rental.getBook().getTitle() + "\t" + String.valueOf(rental.getCharge()) + "\n"; } result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(getFrequentRenterPoints()) + "frequent renter points"; return result; } //获取购买总数 private double getTotalCharge() { double result = 0; for (Rental rental : rentals) { result += rental.getCharge(); } return result; } //统计积分 private double getFrequentRenterPoints() { double result = 0; for (Rental rental : rentals) { result += rental.getFrequentRenterPoints(); } return result; } public String getName() { return name; } public void setName(String name) { this.name = name; }}复制代码

最后 测试结果跟上面的一样,就是将代码的结果调动一下。 现在大致的UML类图如下:

第二次重构

经过第一次重构,还是没有实现需求修改增加多个分类的效果。那么接下来使用接口抽象来再次重构。

Price接口 接口抽象两个规约方法,具体如下

public abstract class Price {    abstract int getPriceCode();    //1.提取统计总价的方法    abstract double getCharge(int daysRented);    //1.提取计算会员积分的方法    public int getFrequentRenterPoints(int daysRented) {        return 1;    }}复制代码

RegularPrice 普通的书价格类

public class RegularPrice extends Price {    @Override    int getPriceCode() {        return Book.REGULAR;    }    @Override    public double getCharge(int daysRented) {        double result = 2;        if (daysRented >2) {            result += (daysRented - 2) * 1.5;        }        return result;    }}复制代码

ChildrensPrice 少儿读物类价格

public class ChildrensPrice extends Price {    @Override    int getPriceCode() {        return Book.CHILDRENS;    }    @Override    public double getCharge(int daysRented) {        double result = 1.5;        if (daysRented >3) {            result += (daysRented - 3) * 1.5;        }        return  result;    }}复制代码

NewReleasePrice 新书型类价格

public class NewReleasePrice extends Price {    @Override    int getPriceCode() {        return Book.NEW_RELEASE;    }    @Override    public double getCharge(int daysRented) {        return daysRented * 3;    }    @Override    public int getFrequentRenterPoints(int daysRented) {        return (daysRented > 1)?2:1;    }}复制代码

Book类 将priceCode换成Price。

public class Book {    public static final int CHILDRENS = 2;    public static final int REGULAR = 0;    public static final int NEW_RELEASE = 1;    private String title;    private Price _price;    public Book() {    }    public Book(String title, int priceCode) {        this.title = title;        setPriceCode(priceCode);    }    //1.提取统计数量的方法    public double getCharge(int daysRented) {        return _price.getCharge(daysRented);    }    //1.提取计算会员积分的方法    public int getFrequentRenterPoints(int daysRented) {        if ((getPriceCode() == Book.NEW_RELEASE) && daysRented > 1) {            return 2;        }        return 1;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public int getPriceCode() {        return _price.getPriceCode();    }    public void setPriceCode(int arg) {        switch (arg){            case REGULAR:                _price = new RegularPrice();                break;            case CHILDRENS:                _price = new ChildrensPrice();                break;            case NEW_RELEASE:                _price = new NewReleasePrice();                break;                default:                    throw new IllegalArgumentException("Incorrect Price code");        }    }}复制代码

最终类图如下:

总结

大致的工作如下:

  • 抽离成方法。
  • 移动方法到所属的类。
  • 用多态性替换条件。
  • 自我封装。
  • 用策略替换类型代码。

最后想说: 如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达成目的,那么就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。 写代码就应该像写诗一样,而不是没BUG,我就不动它。

源码:

参考文章

【】作者: 

最后

如果对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。

转载于:https://juejin.im/post/5ca714aef265da30972872da

你可能感兴趣的文章
一线互联网常见的14个Java面试题,你颤抖了吗
查看>>
学习webpack4 - 样式处理
查看>>
nextjs踩坑
查看>>
TiKV 源码解析系列文章(二)raft-rs proposal 示例情景分析
查看>>
153. Find Minimum in Rotated Sorted Array
查看>>
让移动开发更轻松 闲鱼基于Flutter构建跨端APP应用实践
查看>>
Python爬虫 --- 2.3 Scrapy 框架的简单使用
查看>>
【每日一包0023】concat-stream
查看>>
Metricbeat 参考指南(步骤2:配置Metricbeat)
查看>>
doodoo.js快速入门教程
查看>>
XXL-EXCEL v1.1.1 发布,Java对象和Excel转换工具
查看>>
elasticsearch 自动删除索引脚本
查看>>
NLP入门(五)用深度学习实现命名实体识别(NER)
查看>>
js 排序算法之快速排序
查看>>
边缘计算ENS:拓展云的边界
查看>>
从0开发豆果美食小程序——搜索组件
查看>>
cordova 远程h5页面调用本地js
查看>>
巨杉数据库多活架构实践
查看>>
使用机器学习预测电子竞技游戏《守望先锋》的胜负
查看>>
即将到来的java11的新特性
查看>>