أمان الـ Null في الـ Classes
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 28 من 110.
عند تصميم الأصناف، تحتاج إلى تحديد أي الحقول مطلوبة وأيها اختيارية. تؤثر سلامة القيم الفارغة (Null safety) على كيفية التصريح عن متغيرات النسخة (instance variables) وكتابة المنشئات (constructors).
يجب تهيئة الحقول غير القابلة للقيمة null قبل تشغيل جسم المنشئ. يمكنك القيام بذلك من خلال قوائم التهيئة، أو معاملات المنشئ، أو القيم الافتراضية:
class User {
String name; // يجب تهيئته
String? bio; // اختياري - يمكن أن يكون null
int age = 0; // له قيمة افتراضية
User(this.name); // يتم تهيئة name عبر المعامل
}بالنسبة للحقول التي تقبل القيمة null، يمكنك تركها بدون تهيئة بما أنها تعود افتراضياً إلى null. هذا مفيد للبيانات الاختيارية:
class Product {
String title;
String? description; // وصف اختياري للمنتج
double? discount; // لا يوجد خصم بشكل افتراضي
Product(this.title);
}
var item = Product('Laptop');
print(item.description); // null
print(item.discount); // nullعند الوصول إلى الحقول القابلة للإلغاء (nullable fields)، استخدم العوامل المدركة للقيم الفارغة (null-aware operators) التي تعلمتها:
class Order {
String id;
User? customer;
Order(this.id);
String getCustomerName() {
return customer?.name ?? 'Guest';
}
}تضمن الكلمة الأساسية required في المعلمات المسماة قيام المستدعين بتوفير القيم الأساسية:
class Account {
String email;
String? phone;
Account({required this.email, this.phone});
}
var acc = Account(email: 'test@mail.com'); // الهاتف اختياريمن خلال الاختيار المدروس بين الحقول القابلة للإلغاء (nullable) وغير القابلة للإلغاء (non-nullable)، يمكنك إنشاء فئات (classes) توضح بوضوح أي البيانات أساسية وأيها اختيارية.
التحدي
سهلدعنا نبني نظام حجز لفندق يوضح كيفية تصميم الفئات بمزيج مدروس من الحقول المطلوبة والاختيارية باستخدام ميزة سلامة القيم الخالية (null safety).
ستقوم بتنظيم الكود الخاص بك في ملفين:
reservation.dart: قم بتعريف فئةReservationالتي تمثل حجز فندق. يحتاج كل حجز إلى معلومات أساسية معينة، ولكن بعض التفاصيل هي إضافات اختيارية قد يقدمها الضيوف أو لا يقدمونها. يجب أن تحتوي الفئة الخاصة بك على:- متغير
String guestNameغير قابل للقيمة الخالية - يجب أن يحتوي كل حجز على اسم ضيف - متغير
int roomNumberغير قابل للقيمة الخالية - يجب تخصيص غرفة لكل حجز - متغير
int nightsغير قابل للقيمة الخالية مع قيمة افتراضية هي1- الحد الأدنى للإقامة هو ليلة واحدة - متغير
String? specialRequestsقابل للقيمة الخالية - قد يكون لدى الضيوف طلبات خاصة - متغير
DateTime? checkInTimeقابل للقيمة الخالية - قد لا يكون وقت الوصول الدقيق معروفاً - منشئ (constructor) يستخدم معاملات مسماة (named parameters) مع
requiredلـguestNameوroomNumber، بينما يجب أن يكونnightsاختيارياً مع قيمته الافتراضية - دالة
getRequestsSummary()تعيد الطلبات الخاصة إذا تم توفيرها، أو'No special requests'باستخدام عامل التشغيل?? - دالة
getArrivalInfo()تعيد التمثيل النصي لوقت تسجيل الوصول باستخدام?.، أوnullإذا لم يتم تعيينه - دالة
printConfirmation()تعرض تفاصيل الحجز
- متغير
main.dart: قم باستيراد فئة الحجز الخاصة بك وإنشاء حجوزات تعرض مجموعات مختلفة من البيانات المطلوبة والاختيارية:- أنشئ حجزاً للضيف
'Sarah Johnson'في الغرفة101لمدة3ليالٍ، مع طلبات خاصة'Late checkout'ووقت تسجيل وصول محدد بـDateTime(2024, 6, 15, 14, 30) - أنشئ حجزاً للضيف
'Mike Chen'في الغرفة205(استخدم الليالي الافتراضية)، مع تعيين الطلبات الخاصة فقط إلى'Extra pillows' - أنشئ حجزاً للضيف
'Emma Davis'في الغرفة310لمدة2ليلة (اترك كلا الحقلين الاختياريين كـ null) - لكل حجز بالترتيب، استدعِ
printConfirmation()، ثم اطبعArrival: [result]باستخدامgetArrivalInfo()
- أنشئ حجزاً للضيف
يجب أن تطبع الدالة printConfirmation() بهذا التنسيق:
=== Reservation Confirmed ===
Guest: [guestName]
Room: [roomNumber]
Nights: [nights]
Requests: [getRequestsSummary()]المخرجات المتوقعة:
=== Reservation Confirmed ===
Guest: Sarah Johnson
Room: 101
Nights: 3
Requests: Late checkout
Arrival: 2024-06-15 14:30:00.000
=== Reservation Confirmed ===
Guest: Mike Chen
Room: 205
Nights: 1
Requests: Extra pillows
Arrival: null
=== Reservation Confirmed ===
Guest: Emma Davis
Room: 310
Nights: 2
Requests: No special requests
Arrival: nullورقة مرجعية
يجب تهيئة الحقول غير القابلة للإلغاء (Non-nullable) قبل تشغيل جسم المنشئ (constructor body)، بينما يمكن أن تظل الحقول القابلة للإلغاء (nullable) بدون تهيئة (تكون قيمتها الافتراضية null):
class User {
String name; // يجب تهيئته
String? bio; // اختياري - القيمة الافتراضية null
int age = 0; // لديه قيمة افتراضية
User(this.name);
}استخدم الحقول القابلة للإلغاء (nullable fields) للبيانات الاختيارية:
class Product {
String title;
String? description; // اختياري
double? discount; // اختياري
Product(this.title);
}
var item = Product('Laptop');
print(item.description); // nullالوصول إلى الحقول القابلة للإلغاء باستخدام العوامل المدركة للقيم الفارغة (null-aware operators):
class Order {
String id;
User? customer;
Order(this.id);
String getCustomerName() {
return customer?.name ?? 'Guest';
}
}استخدم الكلمة المفتاحية required في المعاملات المسماة (named parameters) للقيم الأساسية:
class Account {
String email;
String? phone;
Account({required this.email, this.phone});
}
var acc = Account(email: 'test@mail.com');جرّب بنفسك
import 'reservation.dart';
void main() {
// TODO: 'Sarah Johnson' إنشاء حجز لـ
// الغرفة 101، 3 ليالٍ، طلبات خاصة: 'Late checkout'
// DateTime(2024, 6, 15, 14, 30) :وقت تسجيل الوصول
// TODO: 'Mike Chen' إنشاء حجز لـ
// الغرفة 205، استخدام عدد الليالي الافتراضي، طلبات خاصة: 'Extra pillows'
// لم يتم تحديد وقت لتسجيل الوصول
// TODO: 'Emma Davis' إنشاء حجز لـ
// الغرفة 310، ليلتان، لا توجد طلبات خاصة، لا يوجد وقت لتسجيل الوصول
// TODO: لكل حجز بالترتيب:
// 1. printConfirmation() استدعاء
// 2. getArrivalInfo() باستخدام 'Arrival: [result]' طباعة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع