نمط Abstract Factory
جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 88 من 107.
يوسع نمط المصنع المجرد (Abstract Factory pattern) نمط المصنع (Factory pattern) من خلال إنشاء عائلات من الكائنات المرتبطة دون تحديد أنواعها الملموسة. فبدلاً من دالة مصنع واحدة، تقوم بتعريف واجهة مصنع (factory interface) تقوم عدة مصانع ملموسة (concrete factories) بتنفيذها، حيث ينتج كل منها مجموعة متسقة من الكائنات المرتبطة.
فكر في مجموعة أدوات واجهة مستخدم (UI toolkit) تحتاج إلى إنشاء أزرار وخانات اختيار. تتطلب أنظمة التشغيل المختلفة عمليات تنفيذ مختلفة، ولكن يجب أن تعمل المكونات معًا بشكل متسق:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// واجهة المصنع المجرد (Abstract factory interface)
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}ينتج كل مصنع محدد عائلة من المكونات المرتبطة:
// عائلة Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// عائلة Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }يعمل كود العميل مع واجهة المصنع، ويظل مستقلاً عن التنفيذات الملموسة:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}استخدم نمط المصنع المجرد (Abstract Factory) عندما يحتاج نظامك للعمل مع عائلات متعددة من المنتجات ذات الصلة وتريد التأكد من استخدام المكونات من نفس العائلة معًا. إنه أكثر تعقيدًا من نمط المصنع (Factory) البسيط، لذا لا تلجأ إليه إلا عندما تحتاج حقًا إلى إدارة عائلات المنتجات.
التحدي
سهللنقم ببناء نظام لمتجر أثاث باستخدام نمط المصنع المجرد (Abstract Factory pattern)! ستقوم بإنشاء مصانع تنتج عائلات من قطع الأثاث المترابطة—كل مصنع ينتج نمطاً متسقاً (Modern أو Vintage)، مما يضمن أن الكراسي والطاولات من نفس المصنع تتطابق جمالياً.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
furniture.go: حدد واجهات المنتجات (product interfaces) وأنواع الأثاث الملموسة (concrete furniture types).أنشئ واجهتين للمنتجات:
Chairمع طريقةSitOn() stringTableمع طريقةPlaceItems() string
قم بتنفيذ عائلتين من الأثاث:
- عائلة Modern: تعيد
ModernChairالقيمة"Sitting on a sleek modern chair"وتعيدModernTableالقيمة"Placing items on a glass modern table" - عائلة Vintage: تعيد
VintageChairالقيمة"Sitting on an ornate vintage chair"وتعيدVintageTableالقيمة"Placing items on a wooden vintage table"
factory.go: حدد واجهة المصنع المجرد (abstract factory interface) والمصانع الملموسة (concrete factories).أنشئ واجهة
FurnitureFactoryمع طريقتين:CreateChair() ChairCreateTable() Table
قم بتنفيذ مصنعين ملموسين:
ModernFactoryالذي ينتجModernChairوModernTableVintageFactoryالذي ينتجVintageChairوVintageTable
قم أيضاً بإنشاء دالة
GetFactory(style string) FurnitureFactoryالتي تعيد المصنع المناسب بناءً على النمط ("modern"أو"vintage"). اجعلModernFactoryهو الافتراضي للأنماط غير المعترف بها.main.go: استخدم مصانعك لتأثيث الغرف.أنشئ دالة
FurnishRoom(factory FurnitureFactory) stringتستخدم المصنع لإنشاء كرسي وطاولة، ثم تعيد سلسلة نصية منسقة تعرض كلا الإجراءين في سطرين منفصلين.اقرأ مدخلاً للنمط، واحصل على المصنع المناسب، وقم بتأثيث غرفة، واطبع النتيجة.
سيتم توفير المدخلات التالية:
- السطر 1: نمط الأثاث (
"modern"،"vintage"، أو شيء آخر)
على سبيل المثال، إذا كان المدخل:
modernيجب أن يكون مخرجك:
Sitting on a sleek modern chair
Placing items on a glass modern tableوإذا كان المدخل:
vintageيجب أن يكون مخرجك:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableوإذا كان المدخل:
rusticيجب أن يكون مخرجك:
Sitting on a sleek modern chair
Placing items on a glass modern tableلاحظ كيف تعمل دالة FurnishRoom مع أي مصنع من خلال الواجهة—فهي لا تعرف ما إذا كانت تنشئ أثاثاً حديثاً (modern) أم عتيقاً (vintage). يضمن ذلك أن جميع الأثاث في الغرفة يأتي من نفس العائلة، مما يحافظ على نمط متسق!
ورقة مرجعية
يقوم نمط المصنع المجرد (Abstract Factory pattern) بإنشاء عائلات من الكائنات المرتبطة دون تحديد أنواعها الملموسة. فهو يحدد واجهة مصنع تقوم عدة مصانع ملموسة بتنفيذها، حيث ينتج كل منها مجموعة متسقة من الكائنات المرتبطة.
الهيكل
قم بتعريف واجهات المنتجات لكل نوع من الكائنات:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}قم بإنشاء واجهة مصنع مجردة تعلن عن طرق لإنشاء كل نوع من المنتجات:
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}التنفيذات الملموسة
قم بتنفيذ عائلات المنتجات الملموسة حيث يكون لكل عائلة تنسيق أو سلوك متسق:
// عائلة Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// عائلة Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }كود العميل
يعمل كود العميل مع واجهة المصنع، ويظل مستقلاً عن التنفيذات الملموسة:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}متى يُستخدم
استخدم المصنع المجرد (Abstract Factory) عندما يحتاج نظامك إلى العمل مع عائلات متعددة من المنتجات ذات الصلة وتريد التأكد من استخدام المكونات من نفس العائلة معاً. إنه أكثر تعقيداً من المصنع البسيط، لذا استخدمه فقط عندما تحتاج حقاً إلى إدارة عائلات المنتجات.
جرّب بنفسك
package main
import "fmt"
// تستخدم FurnishRoom مصنعاً لإنشاء الأثاث وإرجاع النتيجة
// TODO: قم بتنفيذ هذه الدالة
// - استخدم المصنع لإنشاء كرسي وطاولة
// - أرجع سلسلة نصية تحتوي على نتيجة SitOn() في السطر الأول ونتيجة PlaceItems() في السطر الثاني
func FurnishRoom(factory FurnitureFactory) string {
// TODO: قم بإنشاء كرسي وطاولة باستخدام المصنع
// TODO: أرجع سلسلة نصية منسقة مع كلا الإجراءين في أسطر منفصلة
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: احصل على المصنع المناسب بناءً على النمط
// TODO: قم بتأثيث غرفة باستخدام المصنع
// TODO: اطبع النتيجة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP في Go
الملفات الخارجيةمساحة العمل والـ Modules في Goالـ Packages والـ Importsالأسماء المصدرة مقابل غير المصدرةمقدمة إلى الـ OOP في Goالـ Structs كـ Classesتعريف الـ Methods في الـ Structsالـ Pointer Receivers مقابل الـ Value Receiversتهيئة الـ Structدوال الـ Constructorمراجعة - آلة حاسبة بسيطة4الواجهات
مقدمة في الواجهاتالتنفيذ الضمنيالواجهة كعقدالواجهة الفارغة (any)تأكيد النوع (Type Assertion)تبديل النوع (Type Switch)تركيب الواجهاتواجهات Stringer و Errorمراجعة - حاسبة الأشكال7التغليف
الحقول المصدرة وغير المصدرةالتغليف على مستوى الحزمةدوال الـ Getter والـ Setterإخفاء المعلومات في Goملخص - سجلات الطلاب10الأنواع العامة (Go 1.18+)
مقدمة في الأنواع العامةمعاملات الأنواعقيود الأنواعالـ Structs العامةحل بديل للـ Methods العامةملخص - المجموعات العامة13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Abstract Factoryنمط Observerنمط Strategy2تعمق في الأنواع و Structs
الأنواع الأساسية والمركبةتعريفات الأنواع المخصصةStruct TagsStructs مجهولةStructs متداخلةالقيم الصفرية والافتراضيةمراجعة - دفتر العناوين5التركيب بدلاً من الوراثة
لماذا لا تدعم Go الوراثةأساسيات تضمين الـ Structترقية الـ Methodتضمين عدة Structsالتضمين مقابل التجميعحجب الـ Methods المضمنةملخص - الهيكل الهرمي للموظفين8معالجة الأخطاء و OOP
واجهة errorأنواع الأخطاء المخصصةتغليف الأخطاء (fmt.Errorf)أخطاء Sentinelerrors.Is() و errors.As()Panic و Defer و Recoverملخص - File Parser11المكتبة القياسية والبرمجة كائنية التوجه (OOP)
io.Reader و io.Writerواجهة sort.Interfaceواجهة fmt.Stringerencoding/json مع Structsواجهة http.Handlerمراجعة - نماذج REST API14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبMiddleware كنمط مزين3المؤشرات والذاكرة
أساسيات المؤشرات في Goالمؤشرات إلى الـ Structsالتمرير بالقيمة مقابل التمرير بالمرجعدالة ()newGarbage Collection في Goملخص - بناء Linked List6تعدد الأشكال في Go
تعدد الأشكال عبر InterfacesDuck Typing في Goقواعد استيفاء الـ Interfaceمجموعات متعددة الأشكالحقن التبعيةمراجعة - معالج الدفع9التزامن والبرمجة كائنية التوجه (OOP)
أساسيات الـ Goroutinesالقنوات (Channels) والاتصالالقنوات المخزنة (Buffered) مقابل غير المخزنةجملة Selectsync.Mutex و sync.RWMutexsync.WaitGroupتصميم الـ Structs الآمنة للخيوط (Thread-Safe)مراجعة - Worker Pool