# age_calculator.py import datetime from collections import namedtuple from loguru import logger # type: ignore[reportMissingImports] # 從 converter.py 這個檔案,匯入 Converter 這個 class from converter import Converter # --- 類型定義 --- AgeResult = namedtuple("AgeResult", ["real_age", "east_asian_age"]) # ====================================================================== # 主要類別:AgeCalculator # ====================================================================== class AgeCalculator: """ 提供精準的年齡計算功能 (實歲與虛歲)。 初始化時需要傳入一個 Converter 物件。 """ def __init__(self, converter: Converter): """ 初始化年齡計算機。 這個設計模式稱為「依賴注入 (Dependency Injection)」, AgeCalculator 本身不建立 Converter,而是由外部提供,讓架構更靈活。 :param converter: 一個 Converter 的實體物件。 """ if not isinstance(converter, Converter): raise TypeError("初始化 AgeCalculator 時,必須提供一個有效的 Converter 物件") self.converter = converter def calculate( self, solar_birth_date: datetime.date, lunar_birth_info: dict, today: datetime.date ) -> AgeResult: """ 根據生日資訊,使用農曆轉換器精準計算實歲和虛歲。 """ # 虛歲計算 (邏輯不變) east_asian_age = today.year - solar_birth_date.year + 1 # 實歲計算 (使用 self.converter) try: # 用 converter 找出「今年的農曆生日」對應的西曆日期 this_year_lunar_bday_solar_str = self.converter.lunar_to_solar( lunar_year=today.year, lunar_month=lunar_birth_info['month'], lunar_day=lunar_birth_info['day'], leap=lunar_birth_info['leap'] ) this_year_lunar_bday_date = datetime.datetime.strptime(this_year_lunar_bday_solar_str, '%Y-%m-%d').date() except KeyError: # Edge case: 若今年沒有該農曆日期(如閏月),則認定生日已過,讓邏輯更強健 this_year_lunar_bday_date = today - datetime.timedelta(days=1) real_age = today.year - solar_birth_date.year # 核心判斷:如果今天還沒到「今年的農曆生日(的西曆日期)」,那實歲就 -1 if today < this_year_lunar_bday_date: real_age -= 1 return AgeResult(real_age=real_age, east_asian_age=east_asian_age) # ====================================================================== # 主程式區塊:作為使用範例 # ====================================================================== if __name__ == "__main__": # --- 作為範例的資料 --- TODAY_OVERRIDE = datetime.date(2025, 7, 30) SOLAR_BIRTH_DATE_STR = "1977-10-12" LUNAR_BIRTH_INFO = {"month": 8, "day": 30, "leap": False} try: # --- 步驟 1: 建立依賴物件 (工具人) --- converter = Converter() # --- 步驟 2: 建立主物件,並注入依賴 (主角登場,並把工具人交給他) --- age_calculator = AgeCalculator(converter=converter) # --- 步驟 3: 執行計算 (叫主角開始幹活) --- solar_birth_date = datetime.datetime.strptime(SOLAR_BIRTH_DATE_STR, "%Y-%m-%d").date() age_result = age_calculator.calculate( solar_birth_date=solar_birth_date, lunar_birth_info=LUNAR_BIRTH_INFO, today=TODAY_OVERRIDE, ) # --- 步驟 4: 使用 loguru 輸出結果 --- logger.remove() logger.add(lambda msg: print(msg, end=""), format="{message}") logger.info(f"實歲 (足歲):{age_result.real_age} 歲。") logger.info(f"虛歲:{age_result.east_asian_age} 歲。") except Exception as e: logger.error(f"執行時發生錯誤: {e}")