Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

إخفاء المعلومات

جزء من قسم 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 الخاصة به).

challenge icon

التحدي

سهل

لنقم ببناء نظام محفظة آمن يوضح إخفاء المعلومات (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]"
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming