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

حل بديل للـ Methods العامة

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

لدى Go قيد ملحوظ: لا يمكنك تعريف methods مع own type parameters على struct. وعلى الرغم من أن generic structs تعمل بشكل رائع، فإن إضافة method تُعرّف new type parameter ليست allowed.

لن يتم تجميع هذا الرمز:

type Box[T any] struct {
    Value T
}

// ERROR: يجب ألا تحتوي الطريقة على معاملات نوع
func (b Box[T]) Convert[U any](fn func(T) U) U {
    return fn(b.Value)
}

الحل البديل هو استخدام دالة generic مستقلة بدلًا من method:

type Box[T any] struct {
    Value T
}

// دالة عادية - تستخدم معامل النوع الخاص بالـ struct
func (b Box[T]) Get() T {
    return b.Value
}

// دالة مستقلة - يمكن أن يكون لها معاملات نوع خاصة بها
func Convert[T, U any](b Box[T], fn func(T) U) U {
    return fn(b.Value)
}

func main() {
    intBox := Box[int]{Value: 42}
    
    // استخدم الدالة
    fmt.Println(intBox.Get())  // 42
    
    // استخدم الدالة المستقلة
    strResult := Convert(intBox, func(n int) string {
        return fmt.Sprintf("Number: %d", n)
    })
    fmt.Println(strResult)  // Number: 42
}

لا يزال بإمكان الأساليب الموجودة في البُنى العامة استخدام مُعامل النوع T الخاص بالبنية. ينطبق القيد فقط على تقديم مُعاملات نوع إضافية في توقيع الأسلوب.

عندما تحتاج إلى تلك المرونة، فإن generic function التي تأخذ البنية باعتبارها وسيطها الأول تحقق النتيجة نفسها.

challenge icon

التحدي

سهل

لنبنِ toolkit لتحويل Data يوضّح الحل البديل لقيد Go على generic methods! بما أن methods لا يمكنها تقديم type parameters جديدة، ستنشئ generic functions مستقلة تحقق المرونة نفسها.

ستنظّم code عبر ملفين:

  • wrapper.go: Define حاوية generic و functions للتحويل.

    أنشئ generic struct باسم Wrapper[T any] مع field واحد هو Value من النوع T.

    Add method باسم Get على Wrapper[T] يعيد القيمة المغلّفة. يستخدم هذا method type parameter الخاص بالـ struct، وهو أمر allowed.

    أنشئ generic function مستقلة باسم Transform[T, U any](w Wrapper[T], fn func(T) U) U تطبّق function التحويل على قيمة الحاوية وتعيد results. تحتاج هذه function إلى type parameter خاص بها، وهو U لنوع الخرج، ولهذا يجب أن تكون function مستقلة وليست method.

    أنشئ function مستقلة أخرى باسم TransformToString[T any](w Wrapper[T]) string تحوّل القيمة المغلّفة إلى string باستخدام fmt.Sprintf("%v", ...).

  • main.go: اعرض نمط الحل البديل باستخدام تحويلات مختلفة.

    اقرأ indicator للنوع (int أو string)، ثم اقرأ قيمة. أنشئ Wrapper من النوع المناسب.

    بالنسبة إلى إدخال int: أنشئ Wrapper[int]، ثم استخدم Transform لمضاعفة القيمة (مع إرجاع int)، واستخدم TransformToString للحصول على representation نصية.

    بالنسبة إلى إدخال string: أنشئ Wrapper[string]، ثم استخدم Transform للحصول على length الخاص بـ string (مع إرجاع int)، واستخدم TransformToString للحصول على representation النصية.

    Print النتائج بهذا format:

    Original: [value]
    Transformed: [transformed value]
    As String: [string representation]

سيتم توفير inputs التالية:

  • السطر 1: indicator النوع (int أو string)
  • السطر 2: القيمة

على سبيل المثال، إذا كانت المدخلات:

int
25

فيجب أن يكون output لديك:

Original: 25
Transformed: 50
As String: 25

وإذا كانت المدخلات:

string
Hello World

فيجب أن يكون output لديك:

Original: Hello World
Transformed: 11
As String: Hello World

الفكرة الأساسية هي أن Transform تأخذ Wrapper[T] باعتباره argument الأول وتقدّم type parameter جديدًا هو U لنوع الإرجاع. وهذا أمر لم يكن ممكنًا بوصفه method على الـ struct. يمنحك هذا النمط مرونة generic transformations مع العمل ضمن قيود type system في Go.

جرّب بنفسك

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// اقرأ مؤشر النوع
	typeIndicator, _ := reader.ReadString('\n')
	typeIndicator = strings.TrimSpace(typeIndicator)
	
	// اقرأ القيمة
	value, _ := reader.ReadString('\n')
	value = strings.TrimSpace(value)
	
	if typeIndicator == "int" {
		// حلل قيمة العدد الصحيح
		num, _ := strconv.Atoi(value)
		
		// TODO: أنشئ Wrapper[int] بالعدد المحلل
		
		// TODO: استخدم Transform لمضاعفة القيمة (تلميح: مرر دالة تضاعف)
		
		// TODO: استخدم TransformToString للحصول على التمثيل النصي
		
		// TODO: اطبع النتائج بالتنسيق المطلوب:
		// Original: [value]
		// Transformed: [transformed value]
		// As String: [string representation]
		
		_ = num // احذف هذا السطر عندما تستخدم num
	} else if typeIndicator == "string" {
		// TODO: أنشئ Wrapper[string] بالقيمة
		
		// TODO: استخدم Transform للحصول على طول السلسلة (تلميح: مرّر دالة تُرجع len())
		
		// TODO: استخدم TransformToString للحصول على التمثيل النصي
		
		// TODO: اطبع النتائج بالتنسيق المطلوب:
		// Original: [value]
		// Transformed: [transformed value]
		// As String: [string representation]
		
		_ = value // احذف هذا السطر عندما تستخدم value
	}
}
quiz iconاختبر نفسك

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

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

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