Java 入門指南
單元 18 - 重構
重構 (refactoring) 是指不改變軟體 (software) 外部行為下,對程式原始碼 (source code) 進行整理、修改,不外是希望提升原始碼的可讀性及更易於維護
簡單說,只要感覺原始碼有些不對勁就該重構,尤其在一個大的開發團隊中,每個小組負責的部份可能都不同,因而常常會發生識別字 (identifier) 命名不一致或是太多類別有共通特性的情況。所以需要適時的重構原始程式碼,這也是軟體開發過程中一項重要的工作。
我們利用數學公式設計一種演算法 (algorithm) ,利用這種演算法來建立密碼表,然而這個演算法並不是最理想的方式,因為這個公式建立的密碼表只有
5 × 10 = 50
依據 a 可能有 5 種數字, b 可能有 10 種數字,因此這個演算法最多就只能產生 50 種密碼表。
可是 26 個英文字母的排列會有
1 × 2 × 3 × ⋯⋯ × 25 × 26 = 4.0329 × 1026
由上可見有非常非常多種。
有沒有更理想的方式呢?有的,事實上我們可以直接攪亂字串 (string) 中字母的順序,這需要另一個攪亂順序的演算法,這在 Java API 中有,另外編碼 toEncode() 跟 toDecode() 兩個方法都按照 Unicode 原始數值來做計算也太不直覺了,事實上有更簡單直覺的處理方式。
以下看到重構後的 Encrypt 類別 (class) ,先是 import 的部分
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Collections;
完整的實作檔案可以參考「範例程式碼」的 Encrypt.java 。
屬性 (field) 的部分是把密碼表字元陣列 (array) 改成字母表字串與密碼表字串
// 字母表
final String letter = "abcdefghijklmnopqrstuvwxyz";
// 密碼表
String code;
使用陣列的目的是方便用 Unicode 值計算,改用字串是因為字串是更複雜的物件,有更多好用的方法。
這裡是直接將屬性 letter 設定成字母表的字串,同時用關鍵字 (keyword) 宣告為 final ,表示這是不可改變的常數值。
建構子 (constructor) 增加為兩個,無參數 (parameter) 的建構子直接建立隨機密碼表
// 無參數建構子
public Encrypt() {
this.code = "";
List<String> letterList = new ArrayList<String>(Arrays.asList(this.letter.split("")));
Collections.shuffle(letterList);
for (String s : letterList) {
this.code += s;
}
System.out.println(this.code);
}
無參數建構子中,先將 code 屬性設定為空字串
this.code = "";
然後將字母表屬性 letter 逐字元轉換成 ArrayList , ArrayList 是 Java API 中類似陣列的資料結構
List<String> letterList = new ArrayList<String>(Arrays.asList(this.letter.split("")));
注意這裡是先用 String 型態的 split() 方法將 letter 字母表字串以空字串 "" 分割,結果回傳 String 型態的陣列,這個 String 陣列是由 26 個小寫英文字母的子字串組合而成,再用 Arrays 類別的 asList() 將這個 String 陣列轉換成實作 List 介面的物件,最後用 ArrayList 型態的建構子將 List 型態物件轉變為 ArrayList 。
由於 ArrayList 實作 List 介面,因此 ArrayList 型態的變數可以用 List 宣告。
下面繼續用 Collections 類別的的 shuffle() 方法攪亂 ArrayList 型態的 letterList ,其中每個子字串(字元)的順序
Collections.shuffle(letterList);
再來用 for 迴圈 (loop) 將每一個子字串附加到 code 屬性的最後,使其成為密碼表字串
for (String s : letterList) {
this.code += s;
}
最後在命令列印出密碼表字串
System.out.println(this.code);
有參數的建構子則是利用字串參數 str 設定屬性 code
// 有參數的建構子
public Encrypt(String str) {
this.code = str;
}
之所以要多這個有參數的建構子,主要是在往後實作存檔功能的時候,可以直接儲存密碼表,然後由密碼表字串設定 Encrypt 物件。
下面繼續看到重構後的編碼方法 (method)
// 編碼方法
public String toDecode(String inputString) {
String result = "";
List<String> inputList = new ArrayList<String>(Arrays.asList(inputString.split("")));
for (String s : inputList) {
if (letter.contains(s)) {
result += this.code.substring(this.letter.indexOf(s), this.letter.indexOf(s)+1);
}
else {
result += s;
}
}
return result;
}
編碼方法中先設定區域變數 (local variable) result 用來儲存結果
String result = "";
接下來把輸入字串轉換成 ArrayList
List<String> inputList = new ArrayList<String>(Arrays.asList(inputString.split("")));
下面用 for 迴圈進行編碼
for (String s : inputList) {
if (letter.contains(s)) {
result += this.code.substring(this.letter.indexOf(s), this.letter.indexOf(s)+1);
}
else {
result += s;
}
}
以上利用變數 s 取得輸入字串中的每一個字元字串,然後用屬性 letter 字串的 contains() 判斷變數 s 是否包含在 letter 中,也就是判斷變數 s 是否為英文小寫字母,如果是英文小寫字母,就從屬性 code 擷取在字母表 letter 相同索引值位置的字元字串,將這個字元字串附加到 result 之後,相對不是英文小寫字母,就直接將該字元字串附加到 result 之後。
最後,回傳結果 result
return result;
解碼方法跟編碼方法很相似,除了把 code 換成 letter , letter 換成 code
// 解碼方法
public String toDecode(String inputString) {
String result = "";
List<String> inputList = new ArrayList>String>(Arrays.asList(inputString.split(""s)));
for (String s : inputList) {
if (letter.contains(s)) {
result += this.letter.substring(this.code.indexOf(s), this.code.indexOf(s)+1);
}
else {
result += s;
}
}
return result;
}
執行部份把註解化的程式碼取消註解,執行結果如下
rkuodvxlmyasicgehtnqjpbzwf |
There is no spoon. |
Tldtd mn cg neggc. |
There is no spoon. |
這樣 Encrypt 專案的程式碼是不是更容易理解了呢?接下來我們就要進入 GUI 的部份了,在此之前先來認識一下 Java API 與 JavaFX 吧!
相關教學影片
中英文術語對照 | |
---|---|
algorithm | 演算法 |
array | 陣列 |
class | 類別 |
constructor | 建構子 |
field | 屬性 |
identifier | 識別字 |
keyword | 關鍵字 |
local variable | 區域變數 |
loop | 迴圈 |
method | 方法 |
parameter | 參數 |
refactoring | 重構 |
software | 軟體 |
source code | 原始碼 |
string | 字串 |
重點整理 |
---|
1. 重構是指重新整理程式碼,讓程式更易於維護。 |
2. 常見的重構技術包括整理屬性、方法,挑出類別的共通特性定義父類別等等。 |
問題與討論 |
---|
1. 什麼是重構?為什麼要替開發好的程式進行重構? |
2. Encrppt 類別進行了哪些重構?為什麼要作這些重構? |
練習 |
---|
1. 承接上一個單元的猜數字遊戲,將新專案寫在 Exercise1801 中,修正第一位數可能為 0 的情況。 |
1. 承上題,將新專案寫在 Exercise1802 中,增加一個屬性 digit ,然後定義兩個建構子,第一個建構子把 digit 設定為 4 ,第二個建構子利用參數設定 digit ,然後繼續建立其他屬性,然後把程式中猜測數量 4 的程式碼都改成 digit 。 |