🧬 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” (موظف)
🎯 المطلوب:
- أنشئ كلاس باسم
Employee
يحتوي على:- خاصية
_name
(اسم الموظف –String
) - خاصية
_salary
(الراتب –double
)
- خاصية
- أنشئ Getter لقراءة الاسم والراتب.
- أنشئ Setter لتعديل الراتب بشرط:
- الراتب يجب أن يكون بين
3000
و20000
فقط.
- الراتب يجب أن يكون بين
- أضف دالة
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” (منتج)
🎯 المطلوب:
- أنشئ كلاس باسم
Product
يحتوي على:_name
(اسم المنتج –String
)_price
(السعر –double
)_quantity
(الكمية المتاحة –int
)
- أضف:
- Getter لجميع الخصائص.
- Setter للسعر بشرط ألا يقل عن 1.
- Setter للكمية بشرط أن تكون عددًا موجبًا.
- أضف دالة
sell(int count)
التي:- تخصم من الكمية إذا كانت كافية.
- تطبع رسالة في حالة النقص.
- أضف دالة
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)
⚙️ الوظائف:
- Getter لـ
owner
وbalance
- دالة
deposit(double amount)
:- تضيف مبلغ للحساب فقط إذا كان موجبًا
- دالة
withdraw(double amount)
:- تسحب من الرصيد فقط إذا كان كافيًا
- دالة
transfer(double amount, BankAccount targetAccount)
:- تحوّل مبلغ من هذا الحساب إلى حساب آخر (إذا كان الرصيد كافي)
- دالة
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) | ✅ |
تنظيم الكود بأسلوب احترافي | ✅ |