C#: تمرين: شامل في البرمجة الموجهة للكائنات
1. نظام الحسابات المصرفية
الهدف: إنشاء نظام حسابات مصرفية باستخدام الفئات والوراثة والتعدد الشكلي لإدارة أنواع مختلفة من الحسابات.
المتطلبات:
Accountفئة أساسية تتضمن الرصيد وطرق الإيداع والسحبSavingsAccountيرث خصائصAccount، ويضيف ميزة احتساب الفائدة، ولا يسمح بالسحب على المكشوفCheckingAccountيرث خصائصAccount، ويدعم حد السحب على المكشوف- استخدام تعدد الأشكال لاستدعاء عمليات السحب بشكل موحد
▶ مثال
CSHARP
📖 للعرض فقط
using System;
using System.Collections.Generic;
class Account
{
public string Owner { get; set; }
public decimal Balance { get; protected set; }
public Account(string owner, decimal balance)
{
Owner = owner;
Balance = balance;
}
public virtual void Deposit(decimal amount)
{
if (amount <= 0)
{
Console.WriteLine("Deposit amount must be greater than 0");
return;
}
Balance += amount;
Console.WriteLine($"{Owner} deposited {amount:C}, balance {Balance:C}");
}
public virtual void Withdraw(decimal amount)
{
if (amount <= 0)
{
Console.WriteLine("Withdrawal amount must be greater than 0");
return;
}
if (amount > Balance)
{
Console.WriteLine($"{Owner} insufficient balance, cannot withdraw {amount:C}");
return;
}
Balance -= amount;
Console.WriteLine($"{Owner} withdrew {amount:C}, balance {Balance:C}");
}
public virtual void DisplayInfo()
{
Console.WriteLine($"Account holder: {Owner}, balance: {Balance:C}");
}
}
class SavingsAccount : Account
{
public decimal InterestRate { get; set; }
public SavingsAccount(string owner, decimal balance, decimal interestRate)
: base(owner, balance)
{
InterestRate = interestRate;
}
public void ApplyInterest()
{
decimal interest = Balance * InterestRate;
Balance += interest;
Console.WriteLine($"{Owner} earned interest {interest:C}, balance {Balance:C}");
}
public override void Withdraw(decimal amount)
{
if (amount <= 0)
{
Console.WriteLine("Withdrawal amount must be greater than 0");
return;
}
if (amount > Balance)
{
Console.WriteLine($"Savings account cannot overdraft, balance {Balance:C}, cannot withdraw {amount:C}");
return;
}
Balance -= amount;
Console.WriteLine($"{Owner}(savings) withdrew {amount:C}, balance {Balance:C}");
}
public override void DisplayInfo()
{
Console.WriteLine($"[Savings Account] holder: {Owner}, balance: {Balance:C}, interest rate: {InterestRate:P}");
}
}
class CheckingAccount : Account
{
public decimal OverdraftLimit { get; set; }
public CheckingAccount(string owner, decimal balance, decimal overdraftLimit)
: base(owner, balance)
{
OverdraftLimit = overdraftLimit;
}
public override void Withdraw(decimal amount)
{
if (amount <= 0)
{
Console.WriteLine("Withdrawal amount must be greater than 0");
return;
}
if (amount > Balance + OverdraftLimit)
{
Console.WriteLine($"Exceeds overdraft limit, available {Balance + OverdraftLimit:C}, cannot withdraw {amount:C}");
return;
}
Balance -= amount;
Console.WriteLine($"{Owner}(checking) withdrew {amount:C}, balance {Balance:C}, overdraft limit {OverdraftLimit:C}");
}
public override void DisplayInfo()
{
Console.WriteLine($"[Checking Account] holder: {Owner}, balance: {Balance:C}, overdraft limit: {OverdraftLimit:C}");
}
}
class Program
{
static void Main()
{
List<Account> accounts = new List<Account>
{
new SavingsAccount("Zhang San", 10000m, 0.03m),
new CheckingAccount("Li Si", 5000m, 2000m)
};
Console.WriteLine("=== Polymorphic Withdrawal Demo ===");
foreach (Account acc in accounts)
{
acc.Withdraw(6000m);
}
Console.WriteLine();
Console.WriteLine("=== Deposit & Interest ===");
accounts[0].Deposit(2000m);
((SavingsAccount)accounts[0]).ApplyInterest();
Console.WriteLine();
Console.WriteLine("=== Account Info ===");
foreach (Account acc in accounts)
{
acc.DisplayInfo();
}
}
}
TEXT
📖 للعرض فقط
=== Polymorphic Withdrawal Demo ===
Savings account cannot overdraft, balance ¥10,000.00, cannot withdraw ¥6,000.00
Li Si(checking) withdrew ¥6,000.00, balance ¥-1,000.00, overdraft limit ¥2,000.00
=== Deposit & Interest ===
Zhang San deposited ¥2,000.00, balance ¥12,000.00
Zhang San earned interest ¥360.00, balance ¥12,360.00
=== Account Info ===
[Savings Account] holder: Zhang San, balance: ¥12,360.00, interest rate: 3.00%
[Checking Account] holder: Li Si, balance: ¥-1,000.00, overdraft limit: ¥2,000.00
2. حاسبة مساحة الشكل
الهدف: استخدام الفئات المجردة والواجهات لتنفيذ حسابات المساحة لأشكال متعددة، ودعم الفرز حسب المساحة.
المتطلبات:
- فئة مجردة
Shapeتُعرِّف طريقة مجردةArea() - يرث كل من
CircleوRectangleمنShape، ويقومان بتنفيذ عملية حساب المساحة - تنفيذ واجهة
IComparable<Shape>للفرز حسب المساحة - توضيح عملية الفرز المتعدد الأشكال
▶ مثال
CSHARP
📖 للعرض فقط
using System;
using System.Collections.Generic;
abstract class Shape : IComparable<Shape>
{
public abstract double Area();
public int CompareTo(Shape other)
{
if (other == null) return 1;
return Area().CompareTo(other.Area());
}
public override string ToString()
{
return $"{GetType().Name} - area: {Area():F2}";
}
}
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
public override double Area()
{
return Math.PI * Radius * Radius;
}
public override string ToString()
{
return $"Circle(radius={Radius}) - area: {Area():F2}";
}
}
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double Area()
{
return Width * Height;
}
public override string ToString()
{
return $"Rectangle({Width}x{Height}) - area: {Area():F2}";
}
}
class Program
{
static void Main()
{
List<Shape> shapes = new List<Shape>
{
new Circle(5),
new Rectangle(4, 6),
new Circle(2),
new Rectangle(10, 3),
new Circle(3.5)
};
Console.WriteLine("=== Before Sorting ===");
foreach (Shape s in shapes)
{
Console.WriteLine(s);
}
shapes.Sort();
Console.WriteLine();
Console.WriteLine("=== Sorted by Area ===");
foreach (Shape s in shapes)
{
Console.WriteLine(s);
}
Console.WriteLine();
Console.WriteLine($"Total area: {TotalArea(shapes):F2}");
}
static double TotalArea(List<Shape> shapes)
{
double total = 0;
foreach (Shape s in shapes)
{
total += s.Area();
}
return total;
}
}
TEXT
📖 للعرض فقط
=== Before Sorting ===
Circle(radius=5) - area: 78.54
Rectangle(4x6) - area: 24.00
Circle(radius=2) - area: 12.57
Rectangle(10x3) - area: 30.00
Circle(radius=3.5) - area: 38.48
=== Sorted by Area ===
Circle(radius=2) - area: 12.57
Rectangle(4x6) - area: 24.00
Rectangle(10x3) - area: 30.00
Circle(radius=3.5) - area: 38.48
Circle(radius=5) - area: 78.54
Total area: 183.59
3. نظام بسيط لإدارة المخزون
الهدف: استخدام المجموعات والأحداث العامة لإنشاء نظام لإدارة المخزون يقوم بتشغيل حدث «انخفاض المخزون» عندما ينخفض مستوى المخزون عن الحد الأدنى المحدد.
المتطلبات:
- فئة
Productتحتوي على الاسم والكمية والسعر - فئة
Inventoryالتي تستخدمList<Product>لإدارة المنتجات - حدث انخفاض المخزون الذي يؤدي إلى إرسال إشعار عندما تنخفض كمية المنتج عن الحد الأدنى المحدد
- توفير طرق «الإضافة» و«الحذف» و«البحث»
▶ مثال
CSHARP
📖 للعرض فقط
using System;
using System.Collections.Generic;
class Product
{
public string Name { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public int LowStockThreshold { get; set; }
public Product(string name, int quantity, decimal price, int lowStockThreshold)
{
Name = name;
Quantity = quantity;
Price = price;
LowStockThreshold = lowStockThreshold;
}
public bool IsLowStock()
{
return Quantity < LowStockThreshold;
}
public override string ToString()
{
return $"{Name} - quantity: {Quantity}, unit price: {Price:C}, threshold: {LowStockThreshold}";
}
}
class LowStockEventArgs : EventArgs
{
public Product Product { get; set; }
public int CurrentQuantity { get; set; }
public LowStockEventArgs(Product product, int currentQuantity)
{
Product = product;
CurrentQuantity = currentQuantity;
}
}
class Inventory
{
private List```<Product>``` products = new List```<Product>```();
public event EventHandler<LowStockEventArgs> LowStockAlert;
public void Add(Product product)
{
Product existing = FindByName(product.Name);
if (existing != null)
{
existing.Quantity += product.Quantity;
Console.WriteLine($"Product {product.Name} already exists, quantity increased to {existing.Quantity}");
}
else
{
products.Add(product);
Console.WriteLine($"Added product: {product.Name}, quantity: {product.Quantity}");
}
CheckLowStock(product);
}
public void Remove(string name, int quantity)
{
Product product = FindByName(name);
if (product == null)
{
Console.WriteLine($"Product {name} does not exist");
return;
}
if (quantity > product.Quantity)
{
Console.WriteLine($"Insufficient stock for {name}, current quantity: {product.Quantity}, requested removal: {quantity}");
return;
}
product.Quantity -= quantity;
Console.WriteLine($"Removed {quantity} of {name}, remaining {product.Quantity}");
CheckLowStock(product);
}
public Product FindByName(string name)
{
foreach (Product p in products)
{
if (p.Name == name)
{
return p;
}
}
return null;
}
public void DisplayAll()
{
Console.WriteLine("=== Inventory List ===");
foreach (Product p in products)
{
string warning = p.IsLowStock() ? " ⚠️Low stock" : "";
Console.WriteLine($"{p}{warning}");
}
Console.WriteLine($"Product types: {products.Count}");
}
protected virtual void OnLowStock(LowStockEventArgs e)
{
LowStockAlert?.Invoke(this, e);
}
private void CheckLowStock(Product product)
{
if (product.IsLowStock())
{
OnLowStock(new LowStockEventArgs(product, product.Quantity));
}
}
}
class Program
{
static void Main()
{
Inventory inventory = new Inventory();
inventory.LowStockAlert += OnLowStock;
inventory.Add(new Product("Keyboard", 50, 299m, 10));
inventory.Add(new Product("Mouse", 30, 99m, 10));
inventory.Add(new Product("Monitor", 5, 1999m, 10));
Console.WriteLine();
inventory.Remove("Monitor", 2);
inventory.Remove("Keyboard", 45);
Console.WriteLine();
inventory.Add(new Product("Keyboard", 3, 299m, 10));
Console.WriteLine();
Product found = inventory.FindByName("Mouse");
if (found != null)
{
Console.WriteLine($"Search result: {found}");
}
Console.WriteLine();
inventory.DisplayAll();
}
static void OnLowStock(object sender, LowStockEventArgs e)
{
Console.WriteLine($"💡 [Low Stock Alert] {e.Product.Name} current quantity {e.CurrentQuantity}, below threshold {e.Product.LowStockThreshold}");
}
}
TEXT
📖 للعرض فقط
Added product: Keyboard, quantity: 50
Added product: Mouse, quantity: 30
Added product: Monitor, quantity: 5
💡 [Low Stock Alert] Monitor current quantity 5, below threshold 10
Removed 2 of Monitor, remaining 3
💡 [Low Stock Alert] Monitor current quantity 3, below threshold 10
Removed 45 of Keyboard, remaining 5
💡 [Low Stock Alert] Keyboard current quantity 5, below threshold 10
Product Keyboard already exists, quantity increased to 8
💡 [Low Stock Alert] Keyboard current quantity 8, below threshold 10
Search result: Mouse - quantity: 30, unit price: ¥99.00, threshold: 10
=== Inventory List ===
Keyboard - quantity: 8, unit price: ¥299.00, threshold: 10 ⚠️Low stock
Mouse - quantity: 30, unit price: ¥99.00, threshold: 10
Monitor - quantity: 3, unit price: ¥1,999.00, threshold: 10 ⚠️Low stock
Product types: 3
❓ أسئلة شائعة
س لماذا لا يمكن أن يتجاوز حساب التوفير رصيده عند السحب؟
ج يتمثل مبدأ تصميم حساب التوفير في الإدارة المالية المحافظة. وتضيف طريقة السحب المُعدَّلة فحصًا للرصيد يرفض طلبات السحب التي تتجاوز الرصيد المتاح.
س هل يجب أن أختار فئة مجردة أم واجهة؟
ج إذا كانت هناك حقول مشتركة أو تطبيقات افتراضية، فاستخدم فئة مجردة (مثل CompareTo في فئة Shape)؛ أما إذا كنت تحتاج فقط إلى تعريف عقد سلوكي، فاستخدم واجهة (مثل IComparable
<T>). يمكنك استخدام كليهما معًا.س ما الفرق بين الأحداث والمندوبين؟
ج الحدث هو تغليف للمندوب. لا يمكن للكود الخارجي الاشتراك أو إلغاء الاشتراك إلا عبر += و-=، ولا يمكنه استدعاءه مباشرةً، مما يوفر نمط نشر-اشتراك أكثر أمانًا.
📖 ملخص
- تدربت على مفهوم وراثة الفئات والتعدد الشكلي من خلال نظام الحسابات المصرفية، باستخدام وظيفة «Override Withdraw» لتنفيذ استراتيجيات سحب مختلفة
- تدربت على دمج الفئات المجردة مع الواجهة العامة IComparable
<T>من خلال برنامج حساب مساحة الشكل - تم تطبيق
<T>المجموعات العامة والأحداث المخصصة ونقل البيانات عبر EventArgs في نظام إدارة المخزون - تغطي هذه المشاريع الثلاثة بشكل شامل المعارف الأساسية للمرحلة الثالثة: التوريث، والتعدد الشكلي، والفئات المجردة، والواجهات، والأنواع العامة، والأحداث
📝 تمارين
- إضافة فئة
FixedDepositAccountإلى نظام الحسابات المصرفية مع تحديد مدة ثابتة للودائع، مع فرض غرامة في حالة السحب المبكر - إضافة فئة
Triangleإلى آلة حاسبة مساحة الشكل، بحيث تقوم بتنفيذ عملية حساب المساحة وتشارك في عملية الفرز - إضافة حدث
Purchaseإلى نظام إدارة المخزون، بحيث يتم تشغيله عند إضافة المنتجات، مع تسجيل سجلات الشراء