軟體開發



綜合程式開發連結 list

  • Git
  • 微軟技術學習中心/Microsoft Learn
  • vscode/Vidual Studio Code
  • Meta/Facebook開發人員
  • Chrome開發人員

    程式語言排名

    根據最新的程式語言排名,以下是 2024 年度排名前 20 的程式語言:



    Visual Studio 的語法格式化

    1. 自動格式化代碼

    Visual Studio 提供快捷鍵來快速格式化代碼,適用於各種編程語言: - 使用快捷鍵 `Ctrl + K, Ctrl + D` 格式化整個檔案。 - 使用快捷鍵 `Ctrl + K, Ctrl + F` 格式化選中的代碼區塊。

    2. 啟用自動格式化

    Visual Studio 支援在檔案儲存時自動格式化代碼: 1. 點擊 **工具 > 選項**。 2. 前往 **文字編輯器 > [語言] > 編碼樣式 > 常規**。 3. 勾選 **在檔案儲存時重新格式化代碼**。

    3. 修改格式化設定

    1. 打開 **工具 > 選項**。 2. 前往 **文字編輯器 > [語言] > 編碼樣式 > 格式化**。 3. 在此可以調整縮排、括號樣式、空白行等詳細格式化設定。

    4. 使用編碼樣式設定檔

    Visual Studio 支援編碼樣式設定檔(.editorconfig)來統一團隊的代碼風格: 1. 在專案的根目錄新增一個 `.editorconfig` 檔案。 2. 定義格式規則,例如:
       [*.cs]
       indent_style = space
       indent_size = 4
       
    3. 檔案儲存後,格式化會自動遵循這些規則。

    5. 安裝擴展工具

    如果內建格式化功能無法滿足需求,可以安裝擴展工具: - 在 **擴展管理員** 中搜尋並安裝工具,例如 **CodeMaid** 或 **ReSharper**。 - 這些工具提供更多代碼格式化與重構選項。

    6. 快速修正格式問題

    當格式問題出現時,可以使用快速修正功能: - 在問題代碼上點擊右鍵,選擇 **快速動作與重構**。 - 選擇 **格式化文件** 或 **格式化選區**。

    7. 總結

    - 使用快捷鍵快速格式化代碼。 - 啟用檔案儲存時自動格式化功能。 - 使用 `.editorconfig` 統一代碼風格。 - 安裝擴展工具提升格式化能力。 - 善用快速修正功能改善代碼可讀性。

    Git

  • GitHub

    GitHub

    GitHub 是一個基於雲端的版本控制和協作平台,主要用於軟體開發。它使用 Git 進行版本控制,讓開發者能夠管理專案的源代碼、跟蹤變更以及與他人協作。

    主要功能

    使用 GitHub 的好處

    適用對象

    GitHub 適合各類開發者,無論是單獨開發者、開源社區還是企業團隊。它能夠滿足小型專案到大型軟體專案的版本控制和協作需求。


    GitHub 使用 git pull --rebase

    git pull --rebase 是從遠端分支拉取最新變更,並將本地的更改重新應用到最新的遠端提交之上,而不是使用傳統的合併。

    使用方式

    git pull --rebase

    工作流程

    1. 從遠端分支獲取最新更改。
    2. 暫時移除本地的提交。
    3. 將遠端提交應用到本地分支。
    4. 重新應用本地的提交,保持歷史記錄線性。

    為什麼使用 --rebase

    使用範例

    假設你在本地提交了一些更改,而遠端也有新的提交,使用 git pull --rebase 會:

    解決衝突

    如果在重放本地提交時發生衝突,Git 會要求手動解決衝突:

    1. 解決衝突並使用 git add 暫存已解決的文件。
    2. 使用 git rebase --continue 繼續重放提交。
    3. 若想取消操作,使用 git rebase --abort 回到原狀。

    總結

    git pull --rebase 是保持 Git 提交歷史整潔的重要工具,特別適合多人協作時,能夠避免產生冗餘的合併提交,並保持代碼庫的提交記錄線性。



    Shell

    Bash - $?

    在 Bash 中,$? 代表上一個執行的命令的退出狀態碼(Exit Status)。這是一個整數值,通常用來判斷上一個命令是否成功執行。

    退出狀態碼的意義

    範例 1:檢查成功執行的命令

    #!/bin/bash
    ls
    echo "上一個命令的退出狀態碼是: $?"
        

    在這個範例中,ls 命令會列出目錄內容,執行成功後,$? 的值將是 0。

    範例 2:檢查失敗的命令

    #!/bin/bash
    ls /nonexistent-directory
    echo "上一個命令的退出狀態碼是: $?"
        

    在這個範例中,ls 嘗試列出不存在的目錄,命令會失敗,$? 的值將是一個非 0 數字。

    範例 3:使用退出狀態碼進行條件判斷

    #!/bin/bash
    cp file1.txt /some/directory/
    if [ $? -eq 0 ]; then
        echo "文件複製成功。"
    else
        echo "文件複製失敗。"
    fi
        

    此範例會根據命令的退出狀態來決定要顯示哪一條訊息。



    Bash 檢查變數是否為空

    在 Bash 中,可以使用條件判斷來檢查變數是否為空,以下提供幾種常見的方式:

    範例 1:使用 -z 檢查變數是否為空

    #!/bin/bash
    
    var=""
    if [ -z "$var" ]; then
        echo "變數是空的"
    else
        echo "變數不是空的"
    fi
        

    -z 用來檢查變數是否為空,如果變數為空,條件成立。

    範例 2:使用 -n 檢查變數是否不為空

    #!/bin/bash
    
    var="some value"
    if [ -n "$var" ]; then
        echo "變數不是空的"
    else
        echo "變數是空的"
    fi
        

    -n 用來檢查變數是否不為空,如果變數有值,條件成立。

    範例 3:使用雙引號比較來檢查變數是否為空

    #!/bin/bash
    
    var=""
    if [ "$var" == "" ]; then
        echo "變數是空的"
    else
        echo "變數不是空的"
    fi
        

    這種方式直接將變數與空字串進行比較,來檢查變數是否為空。



    Shell 重導向用法說明

    1. 標準輸出重導向:`>`

    在 Shell 中,使用 `>` 可以將指令的標準輸出(stdout)重導向到一個檔案或設備。若檔案已存在,內容會被覆蓋。

    echo "Hello" > output.txt

    這行指令將 "Hello" 寫入 output.txt 檔案。

    2. 追加輸出重導向:`>>`

    使用 `>>` 會將標準輸出附加到指定檔案的末尾,不會覆蓋原有內容。

    echo "Hello again" >> output.txt

    這行指令會將 "Hello again" 附加到 output.txt 的結尾。

    3. 標準錯誤重導向:`2>`

    在 Shell 中,`2>` 用於將標準錯誤(stderr)重導向到指定位置。例如:

    ls non_existent_file 2> error.log

    這行指令會將錯誤訊息寫入 error.log 檔案。

    4. 追加錯誤重導向:`2>>`

    若不想覆蓋錯誤訊息檔案,可使用 `2>>` 將錯誤訊息附加到檔案末尾。

    ls non_existent_file 2>> error.log

    這行指令會將錯誤訊息附加到 error.log

    5. 同時重導向標準輸出與錯誤:`&>`

    使用 `&>` 可以同時將標準輸出與標準錯誤都重導向到同一個檔案或設備。

    command &> all_output.log

    這行指令會將 command 的所有輸出(標準輸出與錯誤)寫入 all_output.log

    6. 合併標準錯誤到標準輸出:`2>&1`

    `2>&1` 將標準錯誤合併到標準輸出,便於統一管理。例如:

    command > output.log 2>&1

    這行指令會將標準輸出和錯誤都寫入 output.log

    7. 禁止所有輸出:`>/dev/null 2>&1`

    若不想顯示任何輸出,可將所有輸出導向到 /dev/null,如:

    command >/dev/null 2>&1

    這行指令會將 command 的所有輸出丟棄。



    iconv - tee 輸出以 UTF-8 編碼寫入文件

    在使用 tee 命令將輸出追加到文件時,可以通過 iconv 將輸出轉換為 UTF-8 編碼,確保文件內容以 UTF-8 保存。以下是具體的指令和範例。

    指令格式

    以下是將輸出保存為 UTF-8 編碼的 tee 指令格式:

    command | iconv -t utf-8 | tee -a output.txt

    範例

    以下範例演示如何將 ls 命令的輸出以 UTF-8 編碼寫入到 output.txt

    ls | iconv -t utf-8 | tee -a output.txt

    執行該指令後,output.txt 的內容將以 UTF-8 編碼保存,避免出現編碼錯誤。



    C與C++語言

    高效能

    靜態型別系統

    資源控制

    語法靈活性

    廣泛應用領域

    跨平台



    C++ 多維陣列初始化為 0

    使用 std::array 初始化

    在 C++ 中,可以使用 std::array 初始化多維陣列為 0:

    #include <iostream>
    #include <array>
    using namespace std;
    
    int main() {
        array<array<int, 4>, 3> arr = {0}; // 初始化所有元素為 0
    
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 4; j++) {
                cout << arr[i][j] << " ";
            }
            cout << endl;
        }
        return 0;
    }

    注意事項



    C++ 模板 (Template)

    1. 什麼是模板 (Template)?

    在 C++ 中,模板 (Template) 是一種泛型編程的工具,允許我們在編寫函數或類別時,不指定具體的資料型別,從而使程式碼更具重用性。模板有助於在單一程式碼中處理不同型別的資料,避免重複的函數或類別定義。

    2. 函數模板 (Function Template)

    函數模板允許我們撰寫可以處理不同型別的函數。函數模板的語法如下:

    template <typename T>
    T add(T a, T b) {
        return a + b;
    }

    在此例中,add 函數可以處理任何支持加法操作的型別,如 intfloatdouble

    使用時,可以這樣呼叫:

    int result = add(3, 4);  // 使用 int 型別
    double result2 = add(3.5, 2.7);  // 使用 double 型別

    3. 類別模板 (Class Template)

    類別模板允許我們建立可以適用於多種型別的類別。類別模板的語法如下:

    template <typename T>
    class MyClass {
    private:
        T data;
    public:
        MyClass(T data) : data(data) {}
        T getData() { return data; }
    };

    此例中的 MyClass 類別可以使用任何型別的資料作為 data

    使用時可以這樣:

    MyClass<int> obj1(5);      // int 型別
    MyClass<double> obj2(3.14);  // double 型別

    4. 多個模板參數

    模板可以接受多個參數,例如:

    template <typename T, typename U>
    class Pair {
    private:
        T first;
        U second;
    public:
        Pair(T first, U second) : first(first), second(second) {}
        T getFirst() { return first; }
        U getSecond() { return second; }
    };

    這樣的模板類別可以存儲兩種不同型別的資料:

    Pair<int, double> pair1(1, 3.14);

    5. 模板特化 (Template Specialization)

    模板特化允許我們對特定型別的模板進行特別定義。例如:

    template <>
    class MyClass<int> {
    public:
        MyClass(int data) { /* 特化行為 */ }
    };

    這段程式碼特化了 MyClassint 型別的行為,使其與其他型別不同。

    6. 非型別模板參數 (Non-Type Template Parameters)

    模板還可以接受非型別的參數,例如常數值:

    template <typename T, int Size>
    class Array {
    private:
        T data[Size];
    public:
        int getSize() const { return Size; }
    };

    在這裡,Size 是一個非型別參數,表示陣列的大小。

    使用範例:

    Array<int, 10> arr;  // 建立大小為 10 的 int 型別陣列

    7. 結論

    C++ 模板功能強大,使得程式碼可以更具泛用性並減少重複。了解如何利用函數模板、類別模板及模板特化等技術,將大大提升程式設計的彈性與效能。



    C++ 中互相參照的 Class 解決方案

    1. 問題背景

    在 C++ 中,當兩個類別相互依賴並需要同時引用對方的成員時,直接在各自的 .h 檔案中 #include 對方的定義會造成循環引用問題,導致無法編譯。解決方法是使用「前向宣告」(forward declaration)來避免循環引用。

    2. 解決方案步驟

    3. 程式碼範例

    ClassA.h

    
    // ClassA.h
    #ifndef CLASSA_H
    #define CLASSA_H
    
    // 前向宣告 ClassB
    class ClassB;
    
    class ClassA {
    public:
        ClassA();
        void setB(ClassB* b); // 設定指向 ClassB 的指標
        void showBData();      // 顯示 ClassB 的數據
    
    private:
        ClassB* b; // 指向 ClassB 的指標
    };
    
    #endif
            

    ClassB.h

    
    // ClassB.h
    #ifndef CLASSB_H
    #define CLASSB_H
    
    // 前向宣告 ClassA
    class ClassA;
    
    class ClassB {
    public:
        ClassB(int data);
        int getData();         // 取得數據
        void setA(ClassA* a);  // 設定指向 ClassA 的指標
        void showAInfo();      // 顯示 ClassA 的資訊
    
    private:
        int data;
        ClassA* a; // 指向 ClassA 的指標
    };
    
    #endif
            

    ClassA.cpp

    
    #include "ClassA.h"
    #include "ClassB.h"
    #include <iostream>
    
    ClassA::ClassA() : b(nullptr) {}
    
    void ClassA::setB(ClassB* b) {
        this->b = b;
    }
    
    void ClassA::showBData() {
        if (b != nullptr) {
            std::cout << "ClassB data: " << b->getData() << std::endl;
        }
    }
            

    ClassB.cpp

    
    #include "ClassB.h"
    #include "ClassA.h"
    #include <iostream>
    
    ClassB::ClassB(int data) : data(data), a(nullptr) {}
    
    int ClassB::getData() {
        return data;
    }
    
    void ClassB::setA(ClassA* a) {
        this->a = a;
    }
    
    void ClassB::showAInfo() {
        if (a != nullptr) {
            a->showBData();
        }
    }
            

    4. 實作說明



    C++ Friend

    在 C++ 中,friend 關鍵字可以用於函數或類別,以允許其他函數或類別訪問類別的私有成員(private)和保護成員(protected)。這樣的設計可讓外部函數或類別進行操作,而不違反封裝原則。

    1. Friend 函數

    Friend 函數是一種被授權訪問另一類別的私有成員和保護成員的外部函數。在類別內部聲明時,以 friend 關鍵字修飾即可。

    範例如下:

    #include <iostream>
    using namespace std;
    
    class Box {
    private:
        double width;
    
    public:
        Box(double w) : width(w) {}
    
        // 聲明 friend 函數
        friend void showWidth(Box &b);
    };
    
    // friend 函數定義,能訪問 Box 類別的私有成員
    void showWidth(Box &b) {
        cout << "Box 寬度: " << b.width << endl;
    }
    
    int main() {
        Box box(10.5);
        showWidth(box);  // 訪問私有成員 width
        return 0;
    }
            

    在此範例中,showWidth 函數雖然是 Box 類別外的普通函數,但因為被聲明為 friend 函數,仍然能夠訪問 Box 類別的私有成員 width

    2. Friend 類別

    Friend 類別允許某一類別訪問另一類別的所有成員。這樣的設置在類別需要緊密協作時非常有用,但使用時應該謹慎,以避免過多暴露內部細節。

    範例如下:

    #include <iostream>
    using namespace std;
    
    class Square;  // 前置聲明
    
    class Rectangle {
    private:
        double width, height;
    
    public:
        Rectangle(double w, double h) : width(w), height(h) {}
    
        // 聲明 Square 類別為 friend
        friend class Square;
    };
    
    class Square {
    public:
        double areaOfRectangle(Rectangle &rect) {
            return rect.width * rect.height;
        }
    };
    
    int main() {
        Rectangle rect(5.0, 3.0);
        Square square;
        cout << "矩形面積: " << square.areaOfRectangle(rect) << endl;
        return 0;
    }
            

    在此範例中,Square 類別被宣告為 Rectangle 類別的 friend 類別,因此 Square 類別中的成員函數可以直接訪問 Rectangle 類別的私有成員 widthheight

    3. Friend 函數與 Friend 類別的應用情境

    在 C++ 中使用 friend 函數和 friend 類別需謹慎,過多使用會破壞類別的封裝性。因此,friend 關鍵字通常只在設計需要密切配合的類別或函數時使用。



    .NET 程式

    使用 .NET C++ 取得目錄中最新的檔案

    簡介

    在 .NET C++ 中,可以使用 System::IO 命名空間提供的功能來操作檔案與目錄。 取得目錄中最新的檔案,可以透過讀取所有檔案資訊並比較最後修改日期實現。

    範例程式碼

    以下是一個完整的範例,展示如何取得指定目錄中最新的檔案:

    #include "stdafx.h"
    #include <iostream>
    #include <cliext/vector>
    #include <System.IO>
    
    using namespace System;
    using namespace System::IO;
    using namespace cliext;
    
    int main()
    {
        try
        {
            // 指定目錄路徑
            String^ directoryPath = "C:\\Your\\Directory\\Path";
    
            // 檢查目錄是否存在
            if (!Directory::Exists(directoryPath))
            {
                Console::WriteLine("目錄不存在: {0}", directoryPath);
                return -1;
            }
    
            // 取得目錄中的所有檔案
            array^ files = Directory::GetFiles(directoryPath);
    
            // 如果目錄中沒有檔案
            if (files->Length == 0)
            {
                Console::WriteLine("目錄中沒有檔案。");
                return 0;
            }
    
            // 找到最新的檔案
            String^ newestFile = nullptr;
            DateTime newestTime = DateTime::MinValue;
    
            for each (String^ file in files)
            {
                // 取得檔案的最後修改時間
                DateTime lastWriteTime = File::GetLastWriteTime(file);
    
                // 比較時間並更新最新檔案資訊
                if (lastWriteTime > newestTime)
                {
                    newestTime = lastWriteTime;
                    newestFile = file;
                }
            }
    
            // 輸出最新檔案資訊
            Console::WriteLine("最新檔案: {0}", newestFile);
            Console::WriteLine("最後修改時間: {0}", newestTime);
        }
        catch (Exception^ ex)
        {
            Console::WriteLine("發生錯誤: {0}", ex->Message);
        }
    
        return 0;
    }
    

    程式碼解說

    應用場景

    注意事項



    .NET: System.Reflection

    1. System.Reflection 是什麼?

    System.Reflection 是 .NET 框架中的一個命名空間,提供了檢查和操作元數據的工具,使開發者可以在運行時動態檢查類型、方法、屬性等,並動態創建和操縱對象。

    2. System.Reflection 的用途

    3. 常見的 System.Reflection 類別

    4. 使用範例

    以下是一個使用 System.Reflection 的範例程式,展示如何動態檢查類型和方法,並調用方法。

    
    // 定義一個簡單的範例類別
    public class SampleClass {
        public string SayHello(string name) {
            return $"Hello, {name}!";
        }
    }
    
    // 使用 Reflection 來動態調用方法
    using System;
    using System.Reflection;
    
    class Program {
        static void Main() {
            // 創建 SampleClass 類別的實例
            Type sampleType = typeof(SampleClass);
            object sampleInstance = Activator.CreateInstance(sampleType);
    
            // 獲取 SayHello 方法資訊
            MethodInfo methodInfo = sampleType.GetMethod("SayHello");
    
            // 動態調用 SayHello 方法
            object result = methodInfo.Invoke(sampleInstance, new object[] { "World" });
            Console.WriteLine(result); // 輸出: Hello, World!
        }
    }
            

    在上述範例中,我們使用 Activator.CreateInstance 來創建類別的實例,並使用 MethodInfo.Invoke 來調用方法 SayHello

    5. 常見應用場景



    .NET 遍歷子控制項以批量修改屬性

    以下範例展示了如何使用 Controls->GetEnumerator() 方法來逐一遍歷 .NET 表單中的所有子控制項並批量修改其屬性。

    範例程式碼:

    
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    public class FormExample : Form
    {
        public FormExample()
        {
            // 初始化一些控制項
            Button button1 = new Button { Text = "Button 1", Location = new Point(10, 10) };
            TextBox textBox1 = new TextBox { Location = new Point(10, 50) };
            
            Controls.Add(button1);
            Controls.Add(textBox1);
    
            // 使用 GetEnumerator() 遍歷並修改控制項屬性
            ModifyControls();
        }
    
        private void ModifyControls()
        {
            var enumerator = Controls.GetEnumerator();
            
            while (enumerator.MoveNext())
            {
                Control control = (Control)enumerator.Current;
    
                // 設定範例:將所有控制項的背景色設為淺藍
                control.BackColor = Color.LightBlue;
                
                // 若控制項為 TextBox,將其設為不可編輯
                if (control is TextBox)
                {
                    control.Enabled = false;
                }
            }
        }
        
        // 啟動應用程式
        public static void Main()
        {
            Application.Run(new FormExample());
        }
    }
    

    說明

    **GetEnumerator()**: 利用 `Controls.GetEnumerator()` 方法來取得控制項的列舉器,這樣可以遍歷所有子控制項。

    **條件修改**: 在 `while` 迴圈中,對每個 `Control` 物件進行屬性修改,例如將背景色設為淺藍,並根據控制項類型進行特定修改,如將 `TextBox` 設為不可編輯。

    **用途**: 這種方法在需要批量修改屬性時非常有效,比如調整 UI 風格或在特定情況下禁用多個控制項。

    執行效果

    執行後,所有控制項的背景色會變成淺藍色,且所有 `TextBox` 控制項將被設為不可編輯。



    .NET StackTrace中取得 Message m 的內容

    問題背景

    當堆疊追蹤中出現以下錯誤訊息時,開發者可能需要檢查 Message 物件的內容:

       at ....MainForm.Dispose(Boolean A_0)
       at System.Windows.Forms.Form.WmClose(Message& m)
       at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       ...
        

    此時需要透過覆寫或檢測 WndProc 方法中的 Message m 來檢查其詳細資訊。

    解決方法

    以下提供幾種方法來檢查或記錄 Message 物件的內容。

    1. 覆寫 WndProc 方法

    如果可以存取表單或控制項的原始碼,建議覆寫 WndProc 方法,直接記錄或檢查 Message m 的內容。

    
    protected override void WndProc(ref Message m)
    {
        try
        {
            // 記錄 Message 的內容
            Console.WriteLine($"Message Details: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
            base.WndProc(ref m);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex}");
            throw; // 保持原有例外行為
        }
    }
    
        

    這段程式碼會在每次接收到訊息時記錄相關資訊,並允許開發者進一步分析。

    2. 在 Dispose 方法中加入例外處理

    如果錯誤發生在 Dispose 方法中,可以在方法內加入例外處理來檢查相關資訊。

    
    protected override void Dispose(bool disposing)
    {
        try
        {
            if (disposing)
            {
                // 釋放資源
            }
            base.Dispose(disposing);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception in Dispose: {ex}");
            throw;
        }
    }
    
        

    這樣可以確保在釋放資源時不會忽略重要的錯誤資訊。

    3. 使用全域例外處理

    如果無法確定錯誤發生的位置,可以透過全域例外處理來記錄堆疊追蹤和相關資訊。

    
    AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
    {
        Exception ex = (Exception)args.ExceptionObject;
        Console.WriteLine($"Unhandled Exception: {ex}");
        Environment.Exit(1);
    };
    
        

    4. 使用 Debug 工具檢查 Message

    可以使用 Visual Studio 將斷點設置在 WndProcDispose 方法中,並檢查 Message 物件的內容。

    Message 物件的關鍵屬性

    注意事項



    覆寫 Form.WmClose(Message& m) 在 .NET C++

    問題背景

    在 .NET C++/CLI 中,WmClose(Message& m)System.Windows.Forms.Form 的內部保護方法,無法直接覆寫。不過,可以透過覆寫 WndProc 方法來攔截並處理 WM_CLOSE 訊息,以達到類似覆寫 WmClose 的效果。

    完整範例

    
    #include <Windows.h>
    #include <System.Windows.Forms.h>
    
    using namespace System;
    using namespace System::Windows::Forms;
    
    public ref class CustomForm : public Form
    {
    protected:
        // 模擬 WmClose 行為的覆寫
        void WmClose(Message% m)
        {
            // 在這裡加入自定義的 WM_CLOSE 行為
            if (MessageBox::Show("確定要關閉視窗嗎?", "確認", MessageBoxButtons::YesNo) == DialogResult::Yes)
            {
                // 繼續調用基底行為以進行正常關閉
                this->Form::WndProc(m);
            }
            else
            {
                // 阻止關閉視窗
                return;
            }
        }
    
        // 覆寫 WndProc,攔截 WM_CLOSE 訊息並調用 WmClose
        virtual void WndProc(Message% m) override
        {
            const int WM_CLOSE = 0x0010;
    
            if (m.Msg == WM_CLOSE)
            {
                WmClose(m); // 調用自定義的 WmClose 方法
            }
            else
            {
                // 處理其他訊息
                Form::WndProc(m);
            }
        }
    };
    
    [STAThread]
    int main(array<String^>^ args)
    {
        Application::EnableVisualStyles();
        Application::SetCompatibleTextRenderingDefault(false);
    
        CustomForm^ form = gcnew CustomForm();
        form->Text = "覆寫 WmClose 行為範例";
        Application::Run(form);
    
        return 0;
    }
    
        

    程式碼解釋

    1. WmClose 方法

    模擬覆寫 WmClose 方法,在這裡自定義視窗關閉的行為。透過確認對話框詢問使用者是否要關閉視窗。

    2. 覆寫 WndProc

    覆寫 WndProc 方法來攔截 WM_CLOSE 訊息,並將其委派給自定義的 WmClose 方法。

    3. 繼續處理其他訊息

    在沒有攔截 WM_CLOSE 的情況下,調用基底類別的 WndProc 方法處理其他訊息。

    注意事項



    .NET 程式在 Chromebook 上執行方法

    1. 使用 Chromebook 的 Linux 支援執行 .NET

    大多數現代 Chromebook 支援透過 Crostini 專案來運行 Linux 應用程式。

    1. 啟用 Linux 功能:
      • 進入 設定 > 進階 > 開發人員 > 啟用 Linux (Beta)
    2. 安裝 .NET SDK:
      • 打開 Linux 終端機,依照 Microsoft 官方網站的指引安裝。
      • 例如:
        sudo apt update
        sudo apt install -y dotnet-sdk-7.0
    3. 在終端機中編譯並執行 .NET 應用程式。

    2. 使用雲端開發環境

    無需在本地安裝任何軟體,即可在 Chromebook 上使用雲端平台運行 .NET 程式。

    3. 使用 Docker 容器運行 .NET

    Chromebook 支援使用容器運行應用程式,您可以透過 Docker 啟動 .NET 環境。

    1. 在 Linux 環境中安裝 Docker。
    2. 下載 .NET 容器映像檔:
      docker pull mcr.microsoft.com/dotnet/runtime:7.0
    3. 在容器內運行您的 .NET 程式。

    4. 使用跨平台支援功能

    從 .NET 6 開始,應用程式可以跨多平台運行,包括 Linux。將您的應用程式編譯為跨平台格式並部署至 Chromebook。

    1. 在任何機器上編譯您的應用程式:
      dotnet publish -c Release -r linux-x64 --self-contained
    2. 將編譯好的檔案傳輸到 Chromebook,使用附帶的運行時執行。

    注意事項



    Lambda 表達式

    1. 什麼是 Lambda 表達式?

    Lambda 表達式是一種匿名函數,通常用於簡化代碼,特別是在需要傳遞小型函數或回調的情況下。Lambda 表達式的語法簡潔,可以在一行中定義函數邏輯。Lambda 表達式最常見於 C++、JavaScript、Python 和 C# 等語言。

    2. Lambda 表達式的基本語法

    Lambda 表達式的基本語法通常包括參數、箭頭符號 => 和函數體,例如:

    (參數) => 函數體

    具體語法因語言而異,例如:

    3. 各語言中的 Lambda 表達式範例

    C++ 範例

    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    int main() {
        std::vector numbers = {1, 2, 3, 4, 5};
    
        // 使用 lambda 表達式來計算偶數的和
        int sum = 0;
        std::for_each(numbers.begin(), numbers.end(), [&sum](int n) {
            if (n % 2 == 0) sum += n;
        });
    
        std::cout << "偶數的總和: " << sum << std::endl;
        return 0;
    }
            

    Python 範例

    
    # 使用 lambda 表達式來計算兩數的和
    add = lambda x, y: x + y
    print(add(5, 10))  # 輸出: 15
            

    C# 範例

    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    class Program {
        static void Main() {
            List numbers = new List { 1, 2, 3, 4, 5 };
            
            // 使用 Lambda 表達式篩選出偶數並計算總和
            int sum = numbers.Where(n => n % 2 == 0).Sum();
            
            Console.WriteLine($"偶數的總和: {sum}");
        }
    }
            

    4. Lambda 表達式的應用場景

    5. 優缺點



    資料庫

    SQL

    定義

    SQL(Structured Query Language)是結構化查詢語言,用於管理和操作關聯式數據庫。它是數據庫管理系統中使用最廣泛的語言之一。

    主要功能

    基本語法

    -- 查詢數據
    SELECT * FROM 表名 WHERE 條件;
    
    -- 插入數據
    INSERT INTO 表名 (列1, 列2) VALUES (值1, 值2);
    
    -- 更新數據
    UPDATE 表名 SET 列1 = 值1 WHERE 條件;
    
    -- 刪除數據
    DELETE FROM 表名 WHERE 條件;
    
    -- 創建表
    CREATE TABLE 表名 (
        列名1 數據類型,
        列名2 數據類型
    );
    

    常見數據類型

    優點



    MySQL

    關聯式資料庫

    MySQL 是一種流行的開源關聯式資料庫管理系統 (RDBMS),使用 SQL 作為查詢語言,適合中小型到大型應用程式。

    特性

    使用範例

    mysql -u root -p
    CREATE DATABASE example_db;
    USE example_db;
    CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), age INT);
    INSERT INTO users (name, age) VALUES ('Alice', 30);
    SELECT * FROM users;
    

    適用場景



    SQLite

    輕量化資料庫

    SQLite 是一種嵌入式資料庫,無需獨立伺服器進行管理,適合用於輕量級應用程式。

    特性

    使用範例

    sqlite3 example.db
    CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);
    INSERT INTO users (name, age) VALUES ('Alice', 30);
    SELECT * FROM users;
    

    適用場景



    用 Name 代替 ID 欄位

    適用條件

    優勢

    潛在問題

    適用範例

    如設定檔的類型資料表:

    CREATE TABLE config_types (
      name VARCHAR(50) PRIMARY KEY,
      description TEXT
    );
    
    INSERT INTO config_types (name, description) VALUES ('general', 'General settings');
    SELECT * FROM config_types WHERE name = 'general';
    


    關聯式資料庫設計繼承物件

    資料庫結構設計

    animal 表格

    此表格將存放所有「動物」的通用屬性。

    欄位名稱 資料型態 說明
    id INT 動物的唯一識別碼
    species VARCHAR(50) 動物的種類
    age INT 動物的年齡

    cat 表格

    此表格將繼承 animal 表格的 id,並儲存「貓」的特有屬性。

    欄位名稱 資料型態 說明
    id INT 對應到 animal 表格的 id
    breed VARCHAR(50) 貓的品種
    favorite_food VARCHAR(50) 貓的喜愛食物

    SQL 建立表格指令

    CREATE TABLE animal (
        id INT PRIMARY KEY AUTO_INCREMENT,
        species VARCHAR(50) NOT NULL,
        age INT NOT NULL
    );
    
    CREATE TABLE cat (
        id INT PRIMARY KEY,
        breed VARCHAR(50),
        favorite_food VARCHAR(50),
        FOREIGN KEY (id) REFERENCES animal(id)
    );
        

    插入資料的範例

    INSERT INTO animal (species, age) VALUES ('Cat', 3);
    
    INSERT INTO cat (id, breed, favorite_food) VALUES (1, 'Siamese', 'Fish');
        

    HTML 表格展示範例

    動物資料

    動物ID 種類 年齡
    1 Cat 3

    貓的特有資料

    動物ID 品種 喜愛食物
    1 Siamese Fish

    說明

    在此範例中,animal 表格儲存了所有動物的共同屬性,而 cat 表格則儲存貓的特有屬性。cat 表格中的 id 是參照 animal 表格的 id,代表這是一種繼承關係。

    查詢所有動物的資料

    SELECT * FROM animal;
        

    查詢所有貓的資料

    這個查詢將返回所有貓的完整資料,包括繼承自 animal 表格的通用屬性。

    SELECT animal.id, animal.species, animal.age, cat.breed, cat.favorite_food
    FROM animal
    JOIN cat ON animal.id = cat.id;
        

    查詢特定動物的資料(例如特定 ID)

    SELECT * FROM animal WHERE id = 1;
        

    查詢特定貓的資料(例如特定品種)

    SELECT animal.id, animal.species, animal.age, cat.breed, cat.favorite_food
    FROM animal
    JOIN cat ON animal.id = cat.id
    WHERE cat.breed = 'Siamese';
        

    說明

    在這些查詢範例中,我們使用 JOINanimal 表格和 cat 表格結合起來,以取得貓的完整資料。這種方法確保查詢結果包含繼承屬性與特有屬性。



    FOREIGN KEY

    用途

    FOREIGN KEY(外鍵)用於建立兩個資料表之間的關聯性,確保資料的參考完整性。例如,一個資料表的欄位值必須參考另一個資料表中的主鍵或唯一值。

    語法

    CREATE TABLE 子資料表 (
      欄位名稱 資料類型,
      FOREIGN KEY (外鍵欄位) REFERENCES 父資料表(主鍵欄位)
    );
    

    範例

    建立一對多的關聯,例如訂單與客戶:

    -- 建立父資料表 (customers)
    CREATE TABLE customers (
      customer_id INT PRIMARY KEY,
      name VARCHAR(50)
    );
    
    -- 建立子資料表 (orders) 並設定外鍵
    CREATE TABLE orders (
      order_id INT PRIMARY KEY,
      customer_id INT,
      order_date DATE,
      FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    );
    

    注意事項

    進階用法

    可以透過 ON DELETEON UPDATE 指定外鍵行為:

    CREATE TABLE orders (
      order_id INT PRIMARY KEY,
      customer_id INT,
      order_date DATE,
      FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
      ON DELETE CASCADE
      ON UPDATE CASCADE
    );
    

    行為選項



    在 CREATE TABLE 中加入備註

    使用 COMMENT 設定欄位備註

    在 MySQL 中,可以使用 COMMENT 來為欄位添加備註。

    CREATE TABLE users (
      id INT PRIMARY KEY COMMENT '使用者唯一識別碼',
      name VARCHAR(50) COMMENT '使用者姓名',
      age INT COMMENT '使用者年齡'
    );
    

    使用 COMMENT 設定資料表備註

    CREATE TABLE users (
      id INT PRIMARY KEY,
      name VARCHAR(50),
      age INT
    ) COMMENT = '使用者資料表';
    

    查看欄位備註

    可透過以下語法查詢欄位的備註:

    SHOW FULL COLUMNS FROM users;
    

    修改欄位備註

    ALTER TABLE users MODIFY COLUMN name VARCHAR(50) COMMENT '修改後的備註';
    

    適用範圍

    PostgreSQL 設定備註

    COMMENT ON COLUMN users.name IS '使用者姓名';
    


    SQL DATETIME 比較

    使用 TIMESTAMPDIFF

    計算兩個 DATETIME 欄位的時間差(單位:秒)。

    SELECT * FROM table_name
    WHERE TIMESTAMPDIFF(SECOND, datetime_column1, datetime_column2) < 5;
    

    使用 ABS(TIMESTAMPDIFF)

    確保時間差為絕對值,避免順序影響。

    SELECT * FROM table_name
    WHERE ABS(TIMESTAMPDIFF(SECOND, datetime_column1, datetime_column2)) < 5;
    

    使用 DATEDIFF(僅適用於天數比較)

    若只需比較是否為同一天,可以使用 DATEDIFF。

    SELECT * FROM table_name
    WHERE DATEDIFF(datetime_column1, datetime_column2) = 0;
    

    使用 TIMESTAMP 直接相減

    適用於支援時間戳記運算的資料庫,如 MySQL。

    SELECT * FROM table_name
    WHERE ABS(UNIX_TIMESTAMP(datetime_column1) - UNIX_TIMESTAMP(datetime_column2)) < 5;
    


    格式化 FLOAT

    使用 FORMAT()

    在 MySQL 中,可使用 FORMAT() 來格式化浮點數。

    SELECT FORMAT(123.4567, 2); -- 結果: '123.46'
    

    使用 ROUND()

    ROUND() 用於四捨五入至固定小數位數。

    SELECT ROUND(123.4567, 2); -- 結果: 123.46
    

    使用 CAST() 或 CONVERT()

    將 FLOAT 轉換為 DECIMAL 以保持固定小數位數。

    SELECT CAST(123.4567 AS DECIMAL(10,2)); -- 結果: 123.46
    SELECT CONVERT(123.4567, DECIMAL(10,2)); -- 結果: 123.46
    

    應用在資料表欄位

    SELECT id, FORMAT(price, 2) AS formatted_price FROM products;
    

    設定欄位格式

    建立資料表時可直接設定小數點位數。

    CREATE TABLE products (
      id INT PRIMARY KEY,
      price DECIMAL(10,2) -- 兩位小數
    );
    


    查詢欄位總計

    使用 MAX()

    取得欄位的最大值。

    SELECT MAX(price) AS max_price FROM products;
    

    使用 MIN()

    取得欄位的最小值。

    SELECT MIN(price) AS min_price FROM products;
    

    使用 AVG()

    計算欄位的平均值。

    SELECT AVG(price) AS avg_price FROM products;
    

    同時查詢 MAX、MIN、AVG

    SELECT 
      MAX(price) AS max_price, 
      MIN(price) AS min_price, 
      AVG(price) AS avg_price
    FROM products;
    

    依類別分組計算

    SELECT category, 
           MAX(price) AS max_price, 
           MIN(price) AS min_price, 
           AVG(price) AS avg_price
    FROM products
    GROUP BY category;
    

    使用子查詢找最大值

    對另一個查詢結果求最大值。

    SELECT MAX(price) FROM (SELECT price FROM products WHERE category = 'electronics') AS subquery;
    

    使用 ORDER BY + LIMIT

    排序後取最大值。

    SELECT price FROM products WHERE category = 'electronics' ORDER BY price DESC LIMIT 1;
    

    使用 WITH (Common Table Expression, CTE)

    WITH filtered_products AS (
      SELECT price FROM products WHERE category = 'electronics'
    )
    SELECT MAX(price) FROM filtered_products;
    

    幾何平均數公式

    幾何平均數(Geometric Mean)計算公式:

    GM = (x1 * x2 * ... * xn)^(1/n)
    

    使用 EXP() 和 LOG()

    在 SQL 中,可以透過對數運算來計算幾何平均數。

    SELECT EXP(AVG(LOG(price))) AS geometric_mean FROM products WHERE price > 0;
    

    使用 POWER()

    使用 POWER() 計算 n 次方根:

    SELECT POWER(EXP(SUM(LOG(price))), 1 / COUNT(price)) AS geometric_mean 
    FROM products WHERE price > 0;
    

    注意事項

    SELECT EXP(AVG(LOG(price))) AS geometric_mean FROM products WHERE price > 0;
    


    SQL 計算欄位的標準差

    使用 STDDEV() 計算標準差

    在 MySQL / MariaDB 中,可以使用 STDDEV() 來計算標準差。

    範例:

    SELECT STDDEV(salary) AS salary_stddev FROM employees;

    區分母標準差與樣本標準差

    SQL 提供兩種標準差計算方式:

    範例:

    SELECT 
        STDDEV_POP(salary) AS population_stddev, 
        STDDEV_SAMP(salary) AS sample_stddev
    FROM employees;

    手動計算標準差

    如果 SQL 版本不支援 STDDEV(),可以使用以下公式:

    SELECT SQRT(
        SUM(POW(salary - (SELECT AVG(salary) FROM employees), 2)) / COUNT(salary)
    ) AS salary_stddev
    FROM employees;

    結論



    SQL 查詢中是否能在別名(Alias)中引用另一個計算欄位

    問題描述

    SELECT 查詢中,如果定義了 expr1 AS field1,能否在 expr2 中使用 field1

    答案:不行,因為 SQL 查詢的執行順序

    SQL 的執行順序決定了別名不能在同一個 SELECT 內部被再次引用:

    SELECT price * 1.1 AS new_price, new_price + 10 AS final_price FROM products; -- 錯誤

    錯誤訊息:

    Unknown column 'new_price' in 'field list'

    解決方法

    方法 1:使用子查詢

    可以在子查詢中先計算 new_price,然後在外部查詢中引用:

    SELECT new_price, new_price + 10 AS final_price
    FROM (SELECT price * 1.1 AS new_price FROM products) AS subquery;

    方法 2:使用 CTE(WITH 語句)

    若 SQL 支援 Common Table Expressions(CTE),可用 WITH 來簡化:

    WITH cte AS (
        SELECT price * 1.1 AS new_price FROM products
    )
    SELECT new_price, new_price + 10 AS final_price FROM cte;

    方法 3:重複表達式

    如果只是簡單運算,可以直接重複計算(但不推薦,因為可讀性較差):

    SELECT price * 1.1 AS new_price, price * 1.1 + 10 AS final_price FROM products;

    結論



    JOIN

    JOIN 簡介

    JOIN 用於合併多個資料表的相關資料,根據某個欄位(通常是外鍵)建立關聯。

    INNER JOIN

    只返回兩個資料表中符合條件的資料。

    SELECT orders.order_id, customers.name
    FROM orders
    INNER JOIN customers ON orders.customer_id = customers.customer_id;
    

    LEFT JOIN

    返回左表的所有資料,若右表沒有對應資料,則顯示 NULL

    SELECT customers.name, orders.order_id
    FROM customers
    LEFT JOIN orders ON customers.customer_id = orders.customer_id;
    

    RIGHT JOIN

    返回右表的所有資料,若左表沒有對應資料,則顯示 NULL

    SELECT customers.name, orders.order_id
    FROM orders
    RIGHT JOIN customers ON orders.customer_id = customers.customer_id;
    

    FULL JOIN

    返回左右兩表的所有資料,若沒有匹配則顯示 NULL

    MySQL 不支援 FULL JOIN,可用 LEFT JOINRIGHT JOIN 組合模擬。

    SELECT customers.name, orders.order_id
    FROM customers
    LEFT JOIN orders ON customers.customer_id = orders.customer_id
    UNION
    SELECT customers.name, orders.order_id
    FROM customers
    RIGHT JOIN orders ON customers.customer_id = orders.customer_id;
    

    CROSS JOIN

    返回兩個表的所有可能組合(笛卡兒積)。

    SELECT customers.name, products.product_name
    FROM customers
    CROSS JOIN products;
    

    SELF JOIN

    用於同一個表內部的關聯,如員工的上級關係。

    SELECT A.name AS employee, B.name AS manager
    FROM employees A
    JOIN employees B ON A.manager_id = B.employee_id;
    


    在執行 .sql 時,包含其他 .sql 文件

    使用 SOURCE 或 \i 指令

    MySQL 或 MariaDB

    可以在 MySQL 或 MariaDB 的腳本中使用 `SOURCE` 指令包含其他 `.sql` 文件:
    -- abc.sql
    SOURCE other_file.sql;
    SOURCE another_file.sql;
    

    PostgreSQL (psql)

    在 PostgreSQL 中,可以使用 `\i` 指令包含其他 `.sql` 文件:
    -- abc.sql
    \i other_file.sql
    \i another_file.sql
    

    使用批次腳本執行

    當 SQL 客戶端不支援直接包含文件時,可以使用批次腳本來依序執行多個 `.sql` 文件。

    Linux Shell 範例

    #!/bin/bash
    mysql -u user -p database_name < abc.sql
    mysql -u user -p database_name < other_file.sql
    

    進階方式:預處理 SQL 文件

    如果 SQL 客戶端無法直接支援文件包含,可以先將主 SQL 文件與參考的 `.sql` 文件合併,然後再執行。

    使用 Shell Script 合併文件

    cat abc.sql other_file.sql another_file.sql > combined.sql
    mysql -u user -p database_name < combined.sql
    

    注意事項

    1. **執行順序**:確保所包含的文件按正確順序執行,避免資料表或函數的相依性問題。 2. **資料庫特定語法**:不同資料庫的指令語法可能不同,請參閱相應的文件。 3. **文件路徑**:正確使用絕對或相對路徑來引用 `.sql` 文件。 4. **存取權限**:確保 SQL 客戶端有權限讀取被包含的文件。

    總結

    通過上述方法,可以將 SQL 腳本模組化,方便管理和重複使用。

    包含其他 .sql 文件時傳遞參數

    MySQL 和 MariaDB

    MySQL 和 MariaDB 不直接支援在 `SOURCE` 指令中傳遞參數,但可以使用變數配合包含的 .sql 文件。以下是方法:

    使用變數傳遞參數

    1. 在主 SQL 文件中設置變數:
       SET @param1 = 'value1';
       SOURCE other_file.sql;
       
    2. 在 `other_file.sql` 中引用變數:
       SELECT * FROM table WHERE column = @param1;
       

    PostgreSQL (psql)

    PostgreSQL 支援透過 `\set` 指令設置變數並傳遞到其他文件中:

    使用變數傳遞參數

    1. 在主 SQL 文件中設置變數:
       \set param1 'value1'
       \i other_file.sql
       
    2. 在 `other_file.sql` 中使用變數:
       SELECT * FROM table WHERE column = :'param1';
       

    使用命令列工具傳遞參數

    透過命令列執行時傳遞參數到 SQL 文件是一種常見方法。

    MySQL 命令列範例

    1. 使用 `sed` 或其他工具在執行時替換參數:
       sed "s/{param1}/value1/g" abc.sql | mysql -u user -p database_name
       
    2. 在 SQL 文件中使用佔位符 `{param1}`,由命令列工具替換。

    PostgreSQL 命令列範例

    1. 在 `psql` 命令中直接設置參數:
       psql -d database_name -v param1=value1 -f abc.sql
       
    2. 在 SQL 文件中使用 `:'param1'` 表示變數。

    使用程式碼生成 SQL

    透過程式語言(如 Python 或 Bash)動態生成 SQL 是另一種解決方案: 1. 在程式中動態構建包含參數的 SQL 文件。 2. 執行生成的 SQL 文件。

    注意事項

    1. **安全性**:避免直接將用戶輸入的參數嵌入 SQL,應考慮 SQL 注入風險。 2. **環境變數**:某些工具支援使用環境變數作為參數傳遞。

    總結

    雖然 SQL 文件的參數傳遞不總是直接支援,但可以透過變數、命令列工具或程式語言來實現,靈活性取決於工具和數據庫的特性。

    儲存程序

    儲存程序簡介(預存常式)

    Stored Procedure(預存常式 或 儲存程序)是一組預先編譯並存儲在資料庫中的 SQL 語句,可以透過呼叫執行,提高效率並減少重複代碼。

    建立儲存程序

    DELIMITER //
    CREATE PROCEDURE GetAllProducts()
    BEGIN
      SELECT * FROM products;
    END //
    DELIMITER ;
    

    呼叫儲存程序

    CALL GetAllProducts();
    

    帶參數的儲存程序

    輸入參數

    查詢特定類別的商品:

    DELIMITER //
    CREATE PROCEDURE GetProductsByCategory(IN category_name VARCHAR(50))
    BEGIN
      SELECT * FROM products WHERE category = category_name;
    END //
    DELIMITER ;
    

    呼叫:

    CALL GetProductsByCategory('electronics');
    

    輸出參數

    計算某個類別的商品總數:

    DELIMITER //
    CREATE PROCEDURE GetProductCountByCategory(IN category_name VARCHAR(50), OUT total_count INT)
    BEGIN
      SELECT COUNT(*) INTO total_count FROM products WHERE category = category_name;
    END //
    DELIMITER ;
    

    呼叫:

    CALL GetProductCountByCategory('electronics', @count);
    SELECT @count;
    

    儲存程序內使用變數

    DELIMITER //
    CREATE PROCEDURE CalculateTotalRevenue()
    BEGIN
      DECLARE total DECIMAL(10,2);
      SELECT SUM(price) INTO total FROM sales;
      SELECT total AS total_revenue;
    END //
    DELIMITER ;
    

    呼叫:

    CALL CalculateTotalRevenue();
    

    儲存程序內使用條件控制

    IF 條件

    DELIMITER //
    CREATE PROCEDURE CheckStock(IN product_id INT, OUT stock_status VARCHAR(20))
    BEGIN
      DECLARE stock INT;
      SELECT quantity INTO stock FROM inventory WHERE id = product_id;
    
      IF stock > 10 THEN
        SET stock_status = 'In Stock';
      ELSEIF stock > 0 THEN
        SET stock_status = 'Low Stock';
      ELSE
        SET stock_status = 'Out of Stock';
      END IF;
    END //
    DELIMITER ;
    

    呼叫:

    CALL CheckStock(1, @status);
    SELECT @status;
    

    儲存程序內使用 LOOP

    DELIMITER //
    CREATE PROCEDURE CountDown(IN start_num INT)
    BEGIN
      DECLARE i INT;
      SET i = start_num;
    
      loop_label: LOOP
        IF i <= 0 THEN
          LEAVE loop_label;
        END IF;
        SELECT i;
        SET i = i - 1;
      END LOOP;
    END //
    DELIMITER ;
    

    呼叫:

    CALL CountDown(5);
    

    刪除儲存程序

    DROP PROCEDURE IF EXISTS GetAllProducts;
    

    結論



    Stored Procedure 的參數預設值

    MySQL / MariaDB 不支援直接設定參數的預設值

    在 MySQL 和 MariaDB 中,儲存程序的參數不能直接設定預設值(不像 SQL Server 或 PostgreSQL)。不過,可以使用 IF 條件語句來模擬預設值。

    方法 1:使用 IF 條件設定預設值

    假設我們要查詢 users 表,當參數 user_id 沒有提供時,預設查詢 ID 為 1:

    DELIMITER //
    CREATE PROCEDURE GetUserById(IN user_id INT)
    BEGIN
        IF user_id IS NULL THEN
            SET user_id = 1; -- 預設值
        END IF;
    
        SELECT * FROM users WHERE id = user_id;
    END //
    DELIMITER ;
    

    執行方式

    CALL GetUserById(NULL); -- 會查詢 id = 1
    CALL GetUserById(5); -- 查詢 id = 5

    方法 2:使用 COALESCE() 設定預設值

    COALESCE() 會在參數為 NULL 時回傳指定的預設值:

    DELIMITER //
    CREATE PROCEDURE GetUserById(IN user_id INT)
    BEGIN
        SELECT * FROM users WHERE id = COALESCE(user_id, 1);
    END //
    DELIMITER ;
    

    執行方式

    CALL GetUserById(NULL); -- 預設為 1
    CALL GetUserById(10); -- 查詢 id = 10

    方法 3:使用可選參數(變通方式)

    如果想要參數可選,可以建立多個 Stored Procedure。例如:

    DELIMITER //
    CREATE PROCEDURE GetAllUsers()
    BEGIN
        SELECT * FROM users;
    END //
    
    CREATE PROCEDURE GetUserById(IN user_id INT)
    BEGIN
        SELECT * FROM users WHERE id = user_id;
    END //
    DELIMITER ;
    

    執行方式

    CALL GetAllUsers(); -- 無參數,查詢所有
    CALL GetUserById(3); -- 查詢 id = 3

    結論



    Stored Procedure 的回傳值用法

    方法 1:使用 OUT 參數回傳值

    MySQL / MariaDB 的 Stored Procedure 不支援 RETURN 回傳查詢結果,但可以使用 OUT 參數來回傳值。

    DELIMITER //
    CREATE PROCEDURE GetUserCount(OUT user_count INT)
    BEGIN
        SELECT COUNT(*) INTO user_count FROM users;
    END //
    DELIMITER ;
    

    呼叫 Stored Procedure 並獲取回傳值

    CALL GetUserCount(@total);
    SELECT @total; -- 顯示用戶數量

    方法 2:使用 SELECT 回傳結果集

    如果要回傳一個查詢結果,直接 SELECT 即可:

    DELIMITER //
    CREATE PROCEDURE GetUserById(IN user_id INT)
    BEGIN
        SELECT * FROM users WHERE id = user_id;
    END //
    DELIMITER ;
    

    呼叫 Stored Procedure

    CALL GetUserById(5); -- 查詢 ID 為 5 的用戶

    方法 3:使用 RETURN 回傳單一數值

    雖然 MySQL 支援 RETURN,但只能回傳單一數值,通常用於控制流程:

    DELIMITER //
    CREATE PROCEDURE GetMaxSalary()
    BEGIN
        DECLARE max_salary DECIMAL(10,2);
        SELECT MAX(salary) INTO max_salary FROM employees;
        RETURN max_salary; -- 但這樣在 MySQL 不會直接返回值
    END //
    DELIMITER ;

    MySQL 不能直接 CALL 來獲取 RETURN 值,因此建議使用 OUT 參數:

    DELIMITER //
    CREATE PROCEDURE GetMaxSalary(OUT max_salary DECIMAL(10,2))
    BEGIN
        SELECT MAX(salary) INTO max_salary FROM employees;
    END //
    DELIMITER ;
    
    CALL GetMaxSalary(@max);
    SELECT @max; -- 顯示最高薪資

    方法 4:回傳多個值

    使用多個 OUT 參數回傳不同的計算結果:

    DELIMITER //
    CREATE PROCEDURE GetUserStats(OUT total_users INT, OUT avg_age DECIMAL(5,2))
    BEGIN
        SELECT COUNT(*) INTO total_users FROM users;
        SELECT AVG(age) INTO avg_age FROM users;
    END //
    DELIMITER ;
    

    呼叫 Stored Procedure 並取得多個回傳值

    CALL GetUserStats(@total, @avg);
    SELECT @total, @avg; -- 顯示用戶總數與平均年齡

    結論



    在 CALL 儲存程序之外使用回傳值

    方法 1:使用 OUT 參數並存入變數

    MySQL Stored Procedure 可以透過 OUT 參數回傳值,然後在 CALL 之外用 SELECT 取得該值。

    建立 Stored Procedure

    DELIMITER //
    CREATE PROCEDURE GetTotalUsers(OUT total_users INT)
    BEGIN
        SELECT COUNT(*) INTO total_users FROM users;
    END //
    DELIMITER ;
    

    呼叫 Stored Procedure 並使用回傳值

    CALL GetTotalUsers(@total);
    SELECT @total AS UserCount; -- 在 CALL 之外使用回傳值

    方法 2:使用變數存入回傳的查詢結果

    如果 Stored Procedure 使用 SELECT 回傳結果,不能直接存入變數,但可以用 INSERT INTO ... SELECT

    建立 Stored Procedure

    DELIMITER //
    CREATE PROCEDURE GetMaxSalary()
    BEGIN
        SELECT MAX(salary) AS max_salary FROM employees;
    END //
    DELIMITER ;
    

    使用變數存取查詢結果

    CREATE TEMPORARY TABLE temp_result (max_salary DECIMAL(10,2));
    
    INSERT INTO temp_result EXECUTE GetMaxSalary();
    
    SELECT max_salary FROM temp_result; -- 在 CALL 之外使用

    方法 3:使用 Prepared Statement 動態存取結果

    如果 Stored Procedure 產生的結果需要在變數中存取,可以使用 PREPAREEXECUTE

    建立 Stored Procedure

    DELIMITER //
    CREATE PROCEDURE GetUserById(IN user_id INT)
    BEGIN
        SELECT name FROM users WHERE id = user_id;
    END //
    DELIMITER ;
    

    呼叫並存入變數

    SET @sql = 'CALL GetUserById(5)';
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    結論



    MySQL / MariaDB 不允許函數回傳結果集的解決方案

    問題描述

    在 MySQL / MariaDB 中,Stored Function(函數)不能回傳 SELECT 結果集,否則會出現錯誤:

    ERROR 1415 (0A000): Not allowed to return a result set from a function

    解決方法

    方法 1:改用 Stored Procedure

    函數不能回傳結果集,但 Stored Procedure 可以。

    錯誤的函數:

    DELIMITER //
    CREATE FUNCTION GetUsers()
    RETURNS TABLE
    BEGIN
        RETURN (SELECT * FROM users); -- 這是不允許的
    END //
    DELIMITER ;

    正確的 Stored Procedure:

    DELIMITER //
    CREATE PROCEDURE GetUsers()
    BEGIN
        SELECT * FROM users;
    END //
    DELIMITER ;

    呼叫 Stored Procedure:

    CALL GetUsers();

    方法 2:使用函數回傳單一值

    如果只需要回傳單一值(如計數或最大值),可以用 RETURN

    DELIMITER //
    CREATE FUNCTION GetUserCount()
    RETURNS INT DETERMINISTIC
    BEGIN
        DECLARE total INT;
        SELECT COUNT(*) INTO total FROM users;
        RETURN total;
    END //
    DELIMITER ;

    使用函數:

    SELECT GetUserCount();

    方法 3:使用 Temporary Table

    如果真的需要在函數內回傳多行結果,可以讓函數插入資料到 Temporary Table,然後在外部查詢。

    DELIMITER //
    CREATE FUNCTION PopulateTempUsers()
    RETURNS INT DETERMINISTIC
    BEGIN
        CREATE TEMPORARY TABLE IF NOT EXISTS temp_users AS (SELECT * FROM users);
        RETURN 1;
    END //
    DELIMITER ;

    使用 Temporary Table 讀取資料:

    SELECT PopulateTempUsers();
    SELECT * FROM temp_users;

    結論



    HeidiSQL

    簡介

    HeidiSQL 是一款免費的開源 SQL 客戶端,支援 MySQL、MariaDB、PostgreSQL 和 MS SQL Server,提供 GUI 來管理資料庫、執行 SQL 查詢、匯入/匯出資料等。

    主要功能

    下載與安裝

    1. 前往官方網站下載 HeidiSQL: https://www.heidisql.com/download.php
    2. 執行安裝程式並按照指示完成安裝
    3. 開啟 HeidiSQL,設定新連線

    連接 MySQL / MariaDB

    1. 開啟 HeidiSQL
    2. 點擊「新增」建立新連線
    3. 設定:
       - 主機名/IP:127.0.0.1 或 遠端伺服器 IP
       - 使用者名稱:root 或其他使用者
       - 密碼:對應的密碼
       - 連接埠:3306(MySQL / MariaDB)
    4. 點擊「開啟」連接資料庫

    執行 SQL 查詢

    在 HeidiSQL 查詢視窗中輸入 SQL 語句:

    SELECT * FROM users WHERE status = 'active';
    

    點擊「執行」按鈕,即可查看結果。

    匯入 / 匯出資料

    匯出 SQL

    1. 右鍵點擊資料庫 → 選擇「匯出 SQL」
    2. 選擇要匯出的資料表
    3. 設定匯出格式(.sql、.csv、.json)
    4. 點擊「匯出」

    匯入 SQL

    1. 打開 HeidiSQL,選擇目標資料庫
    2. 點擊「工具」→「執行 SQL 檔案」
    3. 選擇 .sql 檔案並執行

    管理使用者權限

    1. 進入「工具」→「管理使用者權限」
    2. 選擇要管理的使用者
    3. 設定資料庫權限(SELECT、INSERT、UPDATE、DELETE 等)
    4. 點擊「儲存」

    結論



    HeidiSQL 新增儲存程序

    步驟 1:連接資料庫

    1. 開啟 HeidiSQL。
    2. 連接 MySQL 或 MariaDB 伺服器。
    3. 在左側的「資料庫」列表中選擇目標資料庫。

    步驟 2:建立新儲存程序

    1. 在左側的資料庫名稱上點擊右鍵,選擇「建立新的」→「儲存程序」。
    2. HeidiSQL 會打開一個新的 SQL 編輯視窗,並提供預設的儲存程序模板。

    步驟 3:編寫儲存程序

    以下是一個簡單的範例,返回 users 表中的所有資料:

    DELIMITER //
    CREATE PROCEDURE GetAllUsers()
    BEGIN
        SELECT * FROM users;
    END //
    DELIMITER ;
    

    步驟 4:執行儲存程序

    1. 點擊「執行」按鈕(綠色閃電)。
    2. 若執行成功,可在左側的「儲存程序」欄位中找到該程序。

    步驟 5:測試儲存程序

    CALL GetAllUsers();
    

    進階:帶參數的儲存程序

    傳入參數來篩選數據,例如根據用戶 ID 查詢:

    DELIMITER //
    CREATE PROCEDURE GetUserById(IN user_id INT)
    BEGIN
        SELECT * FROM users WHERE id = user_id;
    END //
    DELIMITER ;
    

    呼叫帶參數的儲存程序:

    CALL GetUserById(1);
    

    刪除儲存程序

    DROP PROCEDURE IF EXISTS GetAllUsers;
    

    結論



    多媒體

    什麼是多媒體?

    多媒體是指同時使用多種媒介(如文字、圖像、音頻、視頻和動畫)來傳達信息和內容的技術。它提供了一種豐富的方式來呈現和交流信息,並在教育、娛樂和廣告等領域得到了廣泛應用。

    多媒體的組成要素

    多媒體的應用領域

    多媒體技術的發展

    隨著技術的進步,多媒體技術也在不斷演變。從早期的靜態圖像和音頻到現在的高解析度視頻和虛擬實境(VR),多媒體的表達方式變得越來越豐富和多樣。

    結論

    多媒體不僅提高了信息傳遞的效率和趣味性,還為用戶創造了更為沉浸式的體驗。未來,隨著技術的進一步發展,多媒體將在更多領域中發揮更大的作用。



    OpenCV

    1. OpenCV 是什麼?

    OpenCV (Open Source Computer Vision Library) 是一個開源的計算機視覺與機器學習軟件庫,用於即時影像處理與分析。

    2. 支援的功能

    3. 支援的平台

    4. 使用範例

    # 讀取影像並顯示
    import cv2
    image = cv2.imread("image.jpg")
    cv2.imshow("Image", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    5. 資源與文件



    Halcon

    特點

    Halcon 是由 MVTec 公司開發的一款強大的工業視覺軟體,專為影像處理和機器視覺應用而設計。

    功能

    應用領域

    資源



    開源影片編輯軟體

    1. Shotcut

    Shotcut 是一款免費且開源的影片編輯軟體,支援多種格式且具有許多強大的編輯工具。其特色包括:

    適用平台:Windows、Mac、Linux

    2. OpenShot

    OpenShot 是一款易於上手的開源影片編輯工具,功能強大且支持多種格式。其主要特點包括:

    適用平台:Windows、Mac、Linux

    3. Blender

    Blender 是一款知名的開源 3D 建模和動畫軟體,內建功能強大的影片編輯器,適合進行影片剪輯和特效製作。其功能包括:

    適用平台:Windows、Mac、Linux

    4. Kdenlive

    Kdenlive 是 Linux 上廣泛使用的開源影片編輯軟體,也支援 Windows。其主要功能包括:

    適用平台:Windows、Mac、Linux

    5. Lightworks

    Lightworks 提供免費和付費版本,免費版本具備基本編輯功能。其特色包括:

    適用平台:Windows、Mac、Linux

    . Avidemux
    . Cinelerra
    . LiVES
    . Losslesscut
    . Natron
    . Pitivi

    以上這些開源影片編輯軟體提供了強大的功能,適合不同層次的影片編輯需求,從簡單的家庭影片剪輯到專業級的影片製作都可以滿足。

    Google 搜尋量排名

    軟體名稱 大約搜尋量
    OpenShot 110,000
    Kdenlive 90,500
    Shotcut 49,500
    Avidemux 18,100
    Losslesscut 14,800
    Blender VSE 10,000
    Natron 6,600
    Cinelerra 5,400
    Pitivi 3,600
    LiVES 1,600


    OpenShot/openshot-qt - GitHub 專案

    專案簡介

    OpenShot 是一款免費且開源的影片編輯器,專案名稱為 OpenShot/openshot-qt,主要基於 PythonQt 開發。該專案旨在提供一個易於使用且功能豐富的影片編輯工具,適合不同水平的使用者。

    功能特色

    技術架構

    OpenShot 使用 PyQt 作為圖形用戶界面,並結合 libopenshot (C++ 實現) 來處理影片編輯的核心邏輯。此外,OpenShot 還利用了 FFmpeg 來支援多種格式的解碼與編碼。

    使用情境

    OpenShot 適用於需要簡單操作、但功能強大的影片編輯需求的用戶。無論是業餘影片創作者還是教育用途,OpenShot 都提供了靈活的工具和插件,便於進行剪輯和創作。

    社群和貢獻

    OpenShot 專案擁有活躍的開源社群,使用者和開發者可以透過 GitHub 貢獻程式碼、報告問題或提交新功能建議。歡迎所有人參與,以協助提升 OpenShot 的功能與穩定性。

    如何取得 OpenShot

    使用者可以透過 GitHub 頁面下載源碼,或從 OpenShot 官方網站下載可執行檔。詳細安裝指引和說明文件也可在 GitHub 上找到。



    Unity

    Unity 是一個功能強大的遊戲開發引擎和平臺,專門設計用於創建 2D 和 3D 遊戲、互動應用程式及虛擬現實 (VR) 和增強現實 (AR) 體驗。它提供簡單易用的介面和豐富的工具,適合初學者和專業開發人員使用。

    1. Unity 的主要特點
    2. Unity 的核心組件
    3. Unity 的應用範疇
    4. Unity 的優勢

    Unity 是一個強大且靈活的開發引擎,為開發者提供了廣泛的應用場景和工具支持。無論是初學者還是專業開發人員,都可以利用 Unity 快速創建高質量的 2D、3D 遊戲及互動應用。



    Android 開發

    Android 開發平台

    平台簡介

    Android 是一個由 Google 開發的開放原始碼行動作業系統,廣泛應用於智慧型手機、平板電腦和其他智慧裝置。開發者可利用其豐富的 API 和工具創建多樣化的應用程式。

    開發工具

  • Android Studio:Google 官方提供的整合開發環境 (IDE),支援程式碼編輯、模擬器測試及除錯功能。
  • Kotlin 和 Java:Android 開發主要使用的程式語言,其中 Kotlin 是官方推薦語言。
  • Android SDK:提供模擬器、API 庫及工具集,支援應用程式的開發與測試。
  • Gradle:用於管理相依性及建置自動化的工具。

    應用程式生命週期

  • 啟動 (onCreate):應用程式啟動並初始化資源。
  • 運行 (onStart):畫面顯示給使用者。
  • 活動 (onResume):應用程式進入互動狀態。
  • 暫停 (onPause):應用程式部分隱藏或失去焦點。
  • 停止 (onStop):完全隱藏,可能被系統回收。
  • 銷毀 (onDestroy):應用程式被關閉並釋放所有資源。

    優勢

  • 開放性:支援自訂與廣泛的裝置相容性。
  • 市場規模:Google Play 提供全球範圍的應用程式發佈機會。
  • 強大工具:豐富的開發工具和文件資源,有助於開發效率提升。

    學習資源

  • 官方文件:Android Developers 提供詳細的文件與範例。
  • 開發社群:如 Stack Overflow、Reddit 等平臺提供技術支援。
  • 線上課程:可選擇 Udemy、Coursera 或 YouTube 的相關課程。

    結語

    Android 開發平台以其靈活性和強大功能吸引了眾多開發者,成為行動應用程式開發的首選之一。

    Android App 範例

    建立新專案

    在 Android Studio 中建立一個新的專案,選擇 "Empty Activity" 模板,然後設定專案名稱及其他基本資訊。

    編寫介面 (activity_main.xml)

    res/layout/activity_main.xml 檔案中設計簡單的使用者介面,例如包含一個按鈕和一個文字顯示區域:

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:gravity="center">
            
                <Button
                    android:id="@+id/button"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="點我" />
            
                <TextView
                    android:id="@+id/textView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Hello, World!"
                    android:layout_marginTop="20dp" />
            
            </LinearLayout>
    

    編寫程式邏輯 (MainActivity.java)

    MainActivity.java 中,設定按鈕的點擊事件,使其更改文字顯示區域的內容:

            package com.example.simpleapp;
    
            import android.os.Bundle;
            import android.view.View;
            import android.widget.Button;
            import android.widget.TextView;
            import androidx.appcompat.app.AppCompatActivity;
    
            public class MainActivity extends AppCompatActivity {
                @Override
                protected void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.activity_main);
    
                    Button button = findViewById(R.id.button);
                    TextView textView = findViewById(R.id.textView);
    
                    button.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            textView.setText("你點了按鈕!");
                        }
                    });
                }
            }
        

    執行應用程式

    在 Android Studio 中點擊執行按鈕,即可在模擬器或連接的實體裝置上測試應用程式。點擊按鈕後,文字將更改為 "你點了按鈕!"。



    iOS開發

    開發工具

    iOS 開發主要使用 Xcode,這是 Apple 提供的官方整合開發環境 (IDE)。

    開發流程

    1. 設計應用程式架構與介面
    2. 編寫程式碼實現功能
    3. 使用模擬器或實機進行測試
    4. 進行錯誤排除與性能優化
    5. 通過 App Store 提交與發佈

    必備知識

    學習 iOS 開發需要掌握以下基礎:

    開發資源

    以下是一些實用的學習與開發資源:



    Xcode

    功能特色

    Xcode 是 Apple 提供的整合開發環境 (IDE),用於 macOS、iOS、watchOS 和 tvOS 應用程式的開發。

    主要組件

    1. Code Editor: 提供語法高亮、代碼補全與錯誤提示功能
    2. Interface Builder: 支援直覺式拖放設計介面
    3. Simulator: 用於測試與模擬應用程式在不同裝置上的運行
    4. Debugging 工具: 支援斷點、記憶體檢測與性能分析

    安裝與更新

    可從 Mac App Store 或 Apple 開發者官網下載最新版本的 Xcode。

    使用技巧

    提升開發效率的實用技巧:

    資源

    相關學習與參考資源:



    Swift

    語言特點

    Swift 是 Apple 推出的現代化程式語言,用於開發 iOS、macOS、watchOS 和 tvOS 應用程式。

    語法基礎

    核心概念

    1. 使用 Protocol 實現介面與協議
    2. 利用 Extension 擴展類別功能
    3. 泛型支援提升代碼重用性
    4. 運用閉包 (Closures) 實現高階函數
    5. 支援自定義操作符與元組

    應用場景

    Swift 不僅適用於 Apple 生態系統,還可以用於伺服器端開發與跨平台工具。

    資源

    相關學習與參考資源:



    Objective-C

    特點

    Objective-C 是一種以 C 為基礎的物件導向程式語言,最初由 NeXT 公司開發,後來被 Apple 廣泛用於 macOS 和 iOS 應用程式開發。

    語法結構

    Objective-C 的語法結合了 C 和 Smalltalk 的特性,使用 @ 符號來標示語言擴展。

    核心概念

    1. 物件導向程式設計,包括類別與繼承
    2. 協議 (Protocols) 用於定義方法集合
    3. 分類 (Categories) 用於擴展類別功能
    4. Block 語法用於閉包與回呼

    開發工具

    Objective-C 的開發主要使用 Apple 的 Xcode。

    資源

    以下是一些學習與參考的資源:



    GNews

    什麼是GNews?

    GNews是一個由Google開發的新聞聚合平台,旨在幫助用戶獲取最新的全球新聞資訊。它整合了來自各種新聞來源的內容,使用人工智慧技術來個性化推薦用戶感興趣的新聞。

    GNews的主要功能

    如何使用GNews?

    用戶可以通過訪問GNews的網站或下載其應用程式來使用此平台。在平台上,用戶可以選擇感興趣的主題、追蹤特定的新聞來源,並根據自己的需求自訂新聞推送。

    GNews的優勢

    結論

    GNews是一個強大的新聞聚合工具,透過人工智慧技術,為用戶提供個性化的新聞體驗。隨著新聞資訊的快速變化,GNews幫助用戶快速跟上世界動態,獲取所需的資訊。



    AI開發程式比較

    Bolt

    Bolt 是一個快速、輕量的 AI 開發框架,專注於提供開發者簡單且高效的工具來建構應用程式。特點包括:

    Cursor

    Cursor 是專為 AI 程式開發設計的編輯器工具,提供智能輔助程式碼撰寫與調試功能。特點包括:

    v0

    v0 是一個基於視覺化開發的 AI 平台,允許使用者透過拖放介面構建模型與應用。特點包括:

    Codeium

    Codeium 是一款結合 AI 智能輔助的程式編輯器,專注於提升程式開發效率與準確性。特點包括:



    敏捷開發

    核心價值

    主要原則

    1. 以最高優先級滿足客戶需求,通過早期且持續的交付提供價值。
    2. 歡迎需求變更,即使在開發後期,也能利用變更為客戶創造競爭優勢。
    3. 頻繁交付可工作的軟體,交付周期通常以數周或數月計算。
    4. 開發與業務團隊應該每天合作,促進溝通與理解。
    5. 以有動機的個人為核心,提供支持並信任他們完成工作。
    6. 面對面交流是最有效的溝通方式。
    7. 衡量進度的主要指標是可工作的軟體。
    8. 提倡可持續的開發節奏,所有參與者應能維持穩定的步調。
    9. 追求技術卓越與設計簡潔,提升靈活性與質量。
    10. 保持簡單,專注於完成必要的工作。
    11. 自組織團隊能產生最佳設計與架構。
    12. 定期反思並調整行為以提升效率。

    常用框架

    適用場景




    email: [email protected]
    
    T:0000
    資訊與搜尋 電話: 02-27566655 ,03-5924828 email: [email protected]