最近公司作了個項目,深深體會到架構設計以及代碼優化有多麼的重要。 回頭看本身的代碼都以爲特別混亂,有時候還要看好久才能看懂,可擴展性特別差,徹底是爲了完成需求而編碼的。說得形象一點就像修水管,最後所有都漏水了。 我的以爲代碼重構很是有必要,寫程序不但要給機器運行,更讓人看的明白。 寫代碼如寫詩同樣才行。python
按照實例需求,常常都是相似這樣子寫代碼的,以下: Book書本類 主要是關於書名稱和分類信息。git
/**
* 書本
*/
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 租借信息 主要是寫租借信息,包括書和租借天數的關係。github
/**
* 租借信息
*/
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 讀者類 主要寫租借費用計算以及租借的書的關係。golang
/**
* 讀者
*/
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;
}
}
複製代碼
測試類:bash
/**
* 一個圖書館出租書的程序。
* 計算每個讀者的消費金額而且打印詳情清單。
* 打印信息:
* 讀者租了哪些書、租期多長、根據租借時間和書的類型算出費用。
* 書分類:普通讀本、少兒讀本、新書
* 計算費用,以及計算積分。積分根據書的種類是否爲新書而又有所不一樣。
*
*/
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());
}
}
複製代碼
**首先:**分析一下上面的代碼架構
**接着:**直接看下面的代碼重構唄 Book類: 將按照書的不一樣類型,按照不一樣價格統計的方法移動到Book類中,由於這個按理應該屬於Book類中的。ide
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,我就不動它。
【重構】做者: Martin Fowler
若是對 Java、大數據感興趣請長按二維碼關注一波,我會努力帶給大家價值。以爲對你哪怕有一丁點幫助的請幫忙點個贊或者轉發哦。