(7)從護士到c#開發者--面向對象編程基礎

(7)從護士到c#開發者--面向對象編程基礎

作為一名從護理行業轉行的程式設計師,我將分享如何通過醫護工作經驗來理解面向對象編程的概念。本文將居間類、對象、屬性、方法等面向對象的核心概念,並結合醫療保健領域的實例來加深理解。

最后更新 2025/3/18 下午9:15
勇敢的天使
预计阅读 17 分钟
分类
分享 課程 .NET
专题
從護士到c#開發者
标签
.NET C# 轉行開發 編程 面向對象

引言

作為一名從護理行業轉行到編程領域的新手,我發現面向對象編程(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}");
}

通過面向對象的方法,我們:

  1. 創建了表示不同角色和設備的對象(患者、護士、血糖儀)
  2. 每個對象負責自己的功能(血糖儀負責測量,護士負責操作流程)
  3. 無論是哪位護士,都使用相同的方法,減少了代碼重複
  4. 將責任分配給了合適的對象,使代碼更加模塊化和易於維護

這就像現實中的醫療工作一樣:不同的護士使用相同的醫療設備和標準流程為患者服務,而不是每位護士都發明自己的流程。

理解類與對象

在醫院工作時,我們有"護理記錄單"這個模板,根據它我們可以為每個病人創建具體的記錄。在編程中:

  • :就像護理記錄單模板,定義了對象應有的特徵(屬性)和功能(方法)
  • 對象:就像根據模板填寫的具體病人記錄

類的概念

類就像是一個圖紙或者模子,就像我之前做護士時使用的護理評估表格。這個表格定義了需要記錄的內容(屬性)和操作流程(方法)。

在 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);

構造函數的特點:

  1. 沒有返回值,連 void 都不能寫
  2. 名稱必須與類名相同
  3. 可以有多個重載版本
  4. 類中默認有一個無參構造函數,但如果自定義了構造函數,默認的無參構造函數就會消失

new 關鍵字的作用

当我们使用new创建对象时,它做了三件事:

  1. 在內存中開闢空間
  2. 在這個空間中創建對象
  3. 調用對象的構造函數進行初始化

就像醫院為新病人分配床位、準備病歷,然後錄入基本信息。

靜態與非靜態

在醫院中,有些信息是每個病人都不同的(如姓名、年齡),有些信息則是所有病人共享的(如醫院名稱、科室醫生)。

靜態成員與實例成員

  • 實例成員:每個對象獨有的屬性或方法,就像每個病人都有自己的體溫、血壓等數據
  • 靜態成員:屬於類本身而非對象的屬性或方法,所有對象共享,就像醫院的名稱、治療規範等
/// <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 的用途:

  1. 區分局部變量和成員變量
  2. 在構造函數中調用其他構造函數
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}");
    }

}

總結

作為一名從護士轉行到編程的新手,我發現面向對象編程的概念與醫療實踐有許多相似之處:

  • 就像是病歷表格模板
  • 對象就像是具體病人的病歷記錄
  • 屬性就像是病人的各項指標,有些需要數據驗證
  • 方法就像是對病人進行的各種醫療操作
  • 構造函數就像是病人入院登記
  • 靜態成員就像是醫院的共享資源和規範

通過這些類比,我更容易理解和記憶面向對象編程的概念。在下一篇文章中,我將繼續深入探討面向對象編程的進階概念,如繼承、多態和接口。

期待與大家分享更多從醫護到編程的學習心得!

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2025/8/13

推薦一款高性能狀態機管理解決方案

在實際軟體開發中,尤其是工業軟體,每一款設備都有複雜的狀態以及狀態之間的切換的功能需求,在這種情況下,如何管理狀態以及狀態之間切換,和對應狀態下的功能控制,成為非常重要的一個問題。

继续阅读
同分类 / 同标签 2025/2/25

net 10 preview 1發布

今天.net 10 preview 1發布了,我第一時間下載,升級了avalonia ui項目和博客網站,前者功能測試及aot發布正常,後者調試正常,docker暫時未成功

继续阅读