111 一般警察特考三等 警察資訊管理人員 物件導向程式設計 考古題詳解
第 一 題15 分
說明:試題回答程式碼可以選用 C++、C#、Java 或 Python,但副檔名須依使用的程式語言來命名。
下列類別圖及 C# 程式碼是一個影像識別軟體 ImageDetector.exe 的類別設計與部分程式碼,請依以下二個程式執行的結果來撰寫各小題的程式碼。(每小題 5 分,共 15 分)
影像識別軟體的類別圖: 🖼️【此處有附圖,請對照原卷】
影像識別軟體的部分程式碼: Program.cs
class Program
{
void Main(string[] args)
{
IFoo foo = null;
if (args.Length > 0)
{
if (args[0] == "1")
foo = new FooFeature();
else if (args[0] == "2")
foo = new FooFeatureFast();
string text = foo.Run();
Console.WriteLine(text);
}
else
{
Console.WriteLine("The arguments is null value.");
}
}
}
影像識別軟體程式執行結果(1):
C:\> ImageDetector.exe 1
Hi FooFeature Class!
影像識別軟體程式執行結果(2):
C:\> ImageDetector.exe 2
Hi FooFeatureFast Class!
(一) 請撰寫 IFoo.cs 程式碼。 (二) 請撰寫 FooFeatureFast.cs 程式碼。 (三) 請撰寫 FooFeature.cs 程式碼。
登入後即可作答並保存紀錄。
核心觀念
本題考查:
- C#
interface(介面)的宣告。 - 類別以
: IFoo實作介面。 - 介面方法必須由實作類別提供完整內容。
- 多型:
IFoo foo可參考FooFeature或FooFeatureFast物件,實際執行各自的Run方法。
類別圖表示:
IFoo
└── Run: string
FooFeature implements IFoo
FooFeatureFast implements IFoo
因此,兩個類別都必須實作:
public string Run
解題方法
程式依命令列參數建立物件:
if (args[0] == "1")
foo = new FooFeature;
else if (args[0] == "2")
foo = new FooFeatureFast;
接著透過介面參考呼叫:
string text = foo.Run;
要產生題目指定的輸出:
FooFeature.Run回傳"Hi FooFeature Class!"FooFeatureFast.Run回傳"Hi FooFeatureFast Class!"
(一)IFoo.cs
public interface IFoo
{
string Run;
}
介面只宣告方法規格,不提供方法內容。Run 的回傳型別為 string,且沒有參數。
(二)FooFeatureFast.cs
第 二 題20 分
資訊封裝(encapsulation)是物件導向程式設計重要的特性之一,其主要目的在於確保資料存取的安全。下列 UML 類別圖是一個員工資料規劃,包括個人基本資料及薪資等資料。單位內的員工(Employee)分成正職人員(Staff)及約聘人員(Contractor)。正職人員的薪資包含本俸(Salary)及職務津貼(Allowance),約聘人員為單一薪資且須符合國家最低薪資 25250 元。查詢總薪資(TotalSalary)時正職人員須加總本俸及職務津貼。建立人員資料時須檢核身分證字號是否符合編碼規則,身分證字號為員工資料查詢及更新時的關鍵欄。請依據上述說明來回答各小題。(每小題 5 分,共 20 分)
🖼️【此處有附圖,請對照原卷】
(一) 類別圖中有關資訊封裝的設計有誤,請修正繪出正確的類別圖,以符合題目說明之需求。 (二) 請依修正後的類別圖撰寫 Employee 類別的程式碼,無須實作身分證檢查運算式,但須實作出函數及回傳值等程式碼。 (三) 請依修正後的類別圖撰寫 Staff 類別的程式碼,須實作運算式以符合題目說明之查詢需求。 (四) 請依修正後的類別圖撰寫 Contractor 類別的程式碼,須實作運算式以符合題目說明之資料維護需求。
登入後即可作答並保存紀錄。
核心觀念
本題考查:
- 資訊封裝(Encapsulation):資料成員不可由外部直接存取,應設為
private,透過公開方法讀取或修改。 - 繼承與多型:
Staff與Contractor繼承抽象類別Employee,並各自重新定義薪資相關行為。 - 方法覆寫(Override):
Staff::TotalSalary應計算本俸加職務津貼。Contractor::SetSalary應限制薪資不得低於新臺幣 25,250 元。
- 資料驗證:身分證字號建立或修改時,須經由
CheckIDNo檢核。
(一)修正後的 UML 類別圖
原圖的主要錯誤是 id_no、name、salary 及 duties_allowance 使用 +,代表公開成員,會破壞資訊封裝。修正後應改為 -,代表私有成員。
另外,為使子類別能重新定義薪資及總薪資功能,SetSalary 與 TotalSalary 應設計為可覆寫的 virtual 方法。
Employee <<abstract>>
------------------------------------------------
- id_no : string
- name : string
- salary : int
------------------------------------------------
+ Employee(id : string, n : string, s : int)
+ GetIDNo : string
+ SetIDNo(id : string) : bool
+ GetSalary : int
+ SetSalary(salary : int) : bool
+ TotalSalary : int
- CheckIDNo(id : string) : bool
△
│
┌────────┴────────┐
│ │
Staff Contractor
------------------ ------------------
- duties_allowance : int
------------------ ------------------
+ GetDutiesAllowance : int
+ SetDutiesAllowance(value : int) : void
+ SetSalary(salary : int) : bool
+ TotalSalary : int
+ SetSalary(salary : int) : bool
+ TotalSalary : int
在正式 UML 表示中,SetSalary 與 TotalSalary 可標示為斜體或 {abstract},表示由子類別實作。若採用 C++,可使用 virtual,並以 override 重新定義。
(二)Employee 類別程式碼
Employee 負責保存所有員工共有的資料,並將資料成員設為 private。身分證字號的檢查由私有函數 CheckIDNo 負責。
題目允許省略身分證字號的實際檢查運算式,因此以下以固定回傳值表示檢查函數的位置。
第 三 題20 分
使用第二題中的類別建立一位員工資料的串列,並以 Staff 及 Contractor 類別各建立一筆資料,須檢核身分證字號正確後,再將資料物件加入串列中;完成二筆資料後再以迴圈方式將這二筆資料中的身分證字號、姓名、總薪資列印至螢幕。本題無須撰寫第二題中的類別程式,僅須撰寫本題所須的應用程式碼即可。撰寫的程式碼輸出結果必須與以下輸出畫面相同,變數、函數及類別名稱須依第二題之類別圖中的屬性及方法名稱來宣告,串列及物件名稱須依下列指定名稱來實作程式。(20 分)
串列變數名稱:employees 正職人員物件變數名稱:staff 約聘人員物件變數名稱:contractor
程式執行輸出畫面如下:
身分證:A123456789、姓名:王建國、總薪資:80000
身分證:B123456789、姓名:李大明、總薪資:31000
登入後即可作答並保存紀錄。
題目資料說明
題目未附第二題的類別圖,因此無法確認實際的基底類別、建構子、身分證檢核方法及薪資方法名稱。以下依常見類別設計,假設:
Staff、Contractor繼承自EmployeeCheckID:檢核身分證字號Id、Name:身分證字號與姓名屬性GetTotalSalary:取得總薪資Staff與Contractor的建構子可直接設定身分證字號、姓名及薪資
正式作答時,應將下列名稱替換為第二題類別圖所指定的名稱。
核心觀念
本題主要測驗下列物件導向概念:
- 繼承與多型
Staff 與 Contractor 都是員工類別,可共同存放在 List<Employee> 中。迴圈以 Employee 型別處理物件,由各子類別實作自己的總薪資計算方式。
- 泛型串列
使用 List<Employee> 建立員工資料串列:
List<Employee> employees = new List<Employee>;
- 物件建立
分別以 Staff 與 Contractor 建立兩個物件,且變數名稱必須為題目指定的 staff 與 contractor。
- 資料驗證
必須先呼叫身分證字號檢核方法,檢核成功後才能使用 employees.Add 加入串列。
- 總薪資封裝
呼叫 GetTotalSalary 取得總薪資,不應在本題應用程式中重複撰寫 Staff 或 Contractor 的薪資計算公式。
解題方法
解題順序如下:
- 建立
Employee型別的串列employees。 - 建立正職人員物件
staff。 - 呼叫
staff.CheckID,身分證正確才加入串列。 - 建立約聘人員物件
contractor。 - 呼叫
contractor.CheckID,身分證正確才加入串列。 - 使用
foreach逐筆取出員工物件。 - 列印身分證字號、姓名及總薪資。
參考程式碼
第 四 題25 分
請依下列遞迴函數的 C# 程式碼,回答程式執行的結果為何?(25 分)
static void Main(string[] args)
{
Sum(9);
}
public static int Sum(int num)
{
if (num < 1)
{
return 0;
}
else
{
int result = num + Sum(num - 2);
Console.WriteLine(String.Format("Num: {0}", result));
return result;
}
}
登入後即可作答並保存紀錄。
核心觀念
本題考查 C# 的遞迴函數、函數呼叫堆疊,以及遞迴返回後的執行順序。
函數 Sum(num) 的規則如下:
- 當
num < 1時,符合遞迴終止條件,直接回傳 。 - 當
num \geq 1$ 時,先執行Sum(num - 2)`,取得回傳值後,再計算:
接著執行輸出:
Console.WriteLine(String.Format("Num: {0}", result));
因此,Console.WriteLine 是在遞迴呼叫完成、函數逐層返回時才執行。
解題方法
由 Main 呼叫:
Sum(9);
遞迴呼叫過程為:
當執行 Sum(-1) 時:
因此回傳 ,且不輸出任何文字。
接著開始逐層返回:
| 函數呼叫 | 計算方式 | result | 輸出內容 |
|---|---|---|---|
Sum(1) | Num: 1 | ||
Sum(3) | Num: 4 | ||
Sum(5) | Num: 9 | ||
Sum(7) | Num: 16 | ||
Sum(9) | $25 |
第 五 題20 分
物件導向程式設計的類別屬性及方法依讀取的透過度可分為 public、protected 及 private 三種,請回答下圖類別 Program 及類別 Rectangle 所能讀取 Shape 類別中的屬性及方法有那些?(20 分)
🖼️【此處有附圖,請對照原卷】
登入後即可作答並保存紀錄。
核心觀念
本題考查物件導向程式設計中的存取修飾詞(access modifiers):
public:任何其他類別均可存取。protected:- 類別本身可存取;
- 繼承該類別的子類別可存取。
private:僅限宣告該成員的類別本身存取,其他類別及子類別均不可直接存取。
圖中 Rectangle 以繼承關係繼承 Shape;Program 則只是使用 Shape,並非繼承 Shape。
Shape 類別成員
Shape 類別具有:
public int xpublic int yprotected int widthprotected int heightprivate bool isDrawLinepublic Draw : void
解題方法
判斷原則如下:
- 先確認
Program與Shape的關係。 - 再確認
Rectangle是否為Shape的子類別。 - 依
public、protected、private的存取規則逐一判斷。
Program 類別可讀取的成員
Program 不是 Shape 的子類別,因此只能直接讀取 Shape 中的 public 成員:
xyDraw
不可直接讀取:
width、height:因為屬於protected,Program並非子類別。isDrawLine:因為屬於private,只有Shape本身可存取。
Rectangle 類別可讀取的成員
Rectangle 繼承 Shape,因此可以直接讀取: