C++ 實習測試: 靜態 vs 動態的物件功能設定 |
時間: 165分鐘 (106/06/22 09:20 ~ 12:05 上傳時間截止) 你可以看自己手上的資料, 可以查網路上的說明或是範例, 但是請不要和同學討論 |
第一部份目標:撰寫一個概念上的字串編碼與加密解密工具程式這個程式基本的功能是將一個 std::string 物件裡面的資料編碼與加密、解密,不過不需要你撰寫加密與解密的演算法,只需要把加密動作的概念表達出來就可以了,例如下面的程式輸出,第一列是最原始放在 std::string 裡面的明文;第二列我們用 AES(...) 代表經過 AES 加密演算法加密得到的密文;第三列是把第二列的 AES 密文用十六進位編碼的結果;第四列是把第三列的十六進位編碼還原回來並且以 AES 解密把還原回來的密文解密出來的結果 Hello, this is the plaintext. 這個程式的加密與解密只是概念上的動作,所以程式不至於太複雜, 底下先給你主要的程式碼 考試要求一請你在 VC2010 裡開啟一個新的方案 StringEncoding1, 讓 Visual Studio 幫你建立方案的目錄, 然後請把下面的程式碼分別拷貝到不同的檔案裡, 測試執行, 應該可以得到前三列(黑字)的結果 #include <string> #include <iostream> using namespace std; class AESEnc { public: AESEnc(string data); string encrypt(); // assume with fixed key private: string m_data; }; AESEnc::AESEnc(string data):m_data(data) { } string AESEnc::encrypt() { return string("AES(") + m_data + ")"; } class HexAESEnc: public AESEnc { public: HexAESEnc(string data); string encrypt(); }; HexAESEnc::HexAESEnc(string data):AESEnc(data) { } string HexAESEnc::encrypt() { static const char* digits = "0123456789ABCDEF"; string in = AESEnc::encrypt(); string out; for (int i=0; i<in.length(); i++) { out.push_back(digits[(in[i]>>4) & 0x0f]); out.push_back(digits[in[i] & 0x0f]); } return out; } int main() { string line1("Hello, this is the plaintext."); cout << line1 << endl; AESEnc aesEncData(line1); string line2 = aesEncData.encrypt(); cout << line2 << endl; HexAESEnc hexAesEncData(line1); string line3 = hexAesEncData.encrypt(); cout << line3 << endl; /* HexAESDec hexAESDecData(line3); string line4 = hexAESDecData.decrypt(); cout << line4 << endl; */ return 0; } 考試要求二請參考上面的 AESEnc 與 HexAESEnc 類別以及其繼承架構,撰寫兩個類別 AESDec 與 HexAESDec,其中 HexAESDec 類別繼承 AESDec 類別,使得上面 main() 裡面註解掉的 3 列程式可以順利執行並且印出紅字的部份 AESDec 是一個負責 AES 解密的類別,建構元的參數是一個字串,字串裡面是 AES 演算法加密過的密文,建構元裡面把參數的字串拷貝到一個資料成員中,請撰寫一個 string decrypt() 成員函式來解密, 如果資料字串的內容是 AES(XYZ) 的話,就把 AES() 刪除掉傳回 XYZ,如果資料字串的內容只是 XYZ,就傳回 IAES(XYZ) 代表解密出來的字串。 HexAESDec 需要負責十六進位的解碼以及 AES 密文的解密,請繼承 AESDec 類別得到解密的功能,再加上十六進位解碼的功能。請先撰寫一個私有的成員函式 string decode(string &) 把輸入的十六進位編碼字串還原回來;再撰寫一個建構元函式,參數是一個字串,字串裡面是經過十六進位編碼後的 AES 密文,建構元在初始化串列裡用 decode 成員函式的輸出來初始化父類別 AESDec 物件,然後撰寫一個 string decrypt() 成員函式,運用 AESDec::decrypt() 來得到解密出來的明文字串。 目前這個程式的類別圖如下: AESEnc 和 AESDec 類別負責基本的加解密,HexAESEnc 和 HexAESDec 類別則是附加了十六進位的編碼的加密和解密,這樣子的設計是靜態地設定物件的額外功能,所謂靜態的意思就是需要在程式裡設計類別 HexAESEnc 或是 HexAESDec,然後重新編譯過,如果今天我們有另外一種加解密方法 DESEnc 和 DESDec,那麼也許就需要 AESDESEnc 這種類別來表示先用 DES 加密再用 AES 加密,可能也需要 HexAESDESEnc 或是 HexDESEnc 或是 HexDESAESEnc 等等的組合,當符合的選項越來越多的時候,很難靜態地把所有可能需要的組合類別都先設計出來,所以就會有動態地設定物件功能的需求。 上面這個設計還有一個地方看起來有一些問題,就是原始資料保存在 AESEnc 和 AESDec 類別的 m_data 資料成員裡,如果有其他的加解密方法的類別例如 DESEnc 和 DESDec,它們需要保存原始資料嗎? 如果兩個類別都有原始資料,那麼合成起來的類別要把原始資料放在哪一個類別裡呢?如下圖,我們可以修改一下,另外設計專門存放原始資料的類別,這樣子就可以任意調整加密和編碼的順序,也可以任意調整兩種加密的順序: |
第二部份目標:運用 decorator 樣板,撰寫一個能夠動態地設定物件功能的字串編碼與加密解密工具程式想要能夠動態地組合基本物件提供的功能,就不能用上面繼承的方法來合併不同的功能,需要運用委託的機制來達成這個需求,例如下圖所示: 或是 因為在這種資料處理的過程中,一定是一個階段一個階段分別完成的,不會混在一起先加密一部份資料、另外一部份先做 Hex 編碼,所以兩張圖裡面的委託機制都形成一個串列。 物件 :AESEnc 連接到物件 :PlainData,代表的意義是加密的資料由 :PlainData 物件提供,物件 :HexEnc 連接到物件 :AESEnc 代表的意義是編碼的資料來自 AES 加密過後的資料;因為連接的目標物件有兩種,所以如下圖我們定義一個共通的界面 IData,:PlainData 只會是最終端的物件,其它加密、編碼、解密、解碼的功能物件都在串列的中間,我們把它抽象化為 Filter 界面,如上圖這樣子串起來的架構可以用下圖右側的聚合關係來表示 上圖裡面 Filter::ctor(IData&) 主要是在建立物件之間的關聯性,參數是提供資料的物件,需要把這個關聯性記錄在 m_source 資料成員裡面 各種 Filter 的衍生類別一起畫出來可以得到下圖 這樣子的類別設計,在運用的時候基本上會用物件的建構元把物件之間的關聯性建立好,然後在主要的 pumpData() 界面裡,沿著串列將資料一層一層的處理好資料,所有的關聯性都是客戶端程式動態組合出來的,不需要另外定義類別,主要的使用方法如同下面這個 main 函式所顯示 int main() { PlainData data("Hello, this is the plaintext."); cout << data.pumpData() << endl; AESEnc aesEncData(data); cout << aesEncData.pumpData() << endl; DESEnc *pDesEncData = new DESEnc(aesEncData); cout << pDesEncData->pumpData() << endl; AESDec aesDecData(aesEncData); cout << aesDecData.pumpData() << endl; DESDec desDecData(*pDesEncData); cout << desDecData.pumpData() << endl; HexEnc hexEncData(aesEncData); cout << hexEncData.pumpData() << endl; HexDec hexDecData(hexEncData); cout << hexDecData.pumpData() << endl; delete pDesEncData; return 0; }執行的結果如下 Hello, this is the plaintext. AES(Hello, this is the plaintext.) DES(AES(Hello, this is the plaintext.)) Hello, this is the plaintext. AES(Hello, this is the plaintext.) 4145532848656C6C6F2C20746869732069732074686520706C61696E746578742E29 AES(Hello, this is the plaintext.) |
考試要求三請在前面的方案中加入一個新的專案 StringEncoding2, 並且完成下面的類別來支援前面這個 main() 函式裡的測試 |
請完成 IData 抽象類別的設計
|
請完成 PlainData 類別的設計
|
請完成 Filter 抽象類別的設計
|
請完成 AESEnc、AESDec、HexEnc、HexDec 類別的設計
|
請以前面的 main() 函式測試 |
考試時間: 165分鐘 (09:20 ~ 12:05 上傳時間截止)將所完成的 project (只需保留 .cpp, .h, .sln 以及 .vcxproj 檔案即可; 刪除掉 .suo, .sdf, .filters, .users, debug\ 資料匣, 以及 ipch\ 資料匣下的所有內容) 以 zip/rar/7zip 程式將整個資料匣壓縮起來, 在「考試作業」繳交區選擇 Labtest4 上傳 |
後續:
|
回
C++ 物件導向程式設計課程 首頁 製作日期: 06/21/2017 by 丁培毅 (Pei-yih Ting) E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615 海洋大學 電機資訊學院 資訊工程學系 Lagoon |