အခန်း ၈ :: Fundamental Principles of Software Engineering


Software Engineering လောကတွင် နည်းပညာများ၊ Framework များ၊ Language များသည် နေ့စဉ်နှင့်အမျှ ပြောင်းလဲနေပါသည်။ မနေ့က ခေတ်စားခဲ့သော React Class Component သည် ယနေ့ Function Component ဖြစ်သွားသလို၊ မနေ့က Monolith သည် ယနေ့ Microservices ဖြစ်သွားနိုင်ပါသည်။ သို့သော် ပြောင်းလဲခြင်း မရှိသော၊ အချိန်ကာလတိုင်းတွင် မှန်ကန်နေသော အခြေခံ အုတ်မြစ်များ (Fundamental Principles) ရှိပါသည်။

ဤအခြေခံမူများသည် တိကျသော Programming Language တစ်ခုအပေါ်တွင် မူတည်ခြင်းမရှိဘဲ၊ ကောင်းမွန်သော Software Architecture တစ်ခု၊ Maintainable ဖြစ်သော Codebase တစ်ခု ဖြစ်လာစေရန် ထိန်းကျောင်းပေးသည့် "Software လောက၏ ရူပဗေဒ နိယာမများ" ဟု ဆိုနိုင်ပါသည်။

ဤအခန်းတွင် Software Engineer တစ်ယောက် မဖြစ်မနေ နားလည်ထားရမည့် အခြေခံမူများကို လေ့လာသွားပါမည်။

၈.၁ Separation of Concerns (SoC)

Software System တစ်ခုကို တည်ဆောက်ရာတွင် အရေးကြီးဆုံး အချက်မှာ "ရောထွေးမနေစေခြင်း" ဖြစ်သည်။ Separation of Concerns ဆိုသည်မှာ ပရိုဂရမ်တစ်ခုကို တာဝန်မတူညီသော အစိတ်အပိုင်း (Concern) များအဖြစ် သီးခြားစီ ခွဲထုတ်ခြင်း ဖြစ်သည်။

ဥပမာအားဖြင့် စားသောက်ဆိုင် တစ်ဆိုင်တွင် စားဖိုမှူး (ချက်ပြုတ်ရေး)၊ စားပွဲထိုး (ဧည့်ခံရေး) နှင့် ငွေကိုင် (ငွေစာရင်း) ဟူ၍ တာဝန်ခွဲခြားထားသကဲ့သို့ ဖြစ်သည်။ စားဖိုမှူးက ငွေထွက်ရှင်းနေလျှင် ထိုဆိုင် ရှုပ်ထွေးသွားပါလိမ့်မည်။

Software တွင်လည်း ထိုနည်းတူစွာ -

  1. Presentation Logic: User ကို Data ပြသခြင်း (UI)
  2. Business Logic: အဓိက လုပ်ငန်းစဥ်များ (Processing)
  3. Data Access Logic: Database တွင် သိမ်းဆည်းခြင်း (Storage)

ဟူ၍ ခွဲခြားထားသင့်သည်။ ဤအချက်သည် အခန်း ၅ မှာ ဖော်ပြထားသည့် Layered Architecture နှင့် MVC Pattern တို့၏ အဓိက သဘောတရား ဖြစ်သည်။

graph TD
    subgraph "Without SoC (Spaghetti Code)"
        A["<b>Monolithic / God Component</b><br><br>UI ရော၊ တွက်ချက်တာရော၊<br>Database ခေါ်တာရော<br>တစ်နေရာတည်း ရောရေးထားခြင်း"]
    end

    subgraph "With SoC (Clean Architecture)"
        B[UI Component<br>View] --> C[Business Logic Component<br>Service]
        C --> D[Data Access Component<br>Repository]
    end
    
    style A fill:#ffcccc,stroke:#333
    style B fill:#ccffcc,stroke:#333
    style C fill:#ccffcc,stroke:#333
    style D fill:#ccffcc,stroke:#333

SoC ကို လိုက်နာခြင်းဖြင့် UI ဒီဇိုင်း ပြောင်းချင်လျှင် Business Logic ကို သွားထိစရာ မလိုတော့သလို၊ Database ပြောင်းချင်လျှင်လည်း UI ပျက်သွားမည်ကို စိုးရိမ်စရာ မလိုတော့ပါ။

၈.၂ Information Hiding and Encapsulation

ဒီ Principle နှစ်ခုက ဆက်စပ်နေပေမယ့် ကွဲပြားမှု ရှိပါတယ်။ Coupling ကို လျှော့ချဖို့နဲ့ Maintainability ကို မြှင့်တင်ဖို့ အလွန် အရေးပါပါတယ်။

Information Hiding (သတင်းအချက်အလက် ဖုံးကွယ်ခြင်း)

Module တစ်ခုရဲ့ "အတွင်းပိုင်း ဘယ်လို အလုပ်လုပ်လဲ" (How) ဆိုတာကို အခြား Module တွေက မသိအောင် ဖုံးကွယ်ထားခြင်း ဖြစ်ပါတယ်။ အပြင်က လူက "ဘာလုပ်ပေးနိုင်လဲ" (What) ဆိုတာပဲ သိရပါမယ်။

ကားမောင်းခြင်း ဥပမာ: ကားမောင်းသူ (User) တစ်ယောက်အနေနဲ့ စတီယာရင်၊ လီဗာ၊ ဘရိတ် (Interface) ကိုပဲ သိဖို့ လိုပါတယ်။ အင်ဂျင်ထဲမှာ ဆီဘယ်လို ဖြန်းတယ်၊ မီးဘယ်လို ပွင့်တယ် (Implementation Detail) ဆိုတာကို သိစရာ မလိုပါဘူး။ ဒါမှသာ ကားထုတ်လုပ်သူက အင်ဂျင်စနစ်ကို ပြောင်းလဲလိုက်ရင်တောင် မောင်းသူအတွက် ဘာမှ အပြောင်းအလဲ မရှိမှာ ဖြစ်ပါတယ်။

Encapsulation (အလုံပိတ်ခြင်း)

ဒါကတော့ Information Hiding ကို လက်တွေ့ အကောင်အထည်ဖော်တဲ့ နည်းလမ်း ဖြစ်ပါတယ်။ အခန်း ၁ OOP မှာ ဆွေးနွေးခဲ့သလိုပါပဲ။ Data တွေနဲ့ အဲ့ဒီ Data တွေကို ကိုင်တွယ်မယ့် Method တွေကို Class တစ်ခုထဲမှာ ပေါင်းထည့်ပြီး၊ အပြင်ကနေ တိုက်ရိုက် ယူသုံးလို့ မရအောင် private access modifier တွေနဲ့ ပိတ်ထားတာ ဖြစ်ပါတယ်။

// Encapsulation Example
class BankAccount {
    private balance: number = 0; // Data ကို ဖုံးကွယ်ထားသည်

    // အပြင်လူ သုံးဖို့ Interface ဖွင့်ပေးထားသည်
    public deposit(amount: number): void {
        if (amount > 0) {
            this.balance += amount;
        }
    }
}

၈.၃ Modularity and Composability

Software Engineering မှာ အကြီးမားဆုံး စိန်ခေါ်မှုက Complexity (ရှုပ်ထွေးမှု) ပါ။ ဒီရှုပ်ထွေးမှုကို ဖြေရှင်းဖို့ Modularity ကို သုံးရပါတယ်။

ဒါကို အမြင်သာဆုံး ဥပမာ ပေးရရင် LEGO တုံးတွေ လိုပါပဲ။ LEGO တုံးလေးတွေက သီးခြားစီ ရှိနေပေမယ့်၊ ပြန်လည် ပေါင်းစပ်လိုက်ရင် ကားဖြစ်သွားလိုက်၊ အိမ်ဖြစ်သွားလိုက်နဲ့ ပုံစံမျိုးစုံ ဖန်တီးနိုင်သလို Software Components တွေကိုလည်း Reusable ဖြစ်အောင် ဖန်တီးထားရပါမယ်။

graph TD
    subgraph "Composable System"
        direction TB
        Auth[Auth Module]
        Log[Logging Module]
        Email[Email Module]
        Noti[Notification Module]
    end

    subgraph "App 1: Web Application"
        direction LR
        W_App[Web App] --> Auth
        W_App --> Log
        W_App --> Email
    end

    subgraph "App 2: Mobile Application"
        direction LR
        M_App[Mobile App] --> Auth
        M_App --> Noti
    end
    
    style Auth fill:#bbdefb,stroke:#333
    style Log fill:#bbdefb,stroke:#333

၈.၄ Abstraction and Generalization

ဒီနှစ်ခုက ရှုပ်ထွေးမှုကို လျှော့ချပေးတဲ့ ဉာဏ်စဉ်တွေ ဖြစ်ပါတယ်။

၈.၅ Anticipation of Change (အပြောင်းအလဲကို ကြိုတင်မျှော်မှန်းခြင်း)

Software Engineer ကောင်းတစ်ယောက်ဟာ "Change is the only constant" (အပြောင်းအလဲ ဆိုတာ မပြောင်းလဲသော တစ်ခုတည်းသော တရား) ဆိုတာကို မှတ်ထားရပါမည်။

Requirement တွေ ပြောင်းမယ်။ နည်းပညာတွေ ပြောင်းမယ်။ Business Logic တွေ ပြောင်းမယ်။ ဒီအပြောင်းအလဲတွေ လာတဲ့အခါ System တစ်ခုလုံး ဖြိုဖျက်ပြီး ပြန်ဆောက်ရတာမျိုး မဖြစ်ရအောင် ကြိုတင် ပြင်ဆင်ထားရပါမယ်။

လိုက်နာရမည့် နည်းလမ်းများ:

  1. Configuration: တန်ဖိုးတွေကို Code ထဲမှာ Hard-code မလုပ်ပါနဲ့။ Config file (.env, .json) တွေမှာ ထားပါ။
  2. Interfaces: Concrete Class တွေကို တိုက်ရိုက် မချိတ်ဆက်ပါနဲ့။ Interface တွေကို ကြားခံပြီး ချိတ်ဆက်ပါ။ (အခန်း ၅ တွင် ဖော်ပြခဲ့သည့် Dependency Inversion Principle ဖြစ်ပါသည်)။
  3. Low Coupling: Module တစ်ခုနဲ့ တစ်ခု အမှီအခို နည်းနိုင်သမျှ နည်းအောင် တည်ဆောက်ပါ။

၈.၆ Rigor and Formality (တိကျသေချာမှုနှင့် စနစ်ကျမှု)

Coding ဆိုတာ "ပြီးစလွယ်" လုပ်ရမယ့် အလုပ် မဟုတ်ပါဘူး။ Rigor ဆိုတာ လုပ်ငန်းစဉ်တွေကို စနစ်တကျ၊ တိတိကျကျ လိုက်နာဆောင်ရွက်တာကို ဆိုလိုပါတယ်။

ဒါပေမယ့် ဘယ်လောက် တင်းကျပ်မလဲ ဆိုတာကတော့ Project အမျိုးအစားပေါ် မူတည်ပါတယ်။

Engineer တစ်ယောက်အနေနဲ့ ကိုယ့် Project က ဘယ် Level မှာ ရှိလဲ ဆိုတာကို ဆုံးဖြတ်ပြီး လိုအပ်သလောက် Rigor ကို ထည့်သွင်းရပါမယ်။

၈.၇ Incremental Development

အခန်း ၂ (Process) နှင့် အခန်း ၃ (Agile) တို့တွင် ဆွေးနွေးခဲ့ပြီး ဖြစ်သကဲ့သို့၊ ကြီးမားသော စနစ်ကြီးတစ်ခုလုံးကို တစ်ခါတည်း တည်ဆောက်ခြင်း (Big Bang Approach) သည် ရှုံးနိမ့်နိုင်ခြေ (Risk) အလွန်များပါသည်။

ထို့ကြောင့် စနစ်ကို Increments (အတုံးအခဲ ငယ်လေးများ) အဖြစ် ပိုင်းခြားပြီး တစ်ခုပြီး တစ်ခု တည်ဆောက်ခြင်း၊ စမ်းသပ်ခြင်း၊ User Feedback ရယူခြင်းတို့ကို ပြုလုပ်ရပါမည်။

အကျိုးကျေးဇူးများ:

၈.၈ The Principle of Least Astonishment (POLA)

ဒါကတော့ User Experience (UX) နဲ့ API Design ပိုင်းမှာ အရမ်းအရေးကြီးတဲ့ Principle ပါ။ "မင်းရဲ့ Software ဟာ အသုံးပြုသူ မျှော်လင့်ထားတဲ့ အတိုင်းပဲ အလုပ်လုပ်သင့်တယ်။ သူတို့ကို အံ့အားသင့်အောင် (Surprise) မလုပ်သင့်ဘူး" လို့ ဆိုလိုတာပါ။

ဥပမာများ:

Code ရေးတဲ့အခါ "ငါ့ Code ကို နောက်လူ ဖတ်ရင် မျက်လုံးပြူးသွားမလား၊ ခေါင်းညိတ်မလား" ဆိုတာ အမြဲ စဉ်းစားပါ။ ရိုးရှင်းပြီး ခန့်မှန်းရ လွယ်ကူသော (Predictable) Code သည် အကောင်းဆုံး Code ဖြစ်ပါသည်။

Junior ဘဝ က ကိုယ်ရေးသည့် code တွေကို နောက်လူ မဖတ်တတ်အောင် ရေးသားခဲ့ဖူးပါတယ်။ တနည်းပြောရင် ကိုယ့် code တွေက အဆင့်မြင့်လို့ နောက်လူ နားမလည်ဘူး ထင်ရအောင်ပါ။ တကယ်တန်း အဆင့်မြင့်သည့် code တွေ က ဘယ်သူ ဖတ်ဖတ် နားလည် လွယ်ပါတယ်။ ဒါကြောင့် နောက်ပိုင်းမှာ Single Level of Abstraction Principle (SLAP) ပုံစံ နဲ့ ရေးဖြစ်ပါတယ်။

Single Level of Abstraction Principle (SLAP)

SLAP ဆိုသည်မှာ Function တစ်ခု ရေးသားရာတွင် Code များကို တူညီသော Abstraction Level တစ်ခုတည်းတွင်သာ ရှိနေစေရန် ရေးသားခြင်း ဖြစ်သည်။ High-level Logic (စီးပွားရေး လုပ်ငန်းစဉ်) နှင့် Low-level Details (Code အသေးစိတ်) များကို ရောထွေး မရေးသင့်ပါ။

Bad Example (Mixed Levels of Abstraction)

ဒီ Code မှာ checkout လုပ်တဲ့ အဓိက အလုပ်နဲ့၊ Database connection ဖွင့်တာ၊ Email format စစ်တာတွေ ရောထွေးနေပါတယ်။ စာဖတ်သူ အနေနဲ့ အတက်အကျ ကြမ်းလွန်းပါတယ်။

function checkout(cart: Cart, user: User) {
    // Low-level detail (Checking email regex)
    if (!user.email.includes('@')) {
        throw new Error("Invalid Email");
    }

    // High-level logic
    const total = cart.items.reduce((sum, item) => sum + item.price, 0);

    // Low-level detail (Direct DB query)
    db.query("INSERT INTO orders ...", [total, user.id]);

    // Low-level detail (Email sending implementation)
    const transporter = nodemailer.createTransport({ ... });
    transporter.sendMail({ to: user.email, subject: "Order Placed" });
}

Good Example (SLAP Applied)

ဒီ Code မှာတော့ checkout function က မန်နေဂျာ တစ်ယောက်လိုပါပဲ။ အသေးစိတ် မလုပ်ပါဘူး။ သက်ဆိုင်ရာ Function တွေကို လှမ်းခေါ်ခိုင်း (Delegate) ရုံပဲ လုပ်ပါတယ်။ ဖတ်လိုက်ရင် စာအုပ် ခေါင်းစဉ် ဖတ်ရသလို ရှင်းလင်း နေပါလိမ့်မယ်။

function checkout(cart: Cart, user: User) {
    validateUser(user);           // High-level
    const total = calculateTotal(cart); // High-level
    saveOrderToDatabase(total, user);   // High-level
    sendConfirmationEmail(user);        // High-level
}

// Low-level details တွေကို သီးသန့် Function တွေထဲမှာ ဝှက်ထားလိုက်ပါတယ်
function validateUser(user: User) {
    if (!user.email.includes('@')) throw new Error("Invalid Email");
}
// ... other functions

SLAP ကို လိုက်နာခြင်းအားဖြင့် Code သည် "အံ့အားသင့်စရာ" မရှိတော့ဘဲ၊ မျှော်လင့်ထားသည့် အတိုင်း အဆင့်ဆင့် (Step-by-step) ရှင်းလင်းစွာ အလုပ်လုပ်သွားကြောင်း တွေ့မြင်ရမည် ဖြစ်ပါသည်။

Summary

Software Engineering ဆိုသည်မှာ Code ရေးရုံ သက်သက် မဟုတ်ပါ။ အထက်ပါ အခြေခံ မူဝါဒများကို နားလည်ပြီး လက်တွေ့ အသုံးချနိုင်မှသာ "Coder" အဆင့်မှ "Engineer" အဆင့်သို့ ကူးပြောင်းနိုင်မည် ဖြစ်သည်။ Language တွေ၊ Tool တွေ ပြောင်းလဲသွားနိုင်ပေမယ့် Separation of Concerns, Modularity, Encapsulation စသည့် တန်ဖိုးတရားများကတော့ Software ရှိနေသမျှ ကာလပတ်လုံး တည်မြဲနေမည့် အမှန်တရားများ ဖြစ်ပါသည်။