引言
作為一名從護理行業轉行到編程領域的新手,我發現面向對象編程(oop)概念實際上與醫療實踐有許多相似之處。在這篇文章中,我將分享我如何通過醫護工作經驗來理解面向對象編程的基本概念,希望能幫助其他正在學習編程的醫護人員。
面向過程 vs 面向對象
面向過程思維
面向過程編程關注的是完成事情的過程和步驟,強調的是動作和流程。這就像我們在醫院執行的標準操作流程(sop)。
舉個例子,測量病人血糖的面向過程思維:
// 面向过程思维:不同护士执行相同的血糖测量流程
void NurseLiMeasureBloodSugar() // 李护士(经验少)
{
PrepareEquipment(); // 准备设备
CallForHelp(); // 寻求帮助
ExplainToPatient(); // 向患者解释
DisinfectFinger(); // 消毒手指
PrickFinger(); // 采血
ApplyBloodToStrip(); // 血液滴到试纸
ReadResult(); // 读取结果
RecordData(); // 记录数据
DisposeMaterials(); // 处理医疗废物
}
void NurseZhangMeasureBloodSugar() // 张护士(经验丰富)
{
PrepareEquipment(); // 准备设备
ExplainToPatient(); // 向患者解释
DisinfectFinger(); // 消毒手指
PrickFingerExpertly(); // 熟练采血
ApplyBloodToStrip(); // 血液滴到试纸
ReadResultAccurately(); // 精确读取结果
RecordDataInDetail(); // 详细记录数据
DisposeMaterials(); // 处理医疗废物
}
可以看出,面向過程的方法需要我們為不同的執行者編寫不同的代碼,每位護士都需要自己的一套完整流程,導致代碼重複且難以維護。
面向對象思維
面向對象編程則是找個對象幫你做事。針對同樣的血糖測量任務,我們把注意力從"誰怎麼做"轉移到"用什麼設備做",將操作封裝到相應的對象中。
/// <summary>
/// 血糖仪类 - 负责测量和记录血糖值的医疗设备
/// </summary>
public class GlucoseMeter
{
// 属性 - 表示血糖仪的状态
/// <summary>
/// 表示血糖仪是否已校准
/// </summary>
public bool IsCalibrated { get; private set; }
/// <summary>
/// 表示血糖仪当前电量
/// </summary>
public double BatteryLevel { get; private set; }
// 方法 - 表示血糖仪可执行的操作
/// <summary>
/// 校准血糖仪,确保测量准确
/// </summary>
public void Calibrate()
{
IsCalibrated = true;
Console.WriteLine("血糖仪已校准");
}
/// <summary>
/// 执行血糖测量
/// </summary>
/// <returns>测量的血糖值,若设备未校准则返回-1</returns>
public double MeasureBloodSugar()
{
if(!IsCalibrated)
{
Console.WriteLine("错误:血糖仪未校准");
return -1; // 错误值
}
Console.WriteLine("测量血糖中...");
// 执行测量逻辑
return 5.6; // 返回血糖值(示例)
}
}
/// <summary>
/// 护士类 - 代表执行医疗操作的护理人员
/// </summary>
public class Nurse
{
/// <summary>
/// 护士姓名
/// </summary>
public string Name { get; private set; }
/// <summary>
/// 工作经验年限
/// </summary>
public int ExperienceYears { get; private set; }
/// <summary>
/// 创建一个新的护士对象
/// </summary>
/// <param name="name">护士姓名</param>
/// <param name="experienceYears">工作经验年限</param>
public Nurse(string name, int experienceYears)
{
Name = name;
ExperienceYears = experienceYears;
}
/// <summary>
/// 为患者测量血糖的综合操作
/// </summary>
/// <param name="patient">需要测量的患者</param>
/// <param name="meter">使用的血糖仪</param>
public void MeasurePatientBloodSugar(Patient patient, GlucoseMeter meter)
{
Console.WriteLine($"{Name}护士准备为{patient.Name}测量血糖");
// 准备设备
Console.WriteLine("准备设备");
// 校准设备(由血糖仪完成)
meter.Calibrate();
// 向患者解释
Console.WriteLine($"向{patient.Name}解释测量过程");
// 消毒和采血(护士的专业操作)
Console.WriteLine("消毒手指并采血");
// 测量血糖(由血糖仪完成)
double bloodSugar = meter.MeasureBloodSugar();
// 记录结果
if(bloodSugar > 0)
{
patient.RecordBloodSugar(bloodSugar);
Console.WriteLine($"血糖值: {bloodSugar}mmol/L,已记录到患者档案");
}
// 处理医疗废物
Console.WriteLine("处理医疗废物");
}
}
/// <summary>
/// 患者类 - 代表医院中的病人
/// </summary>
public class Patient
{
// 字段 - 私有数据,不直接对外暴露
/// <summary>
/// 患者姓名
/// </summary>
private string _name;
/// <summary>
/// 患者年龄
/// </summary>
private int _age;
/// <summary>
/// 患者的历史血糖记录
/// </summary>
private List<double> _bloodSugarReadings;
/// <summary>
/// 创建一个新的患者对象
/// </summary>
/// <param name="name">患者姓名</param>
/// <param name="age">患者年龄</param>
public Patient(string name, int age)
{
_name = name;
_age = age;
_bloodSugarReadings = new List<double>(); // 初始化空的血糖记录列表
}
// 属性 - 对外提供的访问器
/// <summary>
/// 获取患者姓名
/// </summary>
public string Name
{
get { return _name; }
private set { _name = value; } // 只允许在类内部修改
}
/// <summary>
/// 获取或设置患者年龄(包含验证)
/// </summary>
public int Age
{
get { return _age; }
private set { _age = value > 0 ? value : 0; } // 确保年龄为正数
}
// 方法 - 对象可执行的操作
/// <summary>
/// 记录新的血糖测量值
/// </summary>
/// <param name="value">血糖值</param>
public void RecordBloodSugar(double value)
{
if(value > 0)
{
_bloodSugarReadings.Add(value);
}
}
/// <summary>
/// 获取最近一次血糖记录
/// </summary>
/// <returns>最近的血糖值,如无记录则返回-1</returns>
public double GetLatestBloodSugar()
{
if(_bloodSugarReadings.Count > 0)
return _bloodSugarReadings[_bloodSugarReadings.Count - 1];
else
return -1; // 表示没有记录
}
/// <summary>
/// 获取患者的所有血糖记录历史
/// </summary>
/// <returns>血糖值列表的副本</returns>
public List<double> GetBloodSugarHistory()
{
return new List<double>(_bloodSugarReadings); // 返回副本以保护原始数据
}
}
// 使用示例 - 演示对象之间的协作
/// <summary>
/// 血糖测量的综合示例
/// </summary>
void BloodSugarMeasurementExample()
{
// 创建各种对象
Patient patient = new Patient("王小明", 65);
GlucoseMeter meter = new GlucoseMeter();
// 不同经验的护士使用相同的方法和设备
Nurse nurseLi = new Nurse("李护士", 1); // 经验少
Nurse nurseZhang = new Nurse("张护士", 10); // 经验丰富
// 两位护士执行相同的操作,调用相同的方法
nurseLi.MeasurePatientBloodSugar(patient, meter);
nurseZhang.MeasurePatientBloodSugar(patient, meter);
// 检查患者的血糖历史
List<double> history = patient.GetBloodSugarHistory();
Console.WriteLine($"患者{patient.Name}的血糖记录次数: {history.Count}");
}
通過面向對象的方法,我們:
- 創建了表示不同角色和設備的對象(患者、護士、血糖儀)
- 每個對象負責自己的功能(血糖儀負責測量,護士負責操作流程)
- 無論是哪位護士,都使用相同的方法,減少了代碼重複
- 將責任分配給了合適的對象,使代碼更加模塊化和易於維護
這就像現實中的醫療工作一樣:不同的護士使用相同的醫療設備和標準流程為患者服務,而不是每位護士都發明自己的流程。
理解類與對象
在醫院工作時,我們有"護理記錄單"這個模板,根據它我們可以為每個病人創建具體的記錄。在編程中:
- 類:就像護理記錄單模板,定義了對象應有的特徵(屬性)和功能(方法)
- 對象:就像根據模板填寫的具體病人記錄
類的概念
類就像是一個圖紙或者模子,就像我之前做護士時使用的護理評估表格。這個表格定義了需要記錄的內容(屬性)和操作流程(方法)。
在 c#中,類的基本語法是:
[public] class 类名
{
// 字段
private string _name;
// 属性
public string Name
{
get { return _name; }
set { _name = value; }
}
// 方法
public void DoSomething()
{
// 方法实现
}
}
類和對象的關係
如果把類比作病歷模板,那麼對象就是根據這個模板創建的具體病人記錄。類本身不占用內存(除靜態成員外),而對象是要占用內存的。
醫護實例:
// Patient类(病人类)
public class Patient
{
// 字段
private string _name;
private int _age;
private string _diagnosis;
// 属性
public string Name
{
get { return _name; }
set { _name = value; }
}
public int Age
{
get { return _age; }
set { _age = value > 0 ? value : 0; } // 验证年龄必须为正数
}
// 方法
public void TakeMedicine()
{
Console.WriteLine($"{Name}正在服药...");
}
}
// 创建Patient对象
Patient patient1 = new Patient();
patient1.Name = "张三";
patient1.Age = 45;
patient1.TakeMedicine(); // 输出:张三正在服药...
理解屬性和欄位
欄位與屬性的區別
在護理工作中,我們有很多數據需要記錄,但有些數據需要經過驗證或特殊處理。
- 欄位:相當於病歷上的原始數據,通常應該被保護,不允許外部直接修改
- 屬性:相當於對這些數據的管控機制,可以添加驗證邏輯
public class Patient
{
// 字段(私有)
private double _temperature;
// 属性(公开)
public double Temperature
{
get { return _temperature; }
set
{
// 添加验证逻辑
if(value >= 35 && value <= 42)
_temperature = value;
else
throw new ArgumentException("体温数值不合理!");
}
}
}
属性的本质是两个方法:get()和set()。属性可以是:
- 可讀可寫屬性:有 get 和 set
- 只讀屬性:只有 get
- 只寫屬性:只有 set
訪問修飾符
在醫院信息系統中,不同級別的人員能夠訪問的信息是不同的。這與 c#中的訪問修飾符概念類似:
public:公开的,就像病人的基本信息,所有医护人员都可以查看private:私有的,只能在当前类内部访问,就像某些敏感检查结果,只有主治医生可以查看protected:受保护的,可以被派生类访问,就像某些治疗方案,只有医疗团队成员可以查看
對象的初始化
當新病人入院時,我們需要填寫一系列表格來記錄其基本信息。在編程中,我們通過構造函數來完成對象的初始化工作。
構造函數
構造函數是一個特殊的方法,它在對象創建時自動執行,用於初始化對象。
public class Patient
{
// 字段
private string _name;
private int _age;
// 构造函数
public Patient(string name, int age)
{
_name = name;
_age = age;
Console.WriteLine($"创建了一个新病人: {name}, {age}岁");
}
// 属性
public string Name
{
get { return _name; }
set { _name = value; }
}
public int Age
{
get { return _age; }
set { _age = value; }
}
}
// 使用构造函数创建对象
Patient newPatient = new Patient("李四", 50);
構造函數的特點:
- 沒有返回值,連 void 都不能寫
- 名稱必須與類名相同
- 可以有多個重載版本
- 類中默認有一個無參構造函數,但如果自定義了構造函數,默認的無參構造函數就會消失
new 關鍵字的作用
当我们使用new创建对象时,它做了三件事:
- 在內存中開闢空間
- 在這個空間中創建對象
- 調用對象的構造函數進行初始化
就像醫院為新病人分配床位、準備病歷,然後錄入基本信息。
靜態與非靜態
在醫院中,有些信息是每個病人都不同的(如姓名、年齡),有些信息則是所有病人共享的(如醫院名稱、科室醫生)。
靜態成員與實例成員
- 實例成員:每個對象獨有的屬性或方法,就像每個病人都有自己的體溫、血壓等數據
- 靜態成員:屬於類本身而非對象的屬性或方法,所有對象共享,就像醫院的名稱、治療規範等
/// <summary>
/// 医院类(静态) - 包含所有科室共享的医院级信息
/// </summary>
public static class Hospital
{
// 静态属性 - 所有科室共享
/// <summary>
/// 医院名称 - 所有科室共享
/// </summary>
public static string Name { get; } = "和平医院";
/// <summary>
/// 总床位数 - 由所有科室共用
/// </summary>
public static int TotalBeds { get; private set; } = 500;
/// <summary>
/// 医院地址 - 所有科室共享
/// </summary>
public static string Address { get; } = "和平路123号";
/// <summary>
/// 急诊电话 - 所有科室共享
/// </summary>
public static string EmergencyNumber { get; } = "120";
// 静态方法 - 医院级别的操作
/// <summary>
/// 患者入院流程 - 分配到特定科室
/// </summary>
/// <param name="patient">入院患者</param>
/// <param name="department">目标科室</param>
public static void AdmitPatient(Patient patient, Department department)
{
if(TotalBeds > 0)
{
department.AssignBed(patient);
TotalBeds--;
Console.WriteLine($"{patient.Name}已入院到{department.Name}科");
}
else
{
Console.WriteLine("医院床位已满,无法入院");
}
}
/// <summary>
/// 患者出院流程 - 从特定科室出院
/// </summary>
/// <param name="patient">出院患者</param>
/// <param name="department">来源科室</param>
public static void DischargePatient(Patient patient, Department department)
{
department.ReleaseBed(patient);
TotalBeds++;
Console.WriteLine($"{patient.Name}已从{department.Name}科出院");
}
}
/// <summary>
/// 科室类 - 表示医院中的具体科室,每个科室有自己的特性
/// </summary>
public class Department
{
// 字段 - 科室特有数据
/// <summary>
/// 科室名称
/// </summary>
private string _name;
/// <summary>
/// 科室总床位数
/// </summary>
private int _totalBeds;
/// <summary>
/// 科室可用床位数
/// </summary>
private int _availableBeds;
/// <summary>
/// 科室医护人员ID列表
/// </summary>
private List<string> _staffIds;
/// <summary>
/// 科室当前病人列表
/// </summary>
private List<Patient> _patients;
/// <summary>
/// 创建一个新的科室
/// </summary>
/// <param name="name">科室名称</param>
/// <param name="totalBeds">总床位数</param>
public Department(string name, int totalBeds)
{
_name = name;
_totalBeds = totalBeds;
_availableBeds = totalBeds;
_staffIds = new List<string>();
_patients = new List<Patient>();
}
// 属性 - 科室对外暴露的信息
/// <summary>
/// 获取科室名称
/// </summary>
public string Name { get { return _name; } }
/// <summary>
/// 获取科室可用床位数
/// </summary>
public int AvailableBeds { get { return _availableBeds; } }
// 方法 - 科室可执行的操作
/// <summary>
/// 为患者分配床位
/// </summary>
/// <param name="patient">需要床位的患者</param>
public void AssignBed(Patient patient)
{
if(_availableBeds > 0)
{
_patients.Add(patient);
_availableBeds--;
Console.WriteLine($"{patient.Name}已分配到{_name}科床位");
}
else
{
Console.WriteLine($"{_name}科床位已满");
}
}
/// <summary>
/// 患者出院释放床位
/// </summary>
/// <param name="patient">出院患者</param>
public void ReleaseBed(Patient patient)
{
if(_patients.Contains(patient))
{
_patients.Remove(patient);
_availableBeds++;
Console.WriteLine($"{patient.Name}已释放{_name}科床位");
}
}
/// <summary>
/// 添加医护人员到本科室
/// </summary>
/// <param name="staffId">医护人员ID</param>
public void AddStaff(string staffId)
{
_staffIds.Add(staffId);
}
/// <summary>
/// 显示科室当前状态信息
/// </summary>
public void DisplayDepartmentStatus()
{
// 这里同时访问静态成员(医院名)和实例成员(科室信息)
Console.WriteLine($"{Hospital.Name} - {_name}科");
Console.WriteLine($"总床位:{_totalBeds},可用床位:{_availableBeds}");
Console.WriteLine($"当前患者数:{_patients.Count}");
}
}
this 關鍵字
this关键字表示当前类的实例,在医护工作中,可以理解为"当前正在处理的这个病人"。
this 的用途:
- 區分局部變量和成員變量
- 在構造函數中調用其他構造函數
public class Patient
{
private string name;
private int age;
// 使用this区分同名变量
public Patient(string name, int age)
{
this.name = name; // this.name是字段,name是参数
this.age = age;
}
// 使用this调用其他构造函数
public Patient(string name) : this(name, 0)
{
// 这个构造函数会先调用上面的构造函数
}
}
用藥記錄示例
下面是一個用藥記錄類的完整示例,演示了面向對象編程在醫療場景中的應用:
/// <summary>
/// 用药记录类 - 记录患者用药的详细信息
/// </summary>
public class MedicationRecord
{
// 字段 - 记录的内部数据
/// <summary>
/// 药品名称
/// </summary>
private string _medicationName;
/// <summary>
/// 药品剂量(数值部分)
/// </summary>
private double _dosage;
/// <summary>
/// 剂量单位(如mg, ml等)
/// </summary>
private string _unit;
/// <summary>
/// 给药时间
/// </summary>
private DateTime _administrationTime;
/// <summary>
/// 执行给药的护士ID
/// </summary>
private string _nurseId;
/// <summary>
/// 接受给药的患者ID
/// </summary>
private string _patientId;
/// <summary>
/// 给药途径(口服、静脉注射等)
/// </summary>
private string _route;
/// <summary>
/// 创建新的用药记录
/// </summary>
/// <param name="patientId">患者ID</param>
/// <param name="medicationName">药品名称</param>
/// <param name="dosage">药品剂量</param>
/// <param name="unit">剂量单位</param>
/// <param name="route">给药途径</param>
/// <param name="nurseId">执行护士ID</param>
public MedicationRecord(string patientId, string medicationName,
double dosage, string unit, string route, string nurseId)
{
_patientId = patientId;
_medicationName = medicationName;
_dosage = dosage;
_unit = unit;
_route = route;
_nurseId = nurseId;
_administrationTime = DateTime.Now; // 记录当前时间为给药时间
}
// 属性 - 对外部提供的数据访问接口
/// <summary>
/// 获取药品名称(只读)
/// </summary>
public string MedicationName { get { return _medicationName; } }
/// <summary>
/// 获取剂量(只读)
/// </summary>
public double Dosage { get { return _dosage; } }
/// <summary>
/// 获取剂量单位(只读)
/// </summary>
public string Unit { get { return _unit; } }
/// <summary>
/// 获取给药时间(只读)
/// </summary>
public DateTime AdministrationTime { get { return _administrationTime; } }
// 方法 - 记录相关的操作
/// <summary>
/// 获取完整的剂量信息
/// </summary>
/// <returns>格式化的剂量和给药途径信息</returns>
public string GetFullDosageInfo()
{
return $"{_dosage} {_unit} {_medicationName} 通过{_route}给药";
}
/// <summary>
/// 获取完整的给药记录
/// </summary>
/// <returns>格式化的完整给药记录</returns>
public string GetAdministrationRecord()
{
return $"患者ID: {_patientId}, 药品: {_medicationName}, " +
$"剂量: {_dosage}{_unit}, 途径: {_route}, " +
$"时间: {_administrationTime.ToString("yyyy-MM-dd HH:mm:ss")}, " +
$"执行护士: {_nurseId}";
}
/// <summary>
/// 检查是否在指定时间阈值内给药
/// </summary>
/// <param name="hoursThreshold">小时阈值</param>
/// <returns>如果在阈值内返回true,否则返回false</returns>
public bool IsAdministeredRecently(int hoursThreshold)
{
return (DateTime.Now - _administrationTime).TotalHours < hoursThreshold;
}
}
// 使用示例
public class MedicationExample
{
public static void Main()
{
// 创建用药记录
MedicationRecord record = new MedicationRecord(
"P001", "阿司匹林", 100, "mg", "口服", "N007");
// 获取完整记录
string fullRecord = record.GetAdministrationRecord();
Console.WriteLine(fullRecord);
// 检查是否最近给药(6小时内)
bool isRecent = record.IsAdministeredRecently(6);
Console.WriteLine($"是否6小时内给药: {isRecent}");
}
}
總結
作為一名從護士轉行到編程的新手,我發現面向對象編程的概念與醫療實踐有許多相似之處:
- 類就像是病歷表格模板
- 對象就像是具體病人的病歷記錄
- 屬性就像是病人的各項指標,有些需要數據驗證
- 方法就像是對病人進行的各種醫療操作
- 構造函數就像是病人入院登記
- 靜態成員就像是醫院的共享資源和規範
通過這些類比,我更容易理解和記憶面向對象編程的概念。在下一篇文章中,我將繼續深入探討面向對象編程的進階概念,如繼承、多態和接口。
期待與大家分享更多從醫護到編程的學習心得!