إخفاء المعلومات
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 34 من 110.
إخفاء المعلومات (Information hiding) هو المبدأ الكامن وراء التغليف (encapsulation) - الفكرة التي تنص على أن الفئة (class) يجب أن تخفي آليات عملها الداخلية ولا تكشف إلا عما هو ضروري. لقد تعلمت الآليات (private fields، وgetters، وsetters)، ولكن الهدف الحقيقي هو تصميم فئات سهلة الاستخدام ويصعب إساءة استخدامها.
فكر في فئة (class) تدير كلمة مرور المستخدم. يجب أن يكون التخزين الداخلي ومنطق التجزئة (hashing) مخفيين تمامًا:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password'; // مثال مبسط
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}لا يرى الكود الخارجي أبدًا تجزئة كلمة المرور أو يعرف كيفية تخزينها. هم ببساطة يستدعون verifyPassword(). هذا الفصل يعني أنه يمكنك تغيير خوارزمية التجزئة تمامًا دون التأثير على أي كود يستخدم هذه الفئة.
البصيرة الأساسية هي التفكير في الفئة (class) الخاصة بك من الخارج إلى الداخل.
اسأل نفسك: "ما الذي يحتاجه فعلياً الشخص الذي يستخدم هذا الـ class؟" أظهر ذلك فقط، واجعل كل شيء آخر private. هذا يخلق حدوداً واضحة بين ما يفعله الـ class الخاص بك (الـ public interface الخاصة به) وكيفية قيامه بذلك (الـ private implementation الخاصة به).
التحدي
سهللنقم ببناء نظام محفظة آمن يوضح إخفاء المعلومات (information hiding) بشكل عملي. ستقوم بإنشاء محفظة رقمية تدير رصيد المستخدم وسجل المعاملات، مع الحفاظ على جميع التفاصيل الداخلية الحساسة مخفية تماماً عن الكود الخارجي.
ستقوم بتنظيم الكود الخاص بك في ملفين:
wallet.dart: أنشئ فئةWalletتدير الأموال بشكل آمن. يجب أن تخزن المحفظة اسم المالك بشكل علني، ولكن يجب إخفاء كل شيء آخر خلف واجهة عامة نظيفة:- يجب أن يكون الرصيد الداخلي وسجل المعاملات خاصين (private) - يجب ألا يرى الكود الخارجي أبداً كيف تقوم بتخزين أو تتبع الأموال.
- يجب أن تتعامل طريقة خاصة (private method) مع تسجيل المعاملات داخلياً (تخزين مدخلات مثل
"Deposit: $50.0"أو"Withdrawal: $30.0"). - قم بتوفير طريقة عامة
deposit(double amount)تضيف الأموال (فقط إذا كان المبلغ موجباً) وتسجل المعاملة داخلياً. - قم بتوفير طريقة عامة
withdraw(double amount)تسحب الأموال فقط إذا كانت هناك أموال كافية وكان المبلغ موجباً، مع تسجيل المعاملة داخلياً. قم بإرجاعtrueإذا نجحت العملية، وfalseخلاف ذلك. - قم بتوفير "getter" عام باسم
balanceيعيد الرصيد الحالي (وصول للقراءة فقط). - قم بتوفير طريقة عامة
getTransactionCount()تعيد عدد المعاملات التي تم تسجيلها (دون الكشف عن قائمة المعاملات الفعلية). - قم بتوفير طريقة عامة
getLastTransaction()تعيد سلسلة نصية لآخر معاملة، أو"No transactions"في حال عدم وجود أي معاملة. - قم بتضمين طريقة
printSummary()تعرض حالة المحفظة.
main.dart: قم باستيراد محفظتك ووضح كيف يؤدي إخفاء المعلومات إلى إنشاء واجهة نظيفة وآمنة:- أنشئ محفظة للمالك
"Sarah". - أودع
100.0. - أودع
50.0. - حاول سحب
200.0واطبع"Withdrawal of 200.0: [success/failed]"بناءً على النتيجة. - اسحب
30.0واطبع"Withdrawal of 30.0: [success/failed]". - استدعِ
printSummary(). - اطبع
"Transaction count: [count]". - اطبع
"Last transaction: [transaction]".
- أنشئ محفظة للمالك
يجب أن تقوم الطريقة printSummary() بطباعة:
=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]لاحظ كيف يمكن للكود الخارجي التفاعل مع المحفظة من خلال طرق واضحة وهادفة دون معرفة كيفية تخزين الرصيد أو كيفية تتبع المعاملات داخلياً. تفاصيل التنفيذ مخفية تماماً - يمكنك تغيير كيفية تخزين المعاملات دون التأثير على أي كود يستخدم المحفظة.
المخرجات المتوقعة:
Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0ورقة مرجعية
إخفاء المعلومات هو المبدأ الكامن وراء التغليف (encapsulation) - إخفاء الآليات الداخلية للفئة (class) وإظهار ما هو ضروري فقط.
صمم الفئات (classes) من الخارج إلى الداخل: اكشف فقط عما يحتاجه المستخدمون، وحافظ على خصوصية كل شيء آخر. يؤدي هذا إلى إنشاء حدود واضحة بين الواجهة العامة (ما تفعله الفئة) والتنفيذ الخاص (كيفية القيام بذلك).
مثال على إخفاء المعلومات باستخدام مدير كلمات المرور:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password';
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}لا تطلع الأكواد الخارجية أبدًا على تجزئة كلمة المرور (password hash) أو آلية التخزين - فهي تستخدم فقط verifyPassword(). يتيح ذلك تغيير التنفيذ الداخلي دون التأثير على الكود الخارجي.
جرّب بنفسك
import 'wallet.dart';
void main() {
// TODO: إنشاء محفظة للمالك "Sarah"
// TODO: إيداع 100.0
// TODO: إيداع 50.0
// TODO: محاولة سحب 200.0 وطباعة النتيجة
// التنسيق: "Withdrawal of 200.0: [success/failed]"
// TODO: سحب 30.0 وطباعة النتيجة
// التنسيق: "Withdrawal of 30.0: [success/failed]"
// TODO: استدعاء printSummary()
// TODO: طباعة عدد المعاملات
// التنسيق: "Transaction count: [count]"
// TODO: طباعة آخر معاملة
// التنسيق: "Last transaction: [transaction]"
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع