Encapsulation In Opp In dart

🧬 Encapsulation (التغليف أو الاحتواء) في Dart

هدفنا هنا: أن تتحكم في من يستطيع الوصول إلى بيانات الكائن، وكيف يمكنه استخدامها.

✅ أولاً: ما هو Encapsulation؟

Encapsulation يعني:

🔐 “إخفاء التفاصيل الداخلية للكائن وجعل الوصول إليها يتم بطريقة مُنظمة وآمنة، غالبًا عن طريق واجهات (getters/setters) فقط.”

✅ مثال بسيط من الحياة الواقعية:

تخيل عندك جهاز موبايل:

  • لا ترى الأسلاك والمكونات الداخلية.
  • تتعامل فقط مع الأزرار (زر التشغيل، زر الصوت) — هذه هي الواجهة العامة.

نفس الشيء في الكود:

  • الخصائص الداخلية مخفية.
  • نستخدم دوال محددة للتحكم فيها.
✅ لماذا نستخدم Encapsulation؟
الفائدةالشرح
🔒 الحمايةتمنع التغيير المباشر في البيانات الحساسة.
✅ التحكمتستطيع تحديد من ومتى وكيف تُغيّر القيم.
⚙️ الكود منظمكل شيء واضح ومُتحكم فيه.
🧠 قابلية الصيانةإذا غيرت طريقة التخزين، لن يتأثر الكود خارج الكلاس.
✅ إمكانية التحققيمكنك التحقق من البيانات عند التعيين (validation).
✅ كيف يتم تطبيق Encapsulation في Dart؟

الآلية الأساسية تتكون من 3 خطوات:

1. 🔐 اجعل الخصائص private:
  • تبدأ ب _ (شرط في Dart لجعلها private على مستوى الملف).
2. ✅ استخدم Getters لقراءة القيمة.
3. 🛠️ استخدم Setters لتغيير القيمة مع التحقق.
✅ مثال تطبيقي:
class BankAccount {
  String _ownerName;
  double _balance;

  // Constructor
  BankAccount(this._ownerName, this._balance);

  // Getter: لقراءة الاسم
  String get ownerName => _ownerName;

  // Getter: قراءة الرصيد فقط
  double get balance => _balance;

  // Setter: لإيداع مبلغ
  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
    } else {
      print('لا يمكن إيداع مبلغ سلبي');
    }
  }

  // Setter: لسحب مبلغ
  void withdraw(double amount) {
    if (amount > _balance) {
      print('الرصيد غير كافٍ');
    } else {
      _balance -= amount;
    }
  }
}
Dart
🧪 الاستخدام:
void main() {
  BankAccount acc = BankAccount('ياسين', 500);

  print('المالك: ${acc.ownerName}');
  print('الرصيد الحالي: ${acc.balance}');

  acc.deposit(200);  // إيداع
  acc.withdraw(100); // سحب

  print('الرصيد بعد العمليات: ${acc.balance}');
}
Dart
✅ شرح المثال خطوة بخطوة:
الجزءماذا يفعل؟
_ownerName و _balanceبيانات خاصة لا يمكن الوصول لها مباشرة من خارج الكلاس.
get balanceتسمح فقط بقراءة الرصيد، لا تغييره.
deposit()تضيف مبلغ بعد التأكد أنه موجب.
withdraw()تخصم مبلغ فقط إذا كان كافي.
✅ مثال آخر بسيط (للطلاب):
class Student {
  String _name;
  double _grade;

  Student(this._name, this._grade);

  String get name => _name;

  double get grade => _grade;

  set grade(double value) {
    if (value >= 0 && value <= 100) {
      _grade = value;
    } else {
      print('الدرجة يجب أن تكون بين 0 و 100');
    }
  }
}
Dart
🧪 الاستخدام:
void main() {
  var s = Student('علي', 80);
  print('${s.name} حصل على: ${s.grade}');

  s.grade = 105; // خطأ
  s.grade = 95;  // صحيح

  print('بعد التعديل: ${s.grade}');
}
Dart
✅ الخلاصة 👇
المصطلحالشرح
private fieldخاصية داخلية تبدأ بـ _ ولا يمكن الوصول إليها مباشرة.
getterدالة تقرأ القيمة وتعيدها.
setterدالة تضبط القيمة (مع أو بدون تحقق).
Encapsulationنمط برمجي يُخفي التفاصيل ويوفر تحكم كامل عبر واجهة محددة فقط.
✅ قواعد عامة يجب أن تحفظها:
القاعدةالشرح
لا تكشف خصائصك الداخلية للعالم الخارجي مباشرة.
استخدم Getters وSetters للتحكم بالقيم.
تحقّق دائمًا من صحة البيانات في الـ Setter.
كل ما يجب إخفاؤه ابدأه بـ _.
✅ هل Encapsulation مهم للمحترفين فقط؟

لا! هو مهم جدًا حتى في المشاريع الصغيرة، لأنه:

  • يمنعك من ارتكاب أخطاء في تعديل البيانات.
  • يُجبر الآخرين على احترام حدود كائناتك.
  • يُساعد في تطوير المشروع مستقبلًا بدون كسر الكود القديم.

🧪 التمرين 1: كلاس “Employee” (موظف)
🎯 المطلوب:
  1. أنشئ كلاس باسم Employee يحتوي على:
    • خاصية _name (اسم الموظف – String)
    • خاصية _salary (الراتب – double)
  2. أنشئ Getter لقراءة الاسم والراتب.
  3. أنشئ Setter لتعديل الراتب بشرط:
    • الراتب يجب أن يكون بين 3000 و 20000 فقط.
  4. أضف دالة display() لعرض بيانات الموظف.
✅ الحل:
class Employee {
  String _name;
  double _salary;

  Employee(this._name, this._salary);

  // Getters
  String get name => _name;
  double get salary => _salary;

  // Setter مع تحقق
  set salary(double newSalary) {
    if (newSalary >= 3000 && newSalary <= 20000) {
      _salary = newSalary;
    } else {
      print('🚫 الراتب يجب أن يكون بين 3000 و 20000');
    }
  }

  void display() {
    print('الموظف: $_name - الراتب: $_salary');
  }
}

void main() {
  var emp = Employee('سعاد', 5000);
  emp.display();

  emp.salary = 25000; // خطأ
  emp.salary = 15000; // صحيح
  emp.display();
}
Dart
🧪 التمرين 2: كلاس “Product” (منتج)
🎯 المطلوب:
  1. أنشئ كلاس باسم Product يحتوي على:
    • _name (اسم المنتج – String)
    • _price (السعر – double)
    • _quantity (الكمية المتاحة – int)
  2. أضف:
    • Getter لجميع الخصائص.
    • Setter للسعر بشرط ألا يقل عن 1.
    • Setter للكمية بشرط أن تكون عددًا موجبًا.
  3. أضف دالة sell(int count) التي:
    • تخصم من الكمية إذا كانت كافية.
    • تطبع رسالة في حالة النقص.
  4. أضف دالة display() لعرض تفاصيل المنتج.
✅ الحل:
class Product {
  String _name;
  double _price;
  int _quantity;

  Product(this._name, this._price, this._quantity);

  // Getters
  String get name => _name;
  double get price => _price;
  int get quantity => _quantity;

  // Setters مع تحقق
  set price(double newPrice) {
    if (newPrice >= 1) {
      _price = newPrice;
    } else {
      print('🚫 السعر يجب أن يكون 1 أو أكثر');
    }
  }

  set quantity(int q) {
    if (q >= 0) {
      _quantity = q;
    } else {
      print('🚫 الكمية لا يمكن أن تكون سالبة');
    }
  }

  // بيع المنتج
  void sell(int count) {
    if (count <= _quantity) {
      _quantity -= count;
      print('✅ تم بيع $count قطعة من $_name');
    } else {
      print('🚫 لا توجد كمية كافية للبيع');
    }
  }

  void display() {
    print('المنتج: $_name - السعر: $_price - الكمية: $_quantity');
  }
}

void main() {
  var p = Product('Laptop', 1500, 10);
  p.display();

  p.sell(3);
  p.display();

  p.sell(15); // خطأ
  p.price = 0.5; // خطأ
  p.quantity = -5; // خطأ
}
Dart
✅ بعد حل هذين التمرينين ستتقن:
المهارةتحقق منها؟ ✅
إنشاء الخصائص private
كتابة get و set
تنفيذ التحقق داخل setter
إخفاء البيانات ومنع التعديل المباشر
كتابة واجهات استخدام (دوال مثل display() و sell())

🏦 “حساب البنك – Bank Account”
🎯 المطلوب من التمرين:

أن تنشئ كلاس يمثل حساب بنكي فيه الخصائص التالية:

🔐 الخصائص (private):
  • _owner (صاحب الحساب – String)
  • _balance (الرصيد – double)
⚙️ الوظائف:
  1. Getter لـ owner و balance
  2. دالة deposit(double amount):
    • تضيف مبلغ للحساب فقط إذا كان موجبًا
  3. دالة withdraw(double amount):
    • تسحب من الرصيد فقط إذا كان كافيًا
  4. دالة transfer(double amount, BankAccount targetAccount):
    • تحوّل مبلغ من هذا الحساب إلى حساب آخر (إذا كان الرصيد كافي)
  5. دالة display():
    • تطبع بيانات الحساب
✅ الحل المقترح:
class BankAccount {
  String _owner;
  double _balance;

  BankAccount(this._owner, this._balance);

  // Getters
  String get owner => _owner;
  double get balance => _balance;

  // إيداع
  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
      print('✅ تم إيداع $amount إلى $_owner');
    } else {
      print('🚫 لا يمكن إيداع مبلغ سلبي');
    }
  }

  // سحب
  void withdraw(double amount) {
    if (amount <= 0) {
      print('🚫 لا يمكن سحب مبلغ سلبي');
    } else if (amount > _balance) {
      print('🚫 الرصيد غير كافٍ للسحب');
    } else {
      _balance -= amount;
      print('✅ تم سحب $amount من $_owner');
    }
  }

  // تحويل إلى حساب آخر
  void transfer(double amount, BankAccount targetAccount) {
    if (amount <= 0) {
      print('🚫 مبلغ التحويل يجب أن يكون موجبًا');
    } else if (amount > _balance) {
      print('🚫 لا يمكن التحويل، الرصيد غير كافٍ');
    } else {
      _balance -= amount;
      targetAccount.deposit(amount);
      print('✅ تم تحويل $amount من $_owner إلى ${targetAccount.owner}');
    }
  }

  // طباعة التفاصيل
  void display() {
    print('👤 $_owner - 💵 الرصيد: $_balance');
  }
}
Dart
🧪 الاستخدام:
void main() {
  var acc1 = BankAccount('ياسين', 1000);
  var acc2 = BankAccount('ليلى', 500);

  acc1.display();
  acc2.display();

  print('--- إيداع ---');
  acc1.deposit(300);
  acc1.display();

  print('--- سحب ---');
  acc2.withdraw(100);
  acc2.display();

  print('--- تحويل ---');
  acc1.transfer(400, acc2);

  print('--- بعد التحويل ---');
  acc1.display();
  acc2.display();
}
Dart
✅ المخرجات المتوقعة:
👤 ياسين - 💵 الرصيد: 1000.0
👤 ليلى - 💵 الرصيد: 500.0
--- إيداع ---
✅ تم إيداع 300.0 إلى ياسين
👤 ياسين - 💵 الرصيد: 1300.0
--- سحب ---
✅ تم سحب 100.0 من ليلى
👤 ليلى - 💵 الرصيد: 400.0
--- تحويل ---
✅ تم إيداع 400.0 إلى ليلى
✅ تم تحويل 400.0 من ياسين إلى ليلى
--- بعد التحويل ---
👤 ياسين - 💵 الرصيد: 900.0
👤 ليلى - 💵 الرصيد: 800.0
Dart
✅ المهارات التي ستتعلمها من التمرين:
المهارةهل استخدمناها؟
إنشـاء خصائص خاصة
استخدام Getters
التحقق داخل دوال
التعامل مع أكثر من كائن
التفاعل بين كائنات (transfer)
تنظيم الكود بأسلوب احترافي