الأساليب

الدرس 8: الأساليب

تشبيه من الواقع

تخيل أنك اشتريت سيارة. للسيارة وظائف متنوعة — تشغيل، تسارع، فرامل، تشغيل الأضواء. هذه الوظائف لا توجد بشكل مستقل؛ إنها تنتمي للسيارة. لن تقول "تشغيل" فعل عام؛ بل تقول "السيارة شُغّلت."

في Go، الأسلوب هو دالة مرتبطة بنوع معين. كما أن "التشغيل" ينتمي لـ "السيارة"، ينتمي الأسلوب للنوع المرتبط به. لن تضع دالة التشغيل على ثلاجة — بالمثل، تُعرّف الأساليب على النوع الذي تخدمه حقًا.


المفاهيم الأساسية

ما هو الأسلوب؟

الأسلوب هو دالة لها مستلم. المستلم يربط الأسلوب بنوع، مما يتيح لمتغيرات ذلك النوع استدعاء الأسلوب مباشرة.

المستلم

المستلم هو الجسر بين الأسلوب والنوع. تظهر صيغته بين كلمة func واسم الأسلوب:

GO
func (receiverVariable ReceiverType) methodName(parameters) returnType {
    // جسم الأسلوب
}

المستلم القيمي مقابل مستلم المؤشر

الميزة المستلم القيمي func (s Struct) مستلم المؤشر func (s *Struct)
يعمل على نسخة نعم (التعديلات لا تؤثر على الأصل) لا (يُعدّل الأصل مباشرة)
حالة الاستخدام عمليات للقراءة فقط، تراكيب صغيرة حاجة لتعديل الحقول، تراكيب كبيرة
تطبيق الواجهة قابل للاستدعاء على القيم والمؤشرات قابل للاستدعاء فقط على أنواع المؤشرات

قواعد مجموعة الأساليب

هذا يعني: إذا كان لديك متغير من النوع *T، يمكنه استدعاء جميع أساليب T و *T؛ بينما متغير من النوع T يمكنه فقط استدعاء أساليب T.

التركيب بدل الوراثة

ليس في Go وراثة فئات؛ بل يتم تحقيق إعادة استخدام الكود عبر التركيب (التضمين). ضمّن تراكيب في أخرى، وأساليب التراكيب المضمنة تُرقى تلقائيًا إلى التراكيب الخارجية.


الصيغة الأساسية والاستخدام

تعريف الأساليب

GO
package main

import "fmt"

// تعريف تراكيب
type Rectangle struct {
    Width  float64
    Height float64
}

// أسلوب مستلم القيمة: حساب المساحة
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// أسلوب مستلم المؤشر: القياس (يحتاج لتعديل الحقول)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    fmt.Println("Area:", rect.Area()) // الناتج: Area: 50

    rect.Scale(2)
    fmt.Println("Scaled area:", rect.Area()) // الناتج: Scaled area: 200
}

الفرق بين الأساليب والدوال

GO
// هذه دالة، غير مرتبطة بأي نوع
func Area(r Rectangle) float64 {
    return r.Width * r.Height
}

// هذا أسلوب، مرتبطة بنوع Rectangle
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

توريث الأساليب مع التراكيب المضمنة

GO
package main

import "fmt"

// النوع الأساسي
type Animal struct {
    Name string
}

// أسلوب Animal
func (a Animal) Speak() string {
    return a.Name + " makes a sound"
}

// النوع الذي يضم Animal
type Dog struct {
    Animal  // تضمين، لا حاجة لاسم حقل
    Breed string
}

// أسلوب Dog الخاص
func (d Dog) Bark() string {
    return d.Name + " barks!"
}

func main() {
    dog := Dog{
        Animal: Animal{Name: "Rex"},
        Breed:  "Golden Retriever",
    }

    // يمكن استدعاء أساليب النوع المضمن مباشرة
    fmt.Println(dog.Speak()) // الناتج: Rex makes a sound
    fmt.Println(dog.Bark())  // الناتج: Rex barks!
}
💡 نصيحة: يجب أن تكون أسماء الأساليب موجزة وذات معنى يجب أن تكون أسماء الأساليب فعلًا أو عبارة فعلية تصف العملية المنفذة. أمثلة: Area()، Scale()، Save()، IsValid().

💡 نصيحة: أبقِ أسماء المستلمات قصيرة متغيرات المستلم عادةً الحرف الأول أو أول حرفين من اسم النوع. مثلاً، func (r Rectangle) بدل func (rect Rectangle). هذه عادة Go.

💡 نصيحة: مبدأ الاتساق جميع أساليب النوع نفسه يجب أن تستخدم نفس نوع المستلم — إما جميعها مستلمات قيمية أو جميعها مستلمات مؤشر (عند الحاجة للتعديل). لا تمزج بينها.

💡 نصيحة: متى تستخدم مستلمات المؤشر استخدم دائمًا مستلمات المؤشر عندما يحتوي التراكيب على حقول غير قابلة للنسخ (مثل sync.Mutex) أو عندما يكون التراكيب كبيرًا.


الأمثلة

مثال: تعريف أساسي للأساليب (الصعوبة ⭐)

عرّف تراكيب Circle ونفّذ أساليب لحساب المساحة والمحيط.

GO
package main

import (
    "fmt"
    "math"
)

// تراكيب Circle
type Circle struct {
    Radius float64
}

// Area يحسب المساحة (مستلم القيمة، عملية للقراءة فقط)
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

// Perimeter يحسب المحيط
func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// SetRadius يُعيّن نصف قطر جديد (مستلم المؤشر، يُعدّل الحقل)
func (c *Circle) SetRadius(r float64) {
    if r > 0 {
        c.Radius = r
    }
}

func main() {
    c := Circle{Radius: 5}
    fmt.Printf("Radius: %.2f\n", c.Radius)
    fmt.Printf("Area: %.2f\n", c.Area())
    fmt.Printf("Perimeter: %.2f\n", c.Perimeter())

    c.SetRadius(10)
    fmt.Printf("\nNew radius: %.2f\n", c.Radius)
    fmt.Printf("New area: %.2f\n", c.Area())
    fmt.Printf("New perimeter: %.2f\n", c.Perimeter())
}
▶ جرّب الكود

الناتج:

TEXT
Radius: 5.00
Area: 78.54
Perimeter: 31.42

New radius: 10.00
New area: 314.16
New perimeter: 62.83

مثال: المستلم القيمي مقابل مستلم المؤشر (الصعوبة ⭐⭐)

يُظهر الفروقات السلوكية الجوهرية بين الاثنين.

GO
package main

import "fmt"

// Account حساب بنكي
type Account struct {
    Owner   string
    Balance float64
}

// المستلم القيمة: الحصول على الرصيد (للقراءة فقط، بدون تعديل)
func (a Account) GetBalance() float64 {
    return a.Balance
}

// مستلم المؤشر: الإيداع (يحتاج لتعديل الرصيد)
func (a *Account) Deposit(amount float64) {
    if amount > 0 {
        a.Balance += amount
        fmt.Printf("  Deposited %.2f, Balance: %.2f\n", amount, a.Balance)
    }
}

// مستلم المؤشر: السحب
func (a *Account) Withdraw(amount float64) bool {
    if amount > 0 && a.Balance >= amount {
        a.Balance -= amount
        fmt.Printf("  Withdrew %.2f, Balance: %.2f\n", amount, a.Balance)
        return true
    }
    fmt.Printf("  Withdrawal of %.2f failed, insufficient balance\n", amount)
    return false
}

// المستلم القيمة: تنسيق معلومات الحساب
func (a Account) String() string {
    return fmt.Sprintf("Account[%s] Balance: %.2f", a.Owner, a.Balance)
}

func main() {
    acc := Account{Owner: "Zhang San", Balance: 1000}
    fmt.Println(acc)

    fmt.Println("\n--- Transaction History ---")
    acc.Deposit(500)
    acc.Withdraw(200)
    acc.Withdraw(2000) // رصيد غير كافٍ

    fmt.Println("\n--- Final Status ---")
    fmt.Println(acc)
    fmt.Printf("Current balance: %.2f\n", acc.GetBalance())
}
▶ جرّب الكود

الناتج:

TEXT
Account[Zhang San] Balance: 1000.00

--- Transaction History ---
  Deposited 500.00, Balance: 1500.00
  Withdrew 200.00, Balance: 1300.00
  Withdrawal of 2000.00 failed, insufficient balance

--- Final Status ---
Account[Zhang San] Balance: 1300.00
Current balance: 1300.00

مثال: التركيب بدل الوراثة (الصعوبة ⭐⭐⭐)

يُظهر توريث الأساليب عبر التراكيب المضمنة وتجاوز الأساليب.

GO
package main

import "fmt"

// ==================== الطبقة الأساسية ====================

// Base تراكيب أساسي (مشابه "فئة أب")
type Base struct {
    ID   int
    Name string
}

// Describe يُرجع وصفًا أساسيًا
func (b Base) Describe() string {
    return fmt.Sprintf("Base[ID=%d, Name=%s]", b.ID, b.Name)
}

// Identify يُرجع معلومات التعريف
func (b Base) Identify() string {
    return fmt.Sprintf("I am %s (ID: %d)", b.Name, b.ID)
}

// ==================== طبقة الأعمال ====================

// Employee يضم Base
type Employee struct {
    Base         // تضمين، يرث أساليب Base
    Department string
    Salary     float64
}

// تجاوز أسلوب Describe الخاص بـ Base
func (e Employee) Describe() string {
    return fmt.Sprintf("Employee[ID=%d, Name=%s, Dept=%s, Salary=%.0f]",
        e.ID, e.Name, e.Department, e.Salary)
}

// أسلوب Employee الخاص
func (e Employee) AnnualSalary() float64 {
    return e.Salary * 12
}

// Manager يضم Employee (تضمين متعدد المستويات)
type Manager struct {
    Employee        // تضمين Employee
    TeamSize int
}

// تجاوز أسلوب Describe
func (m Manager) Describe() string {
    return fmt.Sprintf("Manager[ID=%d, Name=%s, Dept=%s, Team=%d people]",
        m.ID, m.Name, m.Department, m.TeamSize)
}

// أسلوب Manager الخاص
func (m Manager) TeamReport() string {
    return fmt.Sprintf("%s manages a team of %d people", m.Name, m.TeamSize)
}

func main() {
    // إنشاء كائن Base
    b := Base{ID: 1, Name: "Base Object"}
    fmt.Println("=== Base ===")
    fmt.Println(b.Describe())
    fmt.Println(b.Identify())

    // إنشاء كائن Employee
    emp := Employee{
        Base:       Base{ID: 2, Name: "Li Si"},
        Department: "Engineering",
        Salary:     15000,
    }
    fmt.Println("\n=== Employee ===")
    fmt.Println(emp.Describe())       // يستدعي أسلوب Employee المتجاوز
    fmt.Println(emp.Identify())       // مُورث من Base
    fmt.Printf("Annual salary: %.0f\n", emp.AnnualSalary())

    // إنشاء كائن Manager
    mgr := Manager{
        Employee: Employee{
            Base:       Base{ID: 3, Name: "Wang Wu"},
            Department: "R&D",
            Salary:     25000,
        },
        TeamSize: 8,
    }
    fmt.Println("\n=== Manager ===")
    fmt.Println(mgr.Describe())       // يستدعي أسلوب Manager المتجاوز
    fmt.Println(mgr.Identify())       // مُورث من Base عبر التضمين متعدد المستويات
    fmt.Printf("Annual salary: %.0f\n", mgr.AnnualSalary()) // مُورث من Employee
    fmt.Println(mgr.TeamReport())     // أسلوب Manager الخاص
}
▶ جرّب الكود

الناتج:

TEXT
=== Base ===
Base[ID=1, Name=Base Object]
I am Base Object (ID: 1)

=== Employee ===
Employee[ID=2, Name=Li Si, Dept=Engineering, Salary=15000]
I am Li Si (ID: 2)
Annual salary: 180000

=== Manager ===
Manager[ID=3, Name=Wang Wu, Dept=R&D, Team=8 people]
I am Wang Wu (ID: 3)
Annual salary: 300000
Wang Wu manages a team of 8 people

سيناريوهات من الواقع

السيناريو 1: نظام مصادقة المستخدم

استخدام الأساليب لتجسيد منطق مصادقة المستخدم.

GO
package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "time"
)

// User تراكيب المستخدم
type User struct {
    Username    string
    PasswordHash string
    Email       string
    CreatedAt   time.Time
    IsActive    bool
    LoginCount  int
}

// NewUser يُنشئ مستخدمًا جديدًا (نمط المُنشئ)
func NewUser(username, password, email string) *User {
    return &User{
        Username:     username,
        PasswordHash: hashPassword(password),
        Email:        email,
        CreatedAt:    time.Now(),
        IsActive:     true,
        LoginCount:   0,
    }
}

// hashPassword يُجزئ كلمة المرور (دالة مستوى الحزمة، ليست أسلوبًا)
func hashPassword(password string) string {
    h := sha256.Sum256([]byte(password))
    return hex.EncodeToString(h[:])
}

// VerifyPassword يتحقق من كلمة المرور
func (u *User) VerifyPassword(password string) bool {
    return u.PasswordHash == hashPassword(password)
}

// Login دخول المستخدم
func (u *User) Login(password string) error {
    if !u.IsActive {
        return fmt.Errorf("account %s has been disabled", u.Username)
    }
    if !u.VerifyPassword(password) {
        return fmt.Errorf("incorrect password")
    }
    u.LoginCount++
    return nil
}

// Deactivate يُعطّل الحساب
func (u *User) Deactivate() {
    u.IsActive = false
}

// Info يُرجع معلومات المستخدم
func (u User) Info() string {
    status := "Active"
    if !u.IsActive {
        status = "Disabled"
    }
    return fmt.Sprintf("[%s] %s | Email: %s | Status: %s | Login count: %d",
        u.Username, u.Username, u.Email, status, u.LoginCount)
}

func main() {
    user := NewUser("zhangsan", "mySecret123", "zhangsan@example.com")
    fmt.Println("Created user:")
    fmt.Println(user.Info())

    // الدخول بكلمة مرور صحيحة
    fmt.Println("\nLogin with correct password:")
    err := user.Login("mySecret123")
    if err != nil {
        fmt.Println("Login failed:", err)
    } else {
        fmt.Println("Login successful!")
    }
    fmt.Println(user.Info())

    // الدخول بكلمة مرور خاطئة
    fmt.Println("\nLogin with wrong password:")
    err = user.Login("wrongPassword")
    if err != nil {
        fmt.Println("Login failed:", err)
    }

    // محاولة الدخول بعد تعطيل الحساب
    fmt.Println("\nTry login after disabling account:")
    user.Deactivate()
    err = user.Login("mySecret123")
    if err != nil {
        fmt.Println("Login failed:", err)
    }
    fmt.Println(user.Info())
}

ناتج عينة:

TEXT
Created user:
[zhangsan] zhangsan | Email: zhangsan@example.com | Status: Active | Login count: 0

Login with correct password:
Login successful!
[zhangsan] zhangsan | Email: zhangsan@example.com | Status: Active | Login count: 1

Login with wrong password:
Login failed: incorrect password

Try login after disabling account:
Login failed: account zhangsan has been disabled
[zhangsan] zhangsan | Email: zhangsan@example.com | Status: Disabled | Login count: 1

السيناريو 2: نظام سلة التسوق

استخدام الأساليب لتنفيذ عمليات السلة (إنشاء، قراءة، تحديث، حذف) والخروج.

GO
package main

import "fmt"

// Product منتج
type Product struct {
    Name     string
    Price    float64
    Category string
}

// CartItem عنصر السلة
type CartItem struct {
    Product  Product
    Quantity int
}

// Subtotal يحسب المجموع الفرعي
func (ci CartItem) Subtotal() float64 {
    return ci.Product.Price * float64(ci.Quantity)
}

// ShoppingCart سلة التسوق
type ShoppingCart struct {
    Items  []CartItem
    Owner  string
}

// Add يُضيف منتجًا إلى السلة
func (sc *ShoppingCart) Add(p Product, qty int) {
    // التحقق مما إذا كان موجودًا
    for i := range sc.Items {
        if sc.Items[i].Product.Name == p.Name {
            sc.Items[i].Quantity += qty
            fmt.Printf("  Updated quantity: %s x%d\n", p.Name, sc.Items[i].Quantity)
            return
        }
    }
    sc.Items = append(sc.Items, CartItem{Product: p, Quantity: qty})
    fmt.Printf("  Added product: %s x%d\n", p.Name, qty)
}

// Remove يُزيل منتجًا من السلة
func (sc *ShoppingCart) Remove(name string) bool {
    for i, item := range sc.Items {
        if item.Product.Name == name {
            sc.Items = append(sc.Items[:i], sc.Items[i+1:]...)
            fmt.Printf("  Removed product: %s\n", name)
            return true
        }
    }
    fmt.Printf("  Product not found: %s\n", name)
    return false
}

// Total يحسب السعر الإجمالي
func (sc ShoppingCart) Total() float64 {
    var total float64
    for _, item := range sc.Items {
        total += item.Subtotal()
    }
    return total
}

// Count يُرجع عدد أنواع المنتجات
func (sc ShoppingCart) Count() int {
    return len(sc.Items)
}

// Display يطبع محتويات السلة
func (sc ShoppingCart) Display() {
    fmt.Printf("\n🛒 %s's cart (%d products):\n", sc.Owner, sc.Count())
    fmt.Println("  ─────────────────────────────────────")
    for _, item := range sc.Items {
        fmt.Printf("  %-12s $%.2f x %d = $%.2f\n",
            item.Product.Name, item.Product.Price, item.Quantity, item.Subtotal())
    }
    fmt.Println("  ─────────────────────────────────────")
    fmt.Printf("  Total: $%.2f\n", sc.Total())
}

func main() {
    cart := ShoppingCart{Owner: "Zhang San"}

    // تعريف المنتجات
    laptop := Product{Name: "Laptop", Price: 999, Category: "Electronics"}
    mouse := Product{Name: "Wireless Mouse", Price: 29, Category: "Electronics"}
    book := Product{Name: "Go Programming", Price: 45, Category: "Books"}
    coffee := Product{Name: "Coffee", Price: 5, Category: "Food"}

    // إضافة المنتجات
    fmt.Println("Adding products:")
    cart.Add(laptop, 1)
    cart.Add(mouse, 2)
    cart.Add(book, 3)
    cart.Add(coffee, 5)
    cart.Display()

    // تحديث الكميات
    fmt.Println("\nUpdating quantities:")
    cart.Add(mouse, 1)  // إضافة فأرة واحدة أخرى
    cart.Add(book, -1)  // إزالة كتاب واحد (عبر رقم سالب)
    cart.Display()

    // إزالة منتج
    fmt.Println("\nRemoving product:")
    cart.Remove("Coffee")
    cart.Display()
}

الناتج:

TEXT
Adding products:
  Added product: Laptop x1
  Added product: Wireless Mouse x2
  Added product: Go Programming x3
  Added product: Coffee x5

🛒 Zhang San's cart (4 products):
  ─────────────────────────────────────
  Laptop       $999.00 x 1 = $999.00
  Wireless Mouse $29.00 x 2 = $58.00
  Go Programming $45.00 x 3 = $135.00
  Coffee       $5.00 x 5 = $25.00
  ─────────────────────────────────────
  Total: $1217.00

Updating quantities:
  Updated quantity: Wireless Mouse x3
  Updated quantity: Go Programming x2

🛒 Zhang San's cart (4 products):
  ─────────────────────────────────────
  Laptop       $999.00 x 1 = $999.00
  Wireless Mouse $29.00 x 3 = $87.00
  Go Programming $45.00 x 2 = $90.00
  Coffee       $5.00 x 5 = $25.00
  ─────────────────────────────────────
  Total: $1201.00

Removing product:
  Removed product: Coffee

🛒 Zhang San's cart (3 products):
  ─────────────────────────────────────
  Laptop       $999.00 x 1 = $999.00
  Wireless Mouse $29.00 x 3 = $87.00
  Go Programming $45.00 x 2 = $90.00
  ─────────────────────────────────────
  Total: $1176.00

❓ أسئلة شائعة

س1: كيف أختار بين المستلم القيمي ومستلم المؤشر?

اتبع هذه القواعد:

  1. تحتاج لتعديل حقول المستلم → مستلم المؤشر
  2. المستلم تراكيب كبير (عديد الحقول أو مصفوفات كبيرة) → مستلم المؤشر (يتجنب تكلفة النسخ)
  3. التراكيب يحتوي حقول غير قابلة للنسخ مثل sync.Mutex → يجب استخدام مستلم المؤشر
  4. عملية للقراءة فقط والتراكيب صغير → المستلم القيمي كافٍ
  5. عند الشك → فضّل مستلم المؤشر
GO
type Small struct { X int }
func (s Small) Get() int { return s.X }       // ✅ مستلم القيمة، للقراءة فقط

type Large struct { Data [1024]byte }
func (l *Large) Process() { /* ... */ }        // ✅ مستلم المؤشر، يتجنب النسخ

type Safe struct { mu sync.Mutex }
func (s *Safe) Lock() { s.mu.Lock() }         // ✅ يجب استخدام المؤشر، Mutex غير قابل للنسخ

س2: لماذا يُصدر المترجم خطأ عند استدعاء أسلوب؟

السبب الأكثر شيوعًا هو التبديل بين اصطلاحات استدعاء المستلم القيمي ومستلم المؤشر.

GO
type Dog struct { Name string }

func (d *Dog) Rename(name string) {
    d.Name = name
}

func main() {
    d := Dog{Name: "Rex"}
    d.Rename("Buddy")    // ✅ Go يأخذ العنوان تلقائيًا، يعادل (&d).Rename("Buddy")

    // لكن هذا سيُسبب خطأ:
    // Dog{"Rex"}.Rename("Buddy")  // ❌ لا يمكن أخذ عنوان قيمة غير قابلة للعنونة
}

مترجم Go يتعامل تلقائيًا مع تحويل d.Rename()(&d).Rename()، لكن فقط إذا كان المتغير قابلًا للعنونة. القيم الحرفية والمؤقتة غير قابلة للعنونة.

س3: هل الأساليب يمكن أن تُرجع عدة قيم؟

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

GO
type Calculator struct {
    Value float64
}

// يُرجع حاصل القسمة والباقي
func (c Calculator) DivideBy(divisor float64) (float64, float64, error) {
    if divisor == 0 {
        return 0, 0, fmt.Errorf("divisor cannot be zero")
    }
    return c.Value / divisor, math.Mod(c.Value, divisor), nil
}

س4: ماذا عن تعارض الأساليب مع التراكيب المضمنة؟

عندما يكون لنوعين مضمنين أساليب بنفس الاسم، سيُصدر مترجم Go خطأ، وتحتاج لتحديد أيهما تستدعي صراحةً.

GO
type A struct{}
func (A) Hello() string { return "A" }

type B struct{}
func (B) Hello() string { return "B" }

type C struct {
    A
    B
}

func main() {
    c := C{}
    // c.Hello()            // ❌ خطأ ترجمة: مُحدِّد غامض c.Hello
    fmt.Println(c.A.Hello()) // ✅ صريح: يُخرج "A"
    fmt.Println(c.B.Hello()) // ✅ صريح: يُخرج "B"
}

📖 ملخص

في هذا الدرس تعلمنا عن الأساليب في Go:

  1. تعريف الأسلوب: ربط الدوال بالأنواع عبر المستلمات
  2. المستلم القيمي مقابل مستلم المؤشر: المستلمات اليمية تعمل على نسخ؛ مستلمات المؤشر تُعدّل القيم الأصلية
  3. قواعد مجموعة الأساليب: أنواع القيم لها فقط أساليب المستلم القيمي؛ أنواع المؤشرات لها جميع الأساليب
  4. التركيب بدل الوراثة: تحقيق إعادة استخدام الأساليب وتجاوزها عبر التراكيب المضمنة
  5. أفضل الممارسات: أبقِ أسماء المستلمات قصيرة، استخدم أنواع مستلمات متسقة لنفس النوع، استخدم المؤشرات عند الحاجة للتعديل

الأساليب هي أساس البرمجة الكائنية في Go. الدرس التالي يتناول الواجهات — أداة Go القوية للتعددية الشكلية، التي تعمل بشكل وثيق مع الأساليب لتشكيل جوهر نظام أنواع Go.


📝 تمارين

تمرين 1: محوّل درجة الحرارة ⭐

أنشئ تراكيب Temperature بحقل Celsius، ونفّذ الأساليب التالية:

GO
// الناتج المتوقع:
// Temperature: 100.00°C = 212.00°F = 373.15K

تمرين 2: حساب بنكي (مع سجل المعاملات) ⭐⭐

وسّع تراكيب Account من السيناريو 2 بالميزات التالية:

GO
// الناتج المتوقع:
// === Account Statement ===
// Account: Zhang San
// Transaction history:
//   1. +1000.00  (Opening deposit)
//   2. -200.00   (Purchase)
//   3. +500.00   (Salary)
// Current balance: 1300.00

تمرين 3: نظام الأشكال (وراثة التركيب) ⭐⭐⭐

أنشئ نظام أشكال:

  1. عرّف واجهة Shape أساسية بأساليب Area() float64 و Perimeter() float64
  2. نفّذ تراكيب Rectangle و Circle و Triangle
  3. أنشئ تراكيب Canvas يمكنها احتواء عدة أشكال
  4. نفّذ Canvas.TotalArea() لحساب المساحة الكلية لجميع الأشكال
  5. استخدم التراكيب المضمنة لإنشاء ColoredRectangle، الذي له أساليب Rectangle وحقل Color
GO
// الناتج المتوقع:
// Canvas has 3 shapes
// Total area: xxx.xx
// Red rectangle: width=10, height=5, color=red

الدرس التالي

الدرس 9: الواجهات — تعلّم عن نظام الواجهات في Go: التطبيق الضمني، الواجهة الفارغة، تأكيدات النوع، تركيب الواجهات، وأقوى آليات التعددية الشكلية في Go.

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%