من الوظيفة إلى التصميم: مخطط الأصناف
كيف تتحول المتطلبات الوظيفية إلى أصناف بخصائص وعمليات يبني منها المبرمج الكود مباشرة؟ ⏱ نحو 45 دقيقة.
- تستخرج الأصناف والخصائص والعمليات من نص المتطلبات بتحليل الأسماء والأفعال.
- تكتب مخطط أصناف بـ
Mermaidبالرؤية والأنواع. - تختار نوع البيانات المناسب لكل خاصية.
- تربط مخطط الأصناف بالكود الذي سيُكتب منه.
١من المتطلبات إلى نموذج النطاق
المتطلبات الوظيفية تقول ماذا يفعل النظام. الخطوة التالية في التحويل إلى مواصفات تقنية: ما الأشياء التي يتعامل معها النظام، وما بياناتها، وما سلوكها؟ هذا هو نموذج النطاق Domain Model، ويُرسم بـ مخطط الأصناف Class Diagram، وهو أهم مخططات UML وأكثرها استعمالًا ومن مخرجات مشروع المحاكاة في الخطة.
طريقة بسيطة للبدء: تحليل الأسماء والأفعال في نص المتطلبات. الأسماء المهمة مرشحة لتكون أصنافًا أو خصائص، والأفعال مرشحة لتكون عمليات:
يجب أن يتمكن المتدرب من التسجيل في دورة متاحة، ويعرض النظام عدد المقاعد المتاحة. وتتمكن موظفة التسجيل من اعتماد طلب التسجيل أو رفضه مع ذكر السبب.
| المرشح | يصبح | لماذا؟ |
|---|---|---|
| المتدرب، الدورة، طلب التسجيل | أصناف | أشياء لها بيانات خاصة بها وتتكرر (متدربون كثيرون) |
| عدد المقاعد المتاحة | عملية محسوبة seatsLeft() | يُحسب من السعة وعدد المسجلين، فلا نخزنه |
| السبب | معامل للعملية reject(reason) | معلومة بسيطة مرتبطة بفعل |
| يسجّل، يعتمد، يرفض | عمليات | سلوك يقوم به صنف أو يحدث له |
٢الصنف في المخطط
كل صنف صندوق من ثلاثة أجزاء: الاسم، ثم الخصائص (البيانات)، ثم العمليات (السلوك).
classDiagram class Course["الدورة"] { +int id +String title +Date startDate +int capacity +decimal fee +seatsLeft() int } class Trainee["المتدرب"] { +int id +String name +String phone -String passwordHash +register(Course c) Enrollment } class Enrollment["التسجيل"] { +int id +String status +DateTime createdAt +approve() void +reject(String reason) void }
نكتب المعرّف بالإنجليزية لأنه سيصبح اسمًا في الكود وقاعدة البيانات (Course)، ونضع الاسم العربي للعرض بين قوسين: class Course["الدورة"]. اضغط ✏️ جرّب وعدّل وأضف للدورة خاصية +String location.
| الصيغة | معناها |
|---|---|
+String title | خاصية عامة + نوعها نص واسمها title |
-String passwordHash | خاصية خاصة - لا يصل إليها إلا الصنف نفسه |
#decimal fee | خاصية محمية # يصل إليها الصنف وأبناؤه |
+seatsLeft() int | عملية بلا معاملات تعيد عددًا صحيحًا |
+reject(String reason) void | عملية تأخذ السبب ولا تعيد شيئًا |
٣الأنواع: من لغة العمل إلى لغة البيانات
تحديد نوع كل خاصية جزء من المواصفات التقنية؛ يمنع أخطاء تكلف كثيرًا لاحقًا:
| البيانات | النوع المناسب | لماذا ليس غيره؟ |
|---|---|---|
| رسوم الدورة | decimal | المال لا يُخزّن بـ float لأن الكسور العشرية تتراكم أخطاؤها |
| رقم الجوال | String | ليس عددًا: قد يبدأ بصفر أو +966، ولا نجري عليه حسابات |
| تاريخ البدء | Date | نحتاج المقارنة والترتيب بالتاريخ |
| حالة الطلب | String بقيم محددة | pending / approved / rejected (أو تعداد enum) |
| هل الدورة منشورة؟ | bool | قيمتان فقط |
٤من المخطط إلى الكود
مخطط الأصناف ليس رسمًا نظريًّا؛ المبرمج يحوّله مباشرة إلى كود. هذا صنف الدورة كما قد يُكتب في JavaScript، ويقابله في Laravel نموذج Model بالاسم نفسه:
class Course { constructor(id, title, capacity) { this.id = id; this.title = title; this.capacity = capacity; this.enrollments = []; } seatsLeft() { return this.capacity - this.enrollments.length; }}
لاحظ: seatsLeft() تحسب الناتج من البيانات ولا تخزنه، كما قرر المحلل في المخطط. التصميم الجيد يقلل الأسئلة التي يطرحها المبرمج، ويجعل الكود متوقعًا.
توقّع وتحقّق
في تحليل الأسماء والأفعال، الأفعال مرشحة لتكون:
ماذا تعني العلامة - قبل الخاصية؟
أنسب نوع لرسوم الدورة:
لماذا seatsLeft() عملية لا خاصية مخزنة؟
الخلاصة
- نموذج النطاق: الأشياء التي يتعامل معها النظام وبياناتها وسلوكها، ويُرسم بمخطط الأصناف.
- الأسماء → أصناف أو خصائص، والأفعال → عمليات.
- الصنف: اسم، وخصائص، وعمليات. الرؤية: + عامة، - خاصة، # محمية.
- المعرّف بالإنجليزية للكود، والاسم العربي للعرض:
class Course["الدورة"]. - ما يُحسب لا يُخزّن، والمال
decimal، والجوالString.
تحدٍّ تصميمي: نموذج نطاق «دورات»
متوسطوسّع مخطط الأصناف ليغطي المتطلبات: الدورة لها سعة ويُحسب منها عدد المقاعد المتاحة، والمتدرب له كلمة مرور مُجزّأة سرية، وطلب التسجيل له حالة ويُعتمد، والمدرب له اسم وتخصص.
- أربعة أصناف: Course و Trainee و Enrollment و Trainer (مع أسمائها العربية).
- للدورة الخاصية capacity والعملية
seatsLeft(). - للمتدرب الخاصية passwordHash خاصة
(-). - لطلب التسجيل الخاصية status والعملية
approve(). - للمدرب خاصيتان على الأقل منهما specialty.
- المخطط مكتوب بصيغة صحيحة ويُرسم
- نوعه: مخطط أصناف classDiagram
- صنف Course
- صنف Trainee
- صنف Enrollment
- صنف Trainer
- Course فيه capacity
- Course فيه
seatsLeft() - passwordHash خاصة في Trainee
- Enrollment فيه status
- Enrollment فيه
approve() - Trainer فيه specialty
- Trainer فيه خاصيتان على الأقل
💡 تلميح 1
class Trainer["المدرب"] { ثم الخصائص سطرًا سطرًا ثم }.💡 تلميح 2
+seatsLeft() int.💡 تلميح 3
- بدل +.حاول بنفسك أولًا؛ للتصميم أكثر من حلّ صحيح، والفحص يتأكد من العناصر الأساسية فقط.
classDiagram class Course["الدورة"] { +int id +String title +int capacity +seatsLeft() int } class Trainee["المتدرب"] { +int id +String name -String passwordHash } class Enrollment["التسجيل"] { +int id +String status +approve() void } class Trainer["المدرب"] { +int id +String name +String specialty }