cpp

Encapsulation

02_Encapsulation⚙️
/**
 * ============================================================
 * C++ ENCAPSULATION
 * ============================================================
 * 
 * This file covers:
 * - What is encapsulation
 * - Data hiding
 * - Getter and setter methods
 * - Validation in setters
 * - Benefits of encapsulation
 * 
 * Compile: g++ -std=c++17 -Wall 01_encapsulation.cpp -o encapsulation
 * Run: ./encapsulation
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// ============================================================
// BAD EXAMPLE: NO ENCAPSULATION
// ============================================================

struct BadDate {
    int day;
    int month;
    int year;
    
    // Problems:
    // - Can set invalid values: day = 50, month = 15
    // - No validation
    // - Hard to change internal representation
};

// ============================================================
// GOOD EXAMPLE: PROPER ENCAPSULATION
// ============================================================

class Date {
private:
    int day;
    int month;
    int year;
    
    // Private helper method
    bool isValidDate(int d, int m, int y) const {
        if (y < 1 || m < 1 || m > 12 || d < 1) return false;
        
        int daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        
        // Leap year check
        if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) {
            daysInMonth[2] = 29;
        }
        
        return d <= daysInMonth[m];
    }
    
public:
    // Constructor with validation
    Date(int d = 1, int m = 1, int y = 2000) {
        if (isValidDate(d, m, y)) {
            day = d;
            month = m;
            year = y;
        } else {
            // Set to default valid date
            day = 1;
            month = 1;
            year = 2000;
            cout << "Invalid date, set to default 01/01/2000" << endl;
        }
    }
    
    // Getters
    int getDay() const { return day; }
    int getMonth() const { return month; }
    int getYear() const { return year; }
    
    // Setters with validation
    bool setDay(int d) {
        if (isValidDate(d, month, year)) {
            day = d;
            return true;
        }
        return false;
    }
    
    bool setMonth(int m) {
        if (isValidDate(day, m, year)) {
            month = m;
            return true;
        }
        return false;
    }
    
    bool setYear(int y) {
        if (isValidDate(day, month, y)) {
            year = y;
            return true;
        }
        return false;
    }
    
    bool setDate(int d, int m, int y) {
        if (isValidDate(d, m, y)) {
            day = d;
            month = m;
            year = y;
            return true;
        }
        return false;
    }
    
    // Display
    void display() const {
        cout << (day < 10 ? "0" : "") << day << "/"
             << (month < 10 ? "0" : "") << month << "/"
             << year;
    }
};

// ============================================================
// EXAMPLE: EMPLOYEE CLASS WITH ENCAPSULATION
// ============================================================

class Employee {
private:
    int id;
    string name;
    double salary;
    string department;
    
    static int nextId;  // Auto-increment ID
    
public:
    // Constructor
    Employee(const string& name, double salary, const string& dept)
        : id(nextId++), name(name), salary(salary > 0 ? salary : 0), department(dept) {}
    
    // Getters
    int getId() const { return id; }
    string getName() const { return name; }
    double getSalary() const { return salary; }
    string getDepartment() const { return department; }
    
    // Setters with validation
    void setName(const string& newName) {
        if (!newName.empty()) {
            name = newName;
        }
    }
    
    bool setSalary(double newSalary) {
        if (newSalary >= 0) {
            salary = newSalary;
            return true;
        }
        return false;
    }
    
    void setDepartment(const string& dept) {
        department = dept;
    }
    
    // Business methods
    void giveRaise(double percentage) {
        if (percentage > 0 && percentage <= 100) {
            salary += salary * (percentage / 100);
        }
    }
    
    void display() const {
        cout << "ID: " << id << endl;
        cout << "Name: " << name << endl;
        cout << "Department: " << department << endl;
        cout << "Salary: $" << salary << endl;
    }
};

int Employee::nextId = 1001;

// ============================================================
// EXAMPLE: TEMPERATURE CLASS WITH UNIT CONVERSION
// ============================================================

class Temperature {
private:
    double kelvin;  // Store internally in Kelvin
    
    // Conversion helpers
    static double celsiusToKelvin(double c) { return c + 273.15; }
    static double fahrenheitToKelvin(double f) { return (f - 32) * 5/9 + 273.15; }
    static double kelvinToCelsius(double k) { return k - 273.15; }
    static double kelvinToFahrenheit(double k) { return (k - 273.15) * 9/5 + 32; }
    
public:
    // Constructors for different units
    static Temperature fromCelsius(double c) {
        Temperature t;
        t.kelvin = celsiusToKelvin(c);
        return t;
    }
    
    static Temperature fromFahrenheit(double f) {
        Temperature t;
        t.kelvin = fahrenheitToKelvin(f);
        return t;
    }
    
    static Temperature fromKelvin(double k) {
        Temperature t;
        t.kelvin = k;
        return t;
    }
    
    // Getters in different units
    double getCelsius() const { return kelvinToCelsius(kelvin); }
    double getFahrenheit() const { return kelvinToFahrenheit(kelvin); }
    double getKelvin() const { return kelvin; }
    
    // Setters in different units
    void setCelsius(double c) { kelvin = celsiusToKelvin(c); }
    void setFahrenheit(double f) { kelvin = fahrenheitToKelvin(f); }
    void setKelvin(double k) { kelvin = k; }
    
    // Display all units
    void displayAll() const {
        cout << getCelsius() << "°C = "
             << getFahrenheit() << "°F = "
             << getKelvin() << "K" << endl;
    }
    
private:
    Temperature() : kelvin(0) {}  // Private default constructor
};

// ============================================================
// EXAMPLE: BANK ACCOUNT WITH TRANSACTION LOGGING
// ============================================================

class SecureBankAccount {
private:
    string accountNumber;
    string ownerName;
    double balance;
    string pin;
    vector<string> transactionHistory;
    bool locked;
    int failedAttempts;
    
    void log(const string& message) {
        transactionHistory.push_back(message);
    }
    
public:
    SecureBankAccount(const string& accNum, const string& owner, const string& pinCode)
        : accountNumber(accNum), ownerName(owner), balance(0), 
          pin(pinCode), locked(false), failedAttempts(0) {
        log("Account created");
    }
    
    // Getters (no setter for account number - immutable)
    string getAccountNumber() const { return accountNumber; }
    string getOwnerName() const { return ownerName; }
    double getBalance() const { return balance; }
    bool isLocked() const { return locked; }
    
    // Operations with PIN verification
    bool deposit(double amount, const string& inputPin) {
        if (locked) {
            cout << "Account is locked!" << endl;
            return false;
        }
        
        if (inputPin != pin) {
            failedAttempts++;
            log("Failed deposit attempt - wrong PIN");
            if (failedAttempts >= 3) {
                locked = true;
                log("Account locked due to too many failed attempts");
            }
            return false;
        }
        
        failedAttempts = 0;
        
        if (amount > 0) {
            balance += amount;
            log("Deposited: $" + to_string(amount));
            return true;
        }
        return false;
    }
    
    bool withdraw(double amount, const string& inputPin) {
        if (locked) {
            cout << "Account is locked!" << endl;
            return false;
        }
        
        if (inputPin != pin) {
            failedAttempts++;
            log("Failed withdrawal attempt - wrong PIN");
            if (failedAttempts >= 3) {
                locked = true;
                log("Account locked due to too many failed attempts");
            }
            return false;
        }
        
        failedAttempts = 0;
        
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            log("Withdrawn: $" + to_string(amount));
            return true;
        }
        return false;
    }
    
    bool changePin(const string& oldPin, const string& newPin) {
        if (oldPin != pin) {
            log("Failed PIN change attempt");
            return false;
        }
        
        if (newPin.length() >= 4) {
            pin = newPin;
            log("PIN changed successfully");
            return true;
        }
        return false;
    }
    
    void printStatement() const {
        cout << "\n=== Account Statement ===" << endl;
        cout << "Account: " << accountNumber << endl;
        cout << "Owner: " << ownerName << endl;
        cout << "Balance: $" << balance << endl;
        cout << "\nTransaction History:" << endl;
        for (const auto& entry : transactionHistory) {
            cout << "  - " << entry << endl;
        }
    }
};

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ ENCAPSULATION" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Bad vs Good Encapsulation
    // ========================================================
    
    cout << "--- DEMO 1: BAD VS GOOD ENCAPSULATION ---" << endl << endl;
    
    cout << "Bad (no encapsulation):" << endl;
    BadDate badDate;
    badDate.day = 50;   // Invalid but allowed!
    badDate.month = 15; // Invalid but allowed!
    cout << "  BadDate: " << badDate.day << "/" << badDate.month << "/" << badDate.year << endl;
    
    cout << "\nGood (with encapsulation):" << endl;
    Date goodDate(50, 15, 2024);  // Invalid values rejected
    cout << "  GoodDate: ";
    goodDate.display();
    cout << endl;
    
    cout << "\nSetting valid date:" << endl;
    goodDate.setDate(15, 8, 2024);
    cout << "  GoodDate: ";
    goodDate.display();
    cout << endl;
    
    cout << "\nTrying to set invalid day:" << endl;
    bool success = goodDate.setDay(35);
    cout << "  setDay(35) returned: " << (success ? "true" : "false") << endl;
    cout << "  Date unchanged: ";
    goodDate.display();
    cout << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 2: Employee with Auto-ID
    // ========================================================
    
    cout << "--- DEMO 2: EMPLOYEE ENCAPSULATION ---" << endl << endl;
    
    Employee emp1("Alice Smith", 50000, "Engineering");
    Employee emp2("Bob Jones", 45000, "Marketing");
    Employee emp3("Charlie Brown", -1000, "Sales");  // Negative salary
    
    cout << "Employee 1:" << endl;
    emp1.display();
    
    cout << "\nEmployee 2:" << endl;
    emp2.display();
    
    cout << "\nEmployee 3 (tried negative salary):" << endl;
    emp3.display();
    
    cout << "\nGiving 10% raise to Employee 1:" << endl;
    emp1.giveRaise(10);
    cout << "New salary: $" << emp1.getSalary() << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 3: Temperature Conversion
    // ========================================================
    
    cout << "--- DEMO 3: TEMPERATURE (INTERNAL REPRESENTATION) ---" << endl << endl;
    
    cout << "Creating temperature from 100°C:" << endl;
    Temperature temp = Temperature::fromCelsius(100);
    temp.displayAll();
    
    cout << "\nCreating temperature from 32°F:" << endl;
    temp = Temperature::fromFahrenheit(32);
    temp.displayAll();
    
    cout << "\nNote: Internal storage is in Kelvin, but user" << endl;
    cout << "      can work with any unit transparently!" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 4: Secure Bank Account
    // ========================================================
    
    cout << "--- DEMO 4: SECURE BANK ACCOUNT ---" << endl << endl;
    
    SecureBankAccount account("ACC-12345", "John Doe", "1234");
    
    cout << "Depositing $1000 with correct PIN:" << endl;
    account.deposit(1000, "1234");
    cout << "Balance: $" << account.getBalance() << endl;
    
    cout << "\nWithdrawing $300 with correct PIN:" << endl;
    account.withdraw(300, "1234");
    cout << "Balance: $" << account.getBalance() << endl;
    
    cout << "\nTrying to withdraw with wrong PIN (3 times):" << endl;
    account.withdraw(100, "0000");
    account.withdraw(100, "0000");
    account.withdraw(100, "0000");  // Account gets locked
    
    cout << "\nAccount locked: " << (account.isLocked() ? "Yes" : "No") << endl;
    
    account.printStatement();
    
    cout << endl;

    // ========================================================
    // ENCAPSULATION BENEFITS
    // ========================================================
    
    cout << "--- ENCAPSULATION BENEFITS ---" << endl << endl;
    
    cout << "1. DATA HIDING" << endl;
    cout << "   Internal representation hidden from users" << endl;
    cout << "   Can change implementation without affecting users" << endl;
    cout << endl;
    
    cout << "2. VALIDATION" << endl;
    cout << "   Setters can validate data before accepting" << endl;
    cout << "   Maintains object invariants" << endl;
    cout << endl;
    
    cout << "3. CONTROL" << endl;
    cout << "   Control how data is accessed and modified" << endl;
    cout << "   Can add logging, security checks, etc." << endl;
    cout << endl;
    
    cout << "4. ABSTRACTION" << endl;
    cout << "   Users work with high-level interface" << endl;
    cout << "   Complexity hidden behind simple methods" << endl;
    cout << endl;
    
    cout << "5. MAINTAINABILITY" << endl;
    cout << "   Easy to modify internal logic" << endl;
    cout << "   Less impact on dependent code" << endl;

    cout << endl;

    cout << "============================================" << endl;
    cout << "ENCAPSULATION SUMMARY:" << endl;
    cout << "============================================" << endl;
    cout << "• Hide data with private members" << endl;
    cout << "• Expose through public methods" << endl;
    cout << "• Validate in setters" << endl;
    cout << "• Getters for read access" << endl;
    cout << "• Keep internals flexible" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Create an 'Email' class that:
 *    - Validates email format in setter
 *    - Stores domain separately (derived from email)
 *    - Masks email for display (j***@example.com)
 * 
 * 2. Create a 'Password' class that:
 *    - Never exposes the actual password
 *    - Validates strength (min length, etc.)
 *    - Stores hashed version internally
 *    - Has verify() method
 * 
 * 3. Create a 'Rectangle' class that:
 *    - Ensures width and height are positive
 *    - Maintains area and perimeter automatically
 *    - Has scale() method
 * 
 * 4. Create a 'GradeBook' class that:
 *    - Stores grades privately
 *    - Validates grades (0-100)
 *    - Calculates average, min, max
 *    - Cannot modify grades after adding
 */
Encapsulation - C++ Tutorial | DeepML