C#: تمرين: شامل في البرمجة الموجهة للكائنات

1. نظام الحسابات المصرفية

الهدف: إنشاء نظام حسابات مصرفية باستخدام الفئات والوراثة والتعدد الشكلي لإدارة أنواع مختلفة من الحسابات.

المتطلبات:

▶ مثال

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();
        }
    }
}
129 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)
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. حاسبة مساحة الشكل

الهدف: استخدام الفئات المجردة والواجهات لتنفيذ حسابات المساحة لأشكال متعددة، ودعم الفرز حسب المساحة.

المتطلبات:

▶ مثال

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;
    }
}
86 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)
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. نظام بسيط لإدارة المخزون

الهدف: استخدام المجموعات والأحداث العامة لإنشاء نظام لإدارة المخزون يقوم بتشغيل حدث «انخفاض المخزون» عندما ينخفض مستوى المخزون عن الحد الأدنى المحدد.

المتطلبات:

▶ مثال

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}");
    }
}
131 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)
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>). يمكنك استخدام كليهما معًا.
س ما الفرق بين الأحداث والمندوبين؟
ج الحدث هو تغليف للمندوب. لا يمكن للكود الخارجي الاشتراك أو إلغاء الاشتراك إلا عبر += و-=، ولا يمكنه استدعاءه مباشرةً، مما يوفر نمط نشر-اشتراك أكثر أمانًا.

📖 ملخص

📝 تمارين

  1. إضافة فئة FixedDepositAccount إلى نظام الحسابات المصرفية مع تحديد مدة ثابتة للودائع، مع فرض غرامة في حالة السحب المبكر
  2. إضافة فئة Triangle إلى آلة حاسبة مساحة الشكل، بحيث تقوم بتنفيذ عملية حساب المساحة وتشارك في عملية الفرز
  3. إضافة حدث Purchase إلى نظام إدارة المخزون، بحيث يتم تشغيله عند إضافة المنتجات، مع تسجيل سجلات الشراء
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%