طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 241 · تحليل وتصميم برمجيات الويب
الدرس 8 · الوحدة 4: تحويل المواصفات الوظيفية إلى مواصفات تقنية

من الوظيفة إلى التصميم: مخطط الأصناف

كيف تتحول المتطلبات الوظيفية إلى أصناف بخصائص وعمليات يبني منها المبرمج الكود مباشرة؟ ⏱ نحو 45 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تستخرج الأصناف والخصائص والعمليات من نص المتطلبات بتحليل الأسماء والأفعال.
  • تكتب مخطط أصناف بـ Mermaid بالرؤية والأنواع.
  • تختار نوع البيانات المناسب لكل خاصية.
  • تربط مخطط الأصناف بالكود الذي سيُكتب منه.

١من المتطلبات إلى نموذج النطاق

المتطلبات الوظيفية تقول ماذا يفعل النظام. الخطوة التالية في التحويل إلى مواصفات تقنية: ما الأشياء التي يتعامل معها النظام، وما بياناتها، وما سلوكها؟ هذا هو نموذج النطاق Domain Model، ويُرسم بـ مخطط الأصناف Class Diagram، وهو أهم مخططات UML وأكثرها استعمالًا ومن مخرجات مشروع المحاكاة في الخطة.

طريقة بسيطة للبدء: تحليل الأسماء والأفعال في نص المتطلبات. الأسماء المهمة مرشحة لتكون أصنافًا أو خصائص، والأفعال مرشحة لتكون عمليات:

🔍 FR-02 و FR-07 بعد التحليل

يجب أن يتمكن المتدرب من التسجيل في دورة متاحة، ويعرض النظام عدد المقاعد المتاحة. وتتمكن موظفة التسجيل من اعتماد طلب التسجيل أو رفضه مع ذكر السبب.

المرشحيصبحلماذا؟
المتدرب، الدورة، طلب التسجيلأصنافأشياء لها بيانات خاصة بها وتتكرر (متدربون كثيرون)
عدد المقاعد المتاحةعملية محسوبة seatsLeft()يُحسب من السعة وعدد المسجلين، فلا نخزنه
السببمعامل للعملية reject(reason)معلومة بسيطة مرتبطة بفعل
يسجّل، يعتمد، يرفضعملياتسلوك يقوم به صنف أو يحدث له

٢الصنف في المخطط

كل صنف صندوق من ثلاثة أجزاء: الاسم، ثم الخصائص (البيانات)، ثم العمليات (السلوك).

diagram.mmd
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  }

الدورة

+int id

+String title

+Date startDate

+int capacity

+decimal fee

+seatsLeft() : int

المتدرب

+int id

+String name

+String phone

-String passwordHash

+register(Course c) : 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() عملية لا خاصية مخزنة؟

الخلاصة

تحدٍّ تصميمي: نموذج نطاق «دورات»

متوسط

وسّع مخطط الأصناف ليغطي المتطلبات: الدورة لها سعة ويُحسب منها عدد المقاعد المتاحة، والمتدرب له كلمة مرور مُجزّأة سرية، وطلب التسجيل له حالة ويُعتمد، والمدرب له اسم وتخصص.

المطلوب:
  • أربعة أصناف: 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
الخاصية الخاصة تبدأ بـ - بدل +.

حاول بنفسك أولًا؛ للتصميم أكثر من حلّ صحيح، والفحص يتأكد من العناصر الأساسية فقط.

diagram.mmd
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  }

الدورة

+int id

+String title

+int capacity

+seatsLeft() : int

المتدرب

+int id

+String name

-String passwordHash

التسجيل

+int id

+String status

+approve() : void

المدرب

+int id

+String name

+String specialty

انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس