最近公司作了個項目,深深體會到架構設計以及代碼優化有多麼的重要。
回頭看本身的代碼都以爲特別混亂,有時候還要看好久才能看懂,可擴展性特別差,徹底是爲了完成需求而編碼的。說得形象一點就像修水管,最後所有都漏水了。
我的以爲代碼重構很是有必要,寫程序不但要給機器運行,更讓人看的明白。
寫代碼如寫詩同樣才行。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; } }
測試類:架構
/** * 一個圖書館出租書的程序。 * 計算每個讀者的消費金額而且打印詳情清單。 * 打印信息: * 讀者租了哪些書、租期多長、根據租借時間和書的類型算出費用。 * 書分類:普通讀本、少兒讀本、新書 * 計算費用,以及計算積分。積分根據書的種類是否爲新書而又有所不一樣。 * */ 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()); } }
首先:分析一下上面的代碼ide
接着:直接看下面的代碼重構唄
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類圖以下:this
通過第一次重構,仍是沒有實現需求修改增長多個分類的效果。那麼接下來使用接口抽象來再次重構。
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,我就不動它。
源碼:
https://github.com/xbmchina/r...
【重構】做者: Martin Fowler
若是對 Java、大數據感興趣請長按二維碼關注一波,我會努力帶給大家價值。以爲對你哪怕有一丁點幫助的請幫忙點個贊或者轉發哦。