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

نمط Factory

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 87 من 107.

يقوم نمط المصنع (Factory pattern) بتفويض عملية إنشاء الكائنات إلى دالة أو طريقة منفصلة، مما يتيح لك إنشاء مثيلات دون الكشف عن منطق الإنشاء. وعلى عكس نمط Singleton الذي يتحكم في عدد المثيلات، يركز Factory على إنشاء الكائنات بمرونة بناءً على معايير المدخلات (input parameters).

في لغة Go، المصنع (factory) هو عادةً دالة تُرجع نوع واجهة (interface type)، مما يتيح لك إنشاء أنواع ملموسة (concrete types) مختلفة من خلال واجهة برمجة تطبيقات (API) موحدة:

type Notifier interface {
    Send(message string) string
}

type EmailNotifier struct{}
type SMSNotifier struct{}

func (e EmailNotifier) Send(message string) string {
    return "Email: " + message
}

func (s SMSNotifier) Send(message string) string {
    return "SMS: " + message
}

func NewNotifier(notifierType string) Notifier {
    switch notifierType {
    case "email":
        return EmailNotifier{}
    case "sms":
        return SMSNotifier{}
    default:
        return EmailNotifier{}
    }
}

تقوم دالة المصنع NewNotifier بتغليف قرار تحديد النوع الملموس (concrete type) الذي سيتم إنشاؤه. يعمل كود العميل فقط مع الواجهة Notifier، ويظل غير مدرك للتنفيذ المحدد:

notifier := NewNotifier("sms")
result := notifier.Send("Hello!")  // "SMS: Hello!"

يتألق هذا النمط عندما تحتاج إلى إنشاء كائنات بناءً على ظروف وقت التشغيل، أو ترغب في مركزية منطق التهيئة المعقد، أو تحتاج إلى إضافة أنواع جديدة بسهولة دون تعديل كود العميل. ما عليك سوى إضافة struct جديد يطبق الـ interface وتحديث الـ factory function.

challenge icon

التحدي

سهل

لنقم ببناء نظام محول مستندات باستخدام نمط المصنع (Factory pattern)! ستقوم بإنشاء مصنع ينتج محولات مستندات مختلفة بناءً على التنسيق المستهدف، مما يسمح لكود العميل بالعمل مع أي محول من خلال واجهة موحدة.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • converter.go: حدد واجهة المحول (interface) والتطبيقات الملموسة (concrete implementations).

    قم بإنشاء واجهة Converter مع طريقة Convert(content string) string تأخذ محتوى المستند وتعيد المخرجات المحولة.

    قم بتنفيذ ثلاثة أنواع من المحولات:

    • PDFConverter — تعيد طريقة Convert الخاصة به PDF: [content]
    • HTMLConverter — تعيد طريقة Convert الخاصة به HTML: <p>[content]</p>
    • MarkdownConverter — تعيد طريقة Convert الخاصة به MD: # [content]

    قم بإنشاء دالة مصنع NewConverter(format string) Converter تعيد المحول المناسب بناءً على سلسلة التنسيق (pdf، html، أو markdown). لأي تنسيق غير معروف، قم بإعادة PDFConverter كافتراضي.

  • main.go: استخدم المصنع الخاص بك لإنشاء المحولات ومعالجة المستندات.

    اقرأ نوع التنسيق ومحتوى المستند. استخدم المصنع لإنشاء المحول المناسب، ثم قم بتحويل المحتوى واطبع النتيجة.

سيتم توفير المدخلات التالية:

  • السطر 1: نوع التنسيق (pdf، html، markdown، أو شيء آخر)
  • السطر 2: محتوى المستند المراد تحويله

على سبيل المثال، إذا كان المعطى:

html
Welcome to Go Programming

يجب أن تكون مخرجاتك:

HTML: <p>Welcome to Go Programming</p>

وإذا كان المعطى:

markdown
Chapter One

يجب أن تكون مخرجاتك:

MD: # Chapter One

وإذا كان المعطى:

pdf
Annual Report 2024

يجب أن تكون مخرجاتك:

PDF: Annual Report 2024

وإذا كان المعطى:

docx
Meeting Notes

يجب أن تكون مخرجاتك:

PDF: Meeting Notes

لاحظ كيف أن الكود الرئيسي لا يحتاج إلى معرفة أنواع المحولات المحددة — فهو يطلب فقط من المصنع محولاً ويستخدم الواجهة. هذا يجعل من السهل إضافة تنسيقات جديدة لاحقاً دون تغيير كود العميل!

ورقة مرجعية

يقوم نمط المصنع (Factory pattern) بتفويض إنشاء الكائنات إلى وظيفة منفصلة، مما يسمح لك بإنشاء مثيلات دون الكشف عن منطق الإنشاء. يركز هذا النمط على إنشاء كائنات مرنة بناءً على معلمات الإدخال.

تعيد وظيفة المصنع نوع واجهة (interface)، مما يتيح إنشاء أنواع ملموسة مختلفة من خلال واجهة برمجة تطبيقات (API) موحدة:

type Notifier interface {
    Send(message string) string
}

type EmailNotifier struct{}
type SMSNotifier struct{}

func (e EmailNotifier) Send(message string) string {
    return "Email: " + message
}

func (s SMSNotifier) Send(message string) string {
    return "SMS: " + message
}

func NewNotifier(notifierType string) Notifier {
    switch notifierType {
    case "email":
        return EmailNotifier{}
    case "sms":
        return SMSNotifier{}
    default:
        return EmailNotifier{}
    }
}

يعمل كود العميل فقط مع الواجهة، ويظل غير مدرك للتنفيذات المحددة:

notifier := NewNotifier("sms")
result := notifier.Send("Hello!")  // "SMS: Hello!"

يكون هذا النمط مفيداً عندما تحتاج إلى إنشاء كائنات بناءً على ظروف وقت التشغيل، أو ترغب في مركزية منطق التهيئة المعقد، أو تحتاج إلى إضافة أنواع جديدة دون تعديل كود العميل.

جرّب بنفسك

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// قراءة نوع التنسيق
	scanner.Scan()
	format := scanner.Text()
	
	// قراءة محتوى المستند
	scanner.Scan()
	content := scanner.Text()
	
	// TODO: استخدم المصنع (factory) لإنشاء المحول المناسب
	// TODO: قم بتحويل المحتوى وطباعة النتيجة
	
	fmt.Println(result)
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming