」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 重構 ReadmeGenie

重構 ReadmeGenie

發佈於2024-12-23
瀏覽:844

Refactoring ReadmeGenie

介绍

本周我的任务是重构 ReadmeGenie。如果您刚刚来到这里,ReadmeGenie 是我的开源项目,它使用 AI 根据用户输入的文件生成自述文件。

最初,我的想法是,“该程序运行良好。从第一天起我就一直以有组织的方式开发它......那么为什么要改变它呢?”

好吧,在从该项目中休息一周后,我再次打开它并立即想到,“这是什么?”

为什么要重构?

为了给您一些背景信息,这里有一个例子:我曾经认为是完美的核心功能之一,结果证明比必要的要复杂得多。在重构过程中,我将其分解为五个独立的函数——你猜怎么着?现在代码更加清晰且更易于管理。

看一下这个函数的原始版本:

def generate_readme(file_paths, api_key, base_url, output_filename, token_usage):
    try:
        load_dotenv()

        # Check if the api_key was provided either as an environment variable or as an argument
        if not api_key and not get_env():
            logger.error(f"{Fore.RED}API key is required but not provided. Exiting.{Style.RESET_ALL}")
            sys.exit(1)

        # Concatenate content from multiple files
        file_content = ""
        try:
            for file_path in file_paths:
                with open(file_path, 'r') as file:
                    file_content  = file.read()   "\\n\\n"
        except FileNotFoundError as fnf_error:
            logger.error(f"{Fore.RED}File not found: {file_path}{Style.RESET_ALL}")
            sys.exit(1)

        # Get the base_url from arguments, environment, or use the default
        chosenModel = selectModel(base_url)
        try:
            if chosenModel == 'cohere':
                base_url = os.getenv("COHERE_BASE_URL", "https://api.cohere.ai/v1")
                response = cohereAPI(api_key, file_content)
                readme_content = response.generations[0].text.strip()   FOOTER_STRING
            else:
                base_url = os.getenv("GROQ_BASE_URL", "https://api.groq.com")
                response = groqAPI(api_key, base_url, file_content)
                readme_content = response.choices[0].message.content.strip()   FOOTER_STRING
        except AuthenticationError as auth_error:
            logger.error(f"{Fore.RED}Authentication failed: Invalid API key. Please check your API key and try again.{Style.RESET_ALL}")
            sys.exit(1)
        except Exception as api_error:
            logger.error(f"{Fore.RED}API request failed: {api_error}{Style.RESET_ALL}")
            sys.exit(1)

        # Process and save the generated README content
        if readme_content[0] != '*':
            readme_content = "\n".join(readme_content.split('\n')[1:])

        try:
            with open(output_filename, 'w') as output_file:
                output_file.write(readme_content)
            logger.info(f"README.md file generated and saved as {output_filename}")
            logger.warning(f"This is your file's content:\n{readme_content}")
        except IOError as io_error:
            logger.error(f"{Fore.RED}Failed to write to output file: {output_filename}. Error: {io_error}{Style.RESET_ALL}")
            sys.exit(1)

        # Save API key if needed
        if not get_env() and api_key is not None:
            logger.warning("Would you like to save your API key and base URL in a .env file for future use? [y/n]")
            answer = input()
            if answer.lower() == 'y':
                create_env(api_key, base_url, chosenModel)
        elif get_env():
            if chosenModel == 'cohere' and api_key != os.getenv("COHERE_API_KEY"):
                if api_key is not None:
                    logger.warning("Would you like to save this API Key? [y/n]")
                    answer = input()
                    if answer.lower() == 'y':
                        create_env(api_key, base_url, chosenModel)
            elif chosenModel == 'groq' and api_key != os.getenv("GROQ_API_KEY"):
                if api_key is not None:
                    logger.warning("Would you like to save this API Key? [y/n]")
                    answer = input()
                    if answer.lower() == 'y':
                        create_env(api_key, base_url, chosenModel)

        # Report token usage if the flag is set
        if token_usage:
            try:
                usage = response.usage
                logger.info(f"Token Usage Information: Prompt tokens: {usage.prompt_tokens}, Completion tokens: {usage.completion_tokens}, Total tokens: {usage.total_tokens}")
            except AttributeError:
                logger.warning(f"{Fore.YELLOW}Token usage information is not available for this response.{Style.RESET_ALL}")
        logger.info(f"{Fore.GREEN}File created successfully")
        sys.exit(0)

1。消除全局变量
全局变量可能会导致意想不到的副作用。将状态保持在其所属范围内,并在必要时显式传递值。

2.使用函数进行计算
尽可能避免将中间值存储在变量中。相反,在需要时使用函数执行计算 - 这使您的代码灵活且更易于调试。

3.不同的职责
单个函数应该做一件事,并且做得很好。将命令行参数解析、文件读取、AI 模型管理和输出生成等任务拆分为单独的函数或类。这种分离允许将来更轻松地进行测试和修改。

4。改进命名
有意义的变量和函数名称至关重要。一段时间后重新访问代码时,清晰的名称可以帮助您理解流程,而无需重新学习所有内容。

5。减少重复
如果您发现自己在复制和粘贴代码,则表明您可以从共享函数或类中受益。重复使得维护变得更加困难,小的变化很容易导致错误。

提交并推送到 GitHub

1。创建分支
我首先使用以下命令创建一个分支:

git checkout -b 

此命令创建一个新分支并切换到它。

2.进行一系列提交
一旦进入新分支,我就进行了增量提交。每次提交都代表一个逻辑工作块,无论是重构功能、修复错误还是添加新功能。频繁进行小额提交有助于更有效地跟踪更改,并更轻松地回顾项目的历史记录。

git status
git add 
git commit -m "Refactored function"

3.变基以保持干净的历史记录
在进行了几次提交之后,我重新调整了我的分支,以保持历史记录的干净和线性。变基允许我在将提交推送到 GitHub 之前对其进行重新排序、组合或修改。如果某些提交非常小或者我想避免太多增量更改使提交历史变得混乱,这尤其有用。

git rebase -i main

在这一步中,我在主分支之上启动了交互式变基。 -i 标志允许我以交互方式修改提交历史记录。我可以将一些较小的提交压缩为一个较大的、有凝聚力的提交。例如,如果我有一系列提交,例如:

重构第 1 部分
重构第 2 部分
修复重构中的错误

我可以将它们压缩成一个带有更清晰消息的提交

4。将更改推送到 GitHub
当我对 rebase 后的提交历史感到满意后,我将更改推送到 GitHub。如果您刚刚创建了一个新分支,则需要使用 -u 标志将其推送到远程存储库,该标志为将来的推送设置上游分支。

git push -u origin 

5。合并
在最后一步中,我快进合并到主分支并再次推送

git checkout main # change to the main branch
git merge --ff-only  # make a fast-forward merge
git push origin main # push to the main

要点

一切都有改进的空间。重构可能看起来很麻烦,但它通常会带来更干净、更易于维护和更高效的代码。因此,下次当您对重构感到犹豫时,请记住:总有更好的方法来做事。
尽管我认为现在已经很完美了,但我在下一次提交中肯定会有一些需要改进的地方。

版本聲明 本文轉載於:https://dev.to/htsagara/refactoring-readmegenie-4816?1如有侵犯,請洽[email protected]刪除
最新教學 更多>
  • 如何正確使用與PDO參數的查詢一樣?
    如何正確使用與PDO參數的查詢一樣?
    在pdo 中使用類似QUERIES在PDO中的Queries時,您可能會遇到類似疑問中描述的問題:此查詢也可能不會返回結果,即使$ var1和$ var2包含有效的搜索詞。錯誤在於不正確包含%符號。 通過將變量包含在$ params數組中的%符號中,您確保將%字符正確替換到查詢中。沒有此修改,PD...
    程式設計 發佈於2025-05-05
  • 您如何在Laravel Blade模板中定義變量?
    您如何在Laravel Blade模板中定義變量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配變量對於存儲以後使用的數據至關重要。在使用“ {{}}”分配變量的同時,它可能並不總是最優雅的解決方案。 幸運的是,Blade通過@php Directive提供了更優雅的方法: $ old_section =...
    程式設計 發佈於2025-05-05
  • 在細胞編輯後,如何維護自定義的JTable細胞渲染?
    在細胞編輯後,如何維護自定義的JTable細胞渲染?
    在JTable中維護jtable單元格渲染後,在JTable中,在JTable中實現自定義單元格渲染和編輯功能可以增強用戶體驗。但是,至關重要的是要確保即使在編輯操作後也保留所需的格式。 在設置用於格式化“價格”列的“價格”列,用戶遇到的數字格式丟失的“價格”列的“價格”之後,問題在設置自定義單元...
    程式設計 發佈於2025-05-05
  • 為什麼我會收到MySQL錯誤#1089:錯誤的前綴密鑰?
    為什麼我會收到MySQL錯誤#1089:錯誤的前綴密鑰?
    mySQL錯誤#1089:錯誤的前綴鍵錯誤descript [#1089-不正確的前綴鍵在嘗試在表中創建一個prefix鍵時會出現。前綴鍵旨在索引字符串列的特定前綴長度長度,以便更快地搜索這些前綴。 理解prefix keys `這將在整個Movie_ID列上創建標準主鍵。主密鑰對於唯一識...
    程式設計 發佈於2025-05-05
  • 在Java中使用for-to-loop和迭代器進行收集遍歷之間是否存在性能差異?
    在Java中使用for-to-loop和迭代器進行收集遍歷之間是否存在性能差異?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    程式設計 發佈於2025-05-05
  • MySQL中如何高效地根據兩個條件INSERT或UPDATE行?
    MySQL中如何高效地根據兩個條件INSERT或UPDATE行?
    在兩個條件下插入或更新或更新 solution:的答案在於mysql的插入中...在重複鍵更新語法上。如果不存在匹配行或更新現有行,則此功能強大的功能可以通過插入新行來進行有效的數據操作。如果違反了唯一的密鑰約束。 實現所需的行為,該表必須具有唯一的鍵定義(在這種情況下為'名稱'...
    程式設計 發佈於2025-05-05
  • \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    答案: 在大多數現代編譯器中,while(1)和(1)和(;;)之間沒有性能差異。編譯器: perl: 1 輸入 - > 2 2 NextState(Main 2 -E:1)V-> 3 9 Leaveloop VK/2-> A 3 toterloop(next-> 8 last-> 9 ...
    程式設計 發佈於2025-05-05
  • 表單刷新後如何防止重複提交?
    表單刷新後如何防止重複提交?
    在Web開發中預防重複提交 在表格提交後刷新頁面時,遇到重複提交的問題是常見的。要解決這個問題,請考慮以下方法: 想像一下具有這樣的代碼段,看起來像這樣的代碼段:)){ //數據庫操作... 迴聲“操作完成”; 死(); } ? > ...
    程式設計 發佈於2025-05-05
  • 如何從2D數組中提取元素?使用另一數組的索引
    如何從2D數組中提取元素?使用另一數組的索引
    Using NumPy Array as Indices for the 2nd Dimension of Another ArrayTo extract specific elements from a 2D array based on indices provided by a second ...
    程式設計 發佈於2025-05-05
  • 如何使用Depimal.parse()中的指數表示法中的數字?
    如何使用Depimal.parse()中的指數表示法中的數字?
    在嘗試使用Decimal.parse(“ 1.2345e-02”中的指數符號表示法表示的字符串時,您可能會遇到錯誤。這是因為默認解析方法無法識別指數符號。 成功解析這樣的字符串,您需要明確指定它代表浮點數。您可以使用numbersTyles.Float樣式進行此操作,如下所示:[&& && && ...
    程式設計 發佈於2025-05-05
  • Java開發者如何保護數據庫憑證免受反編譯?
    Java開發者如何保護數據庫憑證免受反編譯?
    在java 在單獨的配置文件保護數據庫憑證的最有效方法中存儲憑據是將它們存儲在單獨的配置文件中。該文件可以在運行時加載,從而使登錄數據從編譯的二進製文件中遠離。 使用prevereness class import java.util.prefs.preferences; 公共類示例{ 首選...
    程式設計 發佈於2025-05-05
  • 如何使用替換指令在GO MOD中解析模塊路徑差異?
    如何使用替換指令在GO MOD中解析模塊路徑差異?
    在使用GO MOD時,在GO MOD 中克服模塊路徑差異時,可能會遇到衝突,其中可能會遇到一個衝突,其中3派對軟件包將另一個帶有導入套件的path package the Imptioned package the Imptioned package the Imported tocted pac...
    程式設計 發佈於2025-05-05
  • 如何同步迭代並從PHP中的兩個等級陣列打印值?
    如何同步迭代並從PHP中的兩個等級陣列打印值?
    同步的迭代和打印值來自相同大小的兩個數組使用兩個數組相等大小的selectbox時,一個包含country代碼的數組,另一個包含鄉村代碼,另一個包含其相應名稱的數組,可能會因不當提供了exply for for for the uncore for the forsion for for ytry...
    程式設計 發佈於2025-05-05
  • 如何使用Python有效地以相反順序讀取大型文件?
    如何使用Python有效地以相反順序讀取大型文件?
    在python 反向行讀取器生成器 == ord('\ n'): 緩衝區=緩衝區[:-1] 剩餘_size- = buf_size lines = buffer.split('\ n'....
    程式設計 發佈於2025-05-05
  • 圖片在Chrome中為何仍有邊框? `border: none;`無效解決方案
    圖片在Chrome中為何仍有邊框? `border: none;`無效解決方案
    在chrome 中刪除一個頻繁的問題時,在與Chrome and IE9中的圖像一起工作時,遇到了一個頻繁的問題。和“邊境:無;”在CSS中。要解決此問題,請考慮以下方法: Chrome具有忽略“ border:none; none;”的已知錯誤,風格。要解決此問題,請使用以下CSS ID塊創建帶...
    程式設計 發佈於2025-05-05

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3