Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

النظام المصرفي

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 85 من 87.

تطلب منك هذه المهمة بناء نظام بنكي يحاكي العمليات المالية في العالم الحقيقي باستخدام مبادئ البرمجة كائنية التوجه (OOP).

يجب أن يتعامل نظامك مع أنواع حسابات مختلفة مثل SavingsAccount و CheckingAccount، وكلاهما يرث من فئة Account الأساسية. لكل نوع حساب سلوكيات فريدة: فقد تكسب حسابات التوفير فوائد، بينما قد تحتوي الحسابات الجارية على حماية من السحب على المكشوف.

فكر في كيفية نمذجة المعاملات. واجهة مثل Transactable يمكن أن تحدد طرقاً لـ deposit() و withdraw(). فكر في استخدام استثناءات مخصصة مثل InsufficientFundsException لمعالجة العمليات غير الصالحة بشكل سلس.

قد تقوم فئة Bank بإدارة حسابات وعملاء متعددين، مما يوضح مفهوم التركيب (composition). يمكنك تطبيق نمط المصنع (Factory pattern) لإنشاء أنواع حسابات مختلفة، أو استخدام نمط المراقب (Observer pattern) لإخطار العملاء بتغييرات الحساب.

ركز على التغليف (encapsulation) لحماية البيانات الحساسة مثل الأرصدة وأرقام الحسابات، واستخدم تعدد الأشكال (polymorphism) لمعالجة أنواع الحسابات المختلفة بشكل موحد.

challenge icon

التحدي

سهل

لنقم ببناء نظام بنكي كامل يوضح إتقانك لمبادئ البرمجة كائنية التوجه (OOP)! ستقوم بإنشاء نظام بأنواع حسابات مختلفة، ومعالجة المعاملات، وإدارة الاستثناءات بشكل صحيح—كل ذلك منظم عبر ملفات متعددة.

ستقوم بتنظيم الكود الخاص بك عبر سبعة ملفات:

  • Transactable.java: أنشئ واجهة (interface) تحدد العقد للعمليات المالية. يجب أن تصرح عن الطرق deposit(double amount) و withdraw(double amount)، وكلاهما يعيد قيمة منطقية (boolean) للإشارة إلى النجاح.
  • InsufficientFundsException.java: أنشئ استثناءً مخصصاً يرث من Exception. يجب أن يقبل المنشئ (constructor) مبلغ السحب المحاول والرصيد الحالي، مع إنشاء رسالة بالتنسيق Insufficient funds: attempted [amount], available [balance].
  • Account.java: أنشئ فئة أساسية مجردة (abstract base class) تمثل أي حساب بنكي. قم بتضمين حقول خاصة لـ accountNumber (String) و balance (double)، ومنشئ يقوم بتهيئة كليهما، وطرق جلب (getters) مناسبة، وطريقة تعيين (setter) محمية للرصيد (حتى تتمكن الفئات الفرعية من تعديله)، وطريقة مجردة getAccountType() تعيد String. قم بتنفيذ طريقة getDetails() التي تعيد "[accountNumber] ([accountType]): $[balance]" مع تنسيق الرصيد إلى منزلتين عشريتين.
  • SavingsAccount.java: أنشئ فئة ترث من Account وتنفذ Transactable. أضف حقلاً خاصاً لـ interestRate (double). يأخذ المنشئ accountNumber، والرصيد الأولي، و interestRate. قم بتنفيذ getAccountType() لتعيد "Savings". بالنسبة لـ deposit()، أضف المبلغ إلى الرصيد وأعد true. بالنسبة لـ withdraw()، إذا تجاوز المبلغ الرصيد، أعد false؛ خلاف ذلك اطرح المبلغ وأعد true. أضف طريقة applyInterest() تزيد الرصيد بنسبة سعر الفائدة.
  • CheckingAccount.java: أنشئ فئة ترث من Account وتنفذ Transactable. أضف حقلاً خاصاً لـ overdraftLimit (double). يأخذ المنشئ accountNumber، والرصيد الأولي، و overdraftLimit. قم بتنفيذ getAccountType() لتعيد "Checking". بالنسبة لـ deposit()، أضف المبلغ وأعد true. بالنسبة لـ withdraw()، اسمح بالسحب إذا كان المبلغ أقل من أو يساوي الرصيد بالإضافة إلى overdraftLimit؛ وأعد false إذا تجاوز هذا الحد.
  • Bank.java: أنشئ فئة تدير الحسابات باستخدام ArrayList. قم بتضمين الطرق التالية:
    • addAccount(Account account) - يضيف حساباً إلى البنك
    • findAccount(String accountNumber) - يعيد الحساب أو null إذا لم يتم العثور عليه
    • transfer(String fromAccount, String toAccount, double amount) - يحول الأموال بين الحسابات، ويرمي InsufficientFundsException إذا كان الحساب المصدر لا يملك أموالاً كافية. عند النجاح، يعيد "Transferred $[amount] from [fromAccount] to [toAccount]"
    • getTotalDeposits() - يعيد مجموع أرصدة جميع الحسابات
  • Main.java: ابنِ نظامك البنكي! ستتلقى سلسلة نصية من الأوامر مفصولة بفاصلة:
    • SAVINGS:accountNumber:balance:interestRate
    • CHECKING:accountNumber:balance:overdraftLimit
    • DEPOSIT:accountNumber:amount
    • WITHDRAW:accountNumber:amount
    • TRANSFER:fromAccount:toAccount:amount
    • INTEREST:accountNumber

    قم بمعالجة كل أمر واطبع ما يلي:

    • SAVINGS/CHECKING: Account created: [accountNumber]
    • DEPOSIT: Deposited $[amount] to [accountNumber]
    • WITHDRAW: Withdrew $[amount] from [accountNumber] عند النجاح، أو Withdrawal failed: [accountNumber]
    • TRANSFER: اطبع رسالة النجاح أو Transfer failed: [exception message]
    • INTEREST: Interest applied to [accountNumber]

    بعد جميع الأوامر، اطبع --- Bank Summary --- متبوعاً بتفاصيل كل حساب (باستخدام getDetails()) وأخيراً Total deposits: $[total] منسقاً إلى منزلتين عشريتين.

ستتلقى مدخلاً واحداً: سلسلة أوامر مفصولة بفاصلة.

على سبيل المثال، مع المدخل SAVINGS:SAV001:1000.00:0.05,CHECKING:CHK001:500.00:200.00,DEPOSIT:SAV001:250.00,WITHDRAW:CHK001:600.00,TRANSFER:SAV001:CHK001:100.00,INTEREST:SAV001، سيكون مخرجك:

Account created: SAV001
Account created: CHK001
Deposited $250.00 to SAV001
Withdrew $600.00 from CHK001
Transferred $100.00 from SAV001 to CHK001
Interest applied to SAV001
--- Bank Summary ---
SAV001 (Savings): $1207.50
CHK001 (Checking): $0.00
Total deposits: $1207.50

لاحظ كيف سمح حساب الجاري (checking account) بسحب 600 دولار على الرغم من وجود رصيد 500 دولار فقط—هذه هي حماية السحب على المكشوف (overdraft protection) أثناء العمل! يربح حساب التوفير فائدة بنسبة 5% على رصيده بعد التحويل. يجمع هذا التحدي بين الوراثة، والواجهات، وتعدد الأشكال، والاستثناءات المخصصة، والتغليف في تطبيق بنكي واقعي!

ورقة مرجعية

يوضح النظام المصرفي مبادئ البرمجة كائنية التوجه (OOP) من خلال إدارة الحسابات، والمعاملات، ومعالجة الاستثناءات.

الواجهات للعقود (Interfaces for Contracts)

حدد العمليات المشتركة باستخدام الواجهات:

public interface Transactable {
    boolean deposit(double amount);
    boolean withdraw(double amount);
}

الاستثناءات المخصصة (Custom Exceptions)

قم بإنشاء استثناءات مخصصة للأخطاء الخاصة بالمجال:

public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(double attempted, double available) {
        super("Insufficient funds: attempted " + attempted + ", available " + available);
    }
}

الفئات الأساسية المجردة (Abstract Base Classes)

استخدم الفئات المجردة لتعريف هيكل مشترك مع طرق مجردة للسلوك الخاص بالفئات الفرعية:

public abstract class Account {
    private String accountNumber;
    private double balance;
    
    public abstract String getAccountType();
    
    protected void setBalance(double balance) {
        this.balance = balance;
    }
}

الوراثة وتعدد الأشكال (Inheritance and Polymorphism)

تقوم الفئات الفرعية بتوسيع الفئات الأساسية وتنفيذ الواجهات:

public class SavingsAccount extends Account implements Transactable {
    private double interestRate;
    
    @Override
    public String getAccountType() {
        return "Savings";
    }
    
    public void applyInterest() {
        setBalance(getBalance() * (1 + interestRate));
    }
}

التكوين (Composition)

يمكن للفئات إدارة مجموعات من الكائنات الأخرى:

public class Bank {
    private ArrayList<Account> accounts;
    
    public void addAccount(Account account) {
        accounts.add(account);
    }
    
    public Account findAccount(String accountNumber) {
        // البحث وإرجاع الحساب أو null
    }
}

التغليف (Encapsulation)

قم بحماية البيانات الحساسة باستخدام الحقول الخاصة (private fields) والوصول المتحكم فيه من خلال دوال الجلب والتعيين (getters/setters). استخدم معدلات protected للسماح بالوصول للفئات الفرعية مع الحفاظ على التغليف.

معالجة الاستثناءات في المعاملات

قم برمي استثناءات مخصصة للعمليات غير الصالحة:

public String transfer(String from, String to, double amount) throws InsufficientFundsException {
    if (!fromAccount.withdraw(amount)) {
        throw new InsufficientFundsException(amount, fromAccount.getBalance());
    }
    toAccount.deposit(amount);
    return "Transferred $" + amount;
}

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: إنشاء مثيل من Bank
        
        // TODO: تقسيم المدخلات بواسطة الفاصلة للحصول على الأوامر الفردية
        
        // TODO: معالجة كل أمر:
        // - SAVINGS:accountNumber:balance:interestRate -> إنشاء SavingsAccount، وطباعة "Account created: [accountNumber]"
        // - CHECKING:accountNumber:balance:overdraftLimit -> إنشاء CheckingAccount، وطباعة "Account created: [accountNumber]"
        // - DEPOSIT:accountNumber:amount -> إيداع وطباعة "Deposited $[amount] to [accountNumber]"
        // - WITHDRAW:accountNumber:amount -> سحب وطباعة النجاح أو "Withdrawal failed: [accountNumber]"
        // - TRANSFER:fromAccount:toAccount:amount -> تحويل وطباعة النجاح أو "Transfer failed: [exception message]"
        // - INTEREST:accountNumber -> تطبيق الفائدة وطباعة "Interest applied to [accountNumber]"
        
        // TODO: بعد جميع الأوامر، اطبع "--- Bank Summary ---"
        
        // TODO: طباعة تفاصيل كل حساب باستخدام getDetails()
        
        // TODO: طباعة "Total deposits: $[total]" بتنسيق منزلتين عشريتين
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming