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

نمط المزين

جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 93 من 107.

يتيح لك نمط Decorator إضافة سلوك جديد إلى الكائنات بشكل ديناميكي من خلال تغليفها داخل كائنات أخرى. بينما يغيّر Adapter واجهة لتتوافق مع واجهة أخرى، يحافظ Decorator على نفس interface لكنه يعزّز الوظائف من خلال وضع أغلفة حول الكائن الأصلي.

في Go، ننفّذ ذلك بجعل كلٍّ من النوع الأساسي والمزخرفات يطبّقان الواجهة نفسها:

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

func (b BasicNotifier) Send(message string) string {
    return "Sending: " + message
}

تُغلّف Decorators عنصرًا آخر من نوع Notifier وتضيف سلوكًا قبل تفويض التنفيذ إليه أو بعده:

type TimestampDecorator struct {
    wrapped Notifier
}

func (t TimestampDecorator) Send(message string) string {
    timestamped := "[2024-01-15] " + message
    return t.wrapped.Send(timestamped)
}

type UppercaseDecorator struct {
    wrapped Notifier
}

func (u UppercaseDecorator) Send(message string) string {
    return strings.ToUpper(u.wrapped.Send(message))
}

تكمن قوة Decorator في تكديس أغلفة متعددة لدمج السلوكيات:

notifier := BasicNotifier{}
withTimestamp := TimestampDecorator{wrapped: notifier}
withBoth := UppercaseDecorator{wrapped: withTimestamp}

fmt.Println(notifier.Send("Hello"))
// إرسال: Hello

fmt.Println(withBoth.Send("Hello"))
// إرسال: [2024-01-15] HELLO

يُعدّ Decorator مثاليًا عندما تحتاج إلى إضافة مسؤوليات إلى الكائنات دون تعديل شيفرتها، خصوصًا عندما تكون هناك حاجة إلى مجموعات مختلفة من الميزات. ويُستخدم عادةً في طبقات Logging والتخزين المؤقت والمصادقة والضغط.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا لطلب القهوة في متجر باستخدام نمط Decorator! ستنشئ مشروب قهوة أساسيًا وdecorators تضيف إضافات مثل الحليب والسكر والكريمة المخفوقة. يغلّف كل decorator السابق، مما يؤدي إلى بناء الوصف والسعر معًا: مثال واقعي مثالي يوضح كيفية تراكب الوظائف باستخدام decorators.

ستنظّم التعليمات البرمجية عبر ثلاثة ملفات:

  • beverage.go: Define الواجهة الأساسية ونوع القهوة الأساسي.

    أنشئ واجهة Beverage باستخدام طريقتين:

    • Description() string: تُرجع ماهية المشروب
    • Cost() float64: تُرجع السعر

    Implement بنية Coffee باعتبارها المشروب الأساسي. يجب أن يكون وصفها Coffee وأن تكون تكلفتها 2.00.

  • decorators.go: أنشئ أنواع decorators التي تضيف الإضافات إلى أي مشروب.

    يغلّف كل decorator كائنًا من النوع Beverage ويطبّق الواجهة نفسها. أنشئ ثلاثة decorators:

    • MilkDecorator: يضيف , Milk إلى الوصف و0.50 إلى التكلفة
    • SugarDecorator: يضيف , Sugar إلى الوصف و0.25 إلى التكلفة
    • WhipDecorator: يضيف , Whip إلى الوصف و0.75 إلى التكلفة

    يجب أن يستدعي كل decorator طرق المشروب المُغلَّف ويُحسّن النتيجة.

  • main.go: أنشئ مشروبات مخصّصة من خلال تكديس decorators.

    اقرأ عدد الإضافات المراد إضافتها. ثم اقرأ نوع كل إضافة (milk أو sugar أو whip) وغلّف مشروبك الحالي باستخدام decorator المناسب.

    بعد تطبيق جميع الإضافات، اطبع وصف المشروب النهائي وتكلفته في سطرين منفصلين. نسّق التكلفة بالشكل $X.XX مع منزلتين عشريتين بالضبط.

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

  • السطر 1: عدد الإضافات المراد إضافتها
  • الأسطر التالية: نوع إضافة واحد في كل سطر (milk أو sugar أو whip)

على سبيل المثال، عند إعطاء:

2
milk
sugar

يجب أن يكون الناتج:

Coffee, Milk, Sugar
$2.75

وعند إعطاء:

3
whip
milk
milk

يجب أن يكون الناتج:

Coffee, Whip, Milk, Milk
$3.75

وعند إعطاء:

0

يجب أن يكون الناتج:

Coffee
$2.00

لاحظ كيف يغلّف كل decorator المشروب السابق، مما يؤدي إلى بناء الوصف والتكلفة معًا. يمكنك إضافة الإضافة نفسها عدة مرات (مثل double milk)، ويحدّد ترتيب decorators ترتيبها في الوصف. لا تعرف القهوة الأساسية أي شيء عن الإضافات؛ فكل decorator يحسّن ببساطة ما يغلّفه!

جرّب بنفسك

package main

import (
	"fmt"
)

func main() {
	// اقرأ عدد الإضافات
	var count int
	fmt.Scanln(&count)

	// ابدأ بقهوة أساسية
	var drink Beverage = &Coffee{}

	// اقرأ كل إضافة وغلّف المشروب بالديكوراتور المناسب
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: بناءً على نوع الإضافة، غلّف drink بالديكوراتور المناسب
		// استخدم MilkDecorator لـ "milk"
		// استخدم SugarDecorator لـ "sugar"
		// استخدم WhipDecorator لـ "whip"
	}

	// TODO: اطبع الوصف النهائي والتكلفة
	// نسّق التكلفة كـ $X.XX مع منزلتين عشريتين بالضبط
}
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Go عبر الإنترنت