我是靠谱客的博主 留胡子红酒,最近开发中收集的这篇文章主要介绍重构代码(应如写诗),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

背景

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

实例

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

按照实例需求,经常都是类似这样子写代码的,如下: 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<Rental> 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<Rental> 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,我就不动它。

源码: github.com/xbmchina/re…

参考文章

【重构】作者: Martin Fowler

最后

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

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

最后

以上就是留胡子红酒为你收集整理的重构代码(应如写诗)的全部内容,希望文章能够帮你解决重构代码(应如写诗)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部