النظام المصرفي
جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 85 من 87.
يتحداك هذا التمرين ببناء نظام مصرفي يحاكي العمليات المالية في العالم الحقيقي باستخدام مبادئ البرمجة كائنية التوجه (OOP).
ينبغي لنظامك التعامل مع أنواع مختلفة من الحسابات مثل SavingsAccount وCheckingAccount، وكلاهما يرث من الفئة الأساسية Account. لكل نوع حساب سلوكيات فريدة: فقد تكتسب حسابات التوفير فوائد، بينما قد تتمتع الحسابات الجارية بحماية من السحب على المكشوف.
فكّر في كيفية نمذجة المعاملات. يمكن لواجهة مثل Transactable أن تعرّف أساليبًا للإيداع والسحب عبر deposit() وwithdraw(). فكّر في استخدام استثناءات مخصصة مثل InsufficientFundsException لمعالجة العمليات غير الصالحة بسلاسة.
قد تدير الفئة Bank عدة حسابات وعملاء، موضحةً التركيب. يمكنك تطبيق نمط المصنع لإنشاء أنواع مختلفة من الحسابات، أو استخدام نمط المراقب لإخطار العملاء بتغييرات الحساب.
ركّز على التغليف لحماية البيانات الحساسة مثل الأرصدة وأرقام الحسابات، واستخدم تعدد الأشكال لمعالجة أنواع الحسابات المختلفة بطريقة موحّدة.
التحدي
سهللنبنِ نظامًا مصرفيًا متكاملًا يُظهر إتقانك لمبادئ البرمجة كائنية التوجه! ستنشئ نظامًا يتضمن أنواعًا مختلفة من الحسابات، ومعالجة المعاملات، وإدارة الاستثناءات بشكل سليم، وكل ذلك موزع على عدة ملفات.
ستوزع الكود على سبعة ملفات:
Transactable.java: أنشئ واجهة تحدد العقد الخاص بالعمليات المالية. يجب أن تعلن عن الدالتينdeposit(double amount)وwithdraw(double amount)، على أن تعيدا كلتاهما قيمة منطقية للإشارة إلى النجاح.InsufficientFundsException.java: أنشئ استثناءً مخصصًا يرث منException. يجب أن يقبل المُنشئ مبلغ السحب المطلوب والرصيد الحالي، وأن ينشئ رسالة بالتنسيقInsufficient funds: attempted [amount], available [balance].Account.java: أنشئ فئة أساسية مجردة تمثل أي حساب مصرفي. أضف حقولًا خاصة لـaccountNumber(String) وbalance(double)، ومُنشئًا يهيئ كليهما، ودوال الحصول المناسبة، ودالة تعيين محمية للرصيد (كي تتمكن الفئات الفرعية من تعديله)، ودالة مجردةgetAccountType()تعيد قيمة من النوع String. نفّذ الدالةgetDetails()لتعيد"[accountNumber] ([accountType]): $[balance]"مع تنسيق الرصيد إلى منزلتين عشريتين.SavingsAccount.java: أنشئ فئة ترث منAccountوتنفّذTransactable. أضف حقلًا خاصًا لـinterestRate(double). يستقبل المُنشئ accountNumber والرصيد الابتدائي وinterestRate. نفّذgetAccountType()لتعيد"Savings". فيdeposit()، أضف المبلغ إلى الرصيد وأعد true. فيwithdraw()، إذا تجاوز المبلغ الرصيد، فأعد false؛ وإلا اطرح المبلغ وأعد true. أضف دالةapplyInterest()تزيد الرصيد بمقدارbalance * interestRate(يُخزَّن المعدل ككسر، لذا فإن0.05تعني 5%).CheckingAccount.java: أنشئ فئة ترث منAccountوتنفّذTransactable. أضف حقلًا خاصًا لـoverdraftLimit(double). يستقبل المُنشئ accountNumber والرصيد الابتدائي وoverdraftLimit. نفّذgetAccountType()لتعيد"Checking". فيdeposit()، أضف المبلغ وأعد true. فيwithdraw()، اسمح بالسحب إذا كان المبلغ أقل من الرصيد مضافًا إليه overdraftLimit أو مساويًا له؛ وأعد false إذا تجاوز هذا الحد.Bank.java: أنشئ فئة تدير الحسابات باستخدام ArrayList. أضف الدوال التالية:addAccount(Account account)- تضيف حسابًا إلى البنكfindAccount(String accountNumber)- تعيد Account أو null إذا لم يُعثر على الحسابtransfer(String fromAccount, String toAccount, double amount)- تحوّل الأموال بين الحسابات، وتطلقInsufficientFundsExceptionإذا لم يكن في الحساب المصدر أموال كافية. عند النجاح، أعد"Transferred $[amount] from [fromAccount] to [toAccount]"getTotalDeposits()- تعيد مجموع أرصدة جميع الحسابات
Main.java: أنشئ نظامك المصرفي! ستتلقى سلسلة أوامر مفصولة بفواصل:SAVINGS:accountNumber:balance:interestRateCHECKING:accountNumber:balance:overdraftLimitDEPOSIT:accountNumber:amountWITHDRAW:accountNumber:amountTRANSFER:fromAccount:toAccount:amountINTEREST: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 بسحب 600$ رغم أن رصيده لا يتجاوز 500$؛ فهذه هي حماية السحب على المكشوف أثناء العمل! يكتسب حساب Savings فائدة بنسبة 5% على رصيده بعد التحويل. يجمع هذا التحدي بين الوراثة والواجهات وتعدد الأشكال والاستثناءات المخصصة والتغليف في تطبيق مصرفي واقعي!
جرّب بنفسك
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: قسّم input باستخدام الفاصلة للحصول على الأوامر الفردية
// 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]" بتنسيق يتضمن منزلتين عشريتين
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت