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

التغليف على مستوى الحزمة

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

بينما تناول الدرس السابق مستوى رؤية field، فإن التغليف في Go يعمل فعليًا على مستوى الحزمة. كل ما يوجد داخل الحزمة نفسها، بغضّ النظر عن الملف الذي يوجد فيه، يمكنه الوصول إلى الحقول وfunction غير المُصدَّرة.

تأمل حزمة تحتوي على ملفات متعددة:

// account/account.go
package account

type Account struct {
    balance int  // غير مُصدَّر
}

func (a *Account) Deposit(amount int) {
    a.balance += amount
}
// account/helper.go
package account

func ResetAccount(a *Account) {
    a.balance = 0  // يعمل - نفس الحزمة
}

ينتمي كلا الملفين إلى الحزمة account، لذا يمكن لـ helper.go الوصول مباشرةً إلى الحقل غير المُصدَّر balance. هذا مقصود، إذ يتيح لك تقسيم منطق الحزمة عبر ملفات متعددة مع الحفاظ على الوصول الداخلي.

ومع ذلك، لا يمكن للكود الموجود خارج الحزمة الوصول إلى الأعضاء غير المُصدَّرة:

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // يعمل - method مُصدَّر
    // acc.balance = 50     // خطأ - حقل غير مُصدَّر
    // account.ResetAccount // يعمل إذا كان ResetAccount مُصدَّرًا
}

يُعدّ هذا الحدّ على مستوى الحزمة آلية التغليف الأساسية في Go. تصمّم الحزم كوحدات متماسكة، حيث يتعاون الكود الداخلي بحرية، بينما يتفاعل الكود الخارجي فقط من خلال واجهة API المُصدَّرة exported. فكّر في كل حزمة على أنها وحدة مستقلة ذات واجهة عامة واضحة.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا لإدارة inventory يوضّح كيف يمكن لملفات متعددة داخل الحزمة نفسها أن تتعاون من خلال الوصول مباشرةً إلى الحقول غير المُصدَّرة، مع إبقاء تلك البيانات مخفيةً عن الشيفرة الخارجية.

ستُنشئ ثلاثة ملفات تعمل معًا في الحزمة main:

  • product.go: عرّف بنية Product مع حقل Name مُصدَّر وحقل quantity غير مُصدَّر (من النوع int). أضف method مُصدَّرة Info() string تُعيد format: [Name]: [quantity] in stock
  • inventory.go: أنشئ helper functions تعمل مع المنتجات من خلال الوصول مباشرةً إلى الحقل غير المُصدَّر quantity (وهو أمر ممكن لأنها في الحزمة نفسها). Implement:
    • Restock(p *Product, amount int) - adds المقدار إلى quantity المنتج
    • Sell(p *Product, amount int) bool - يطرح المقدار من quantity إذا كان المخزون enough، ويُعيد true عند النجاح أو false إذا لم يكن المخزون enough
    • NewProduct(name string, initialQty int) *Product - constructor ينشئ منتجًا بالاسم والمقدار الأولي given
  • main.go: اقرأ تفاصيل المنتج من الإدخال، وأنشئ منتجًا باستخدام constructor، ونفّذ عمليات inventory، واعرض النتائج. بعد كل عملية، اطبع معلومات المنتج لإظهار الحالة المحدَّثة.

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

  • السطر 1: اسم المنتج
  • السطر 2: المقدار الأولي (عدد صحيح)
  • السطر 3: مقدار إعادة التخزين (عدد صحيح)
  • السطر 4: مقدار البيع (عدد صحيح)

بعد إنشاء المنتج، اطبع معلوماته الأولية. ثم نفّذ إعادة التخزين واطبع المعلومات مرة أخرى. أخيرًا، حاول البيع واطبع إما المعلومات المحدَّثة (إذا نجح) أو Sale failed: insufficient stock (إذا لم تكن الكمية كافية).

على سبيل المثال، given Laptop و10 و5 و8، يجب أن يكون الناتج:

Laptop: 10 in stock
Laptop: 15 in stock
Laptop: 7 in stock

وبالنسبة إلى Phone و3 و2 و10، يجب أن يكون الناتج:

Phone: 3 in stock
Phone: 5 in stock
Sale failed: insufficient stock

الفكرة الأساسية هنا هي أن inventory.go يستطيع قراءة الحقل غير المُصدَّر quantity وتعديله مباشرةً لأنه موجود في الحزمة نفسها مثل product.go. هذا هو مبدأ التغليف على مستوى الحزمة عمليًا: يحدث التعاون الداخلي بحرية، بينما تظل البيانات محميةً من الحزم الخارجية.

جرّب بنفسك

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)

	// اقرأ اسم المنتج
	name, _ := reader.ReadString('\n')
	name = strings.TrimSpace(name)

	// اقرأ الكمية الأولية
	initialQtyStr, _ := reader.ReadString('\n')
	initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))

	// اقرأ كمية إعادة التخزين
	restockStr, _ := reader.ReadString('\n')
	restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))

	// اقرأ كمية البيع
	sellStr, _ := reader.ReadString('\n')
	sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))

	// TODO: أنشئ منتجًا جديدًا باستخدام المُنشئ NewProduct

	// TODO: اطبع معلومات المنتج الأولية

	// TODO: أعد تخزين المنتج واطبع المعلومات

	// TODO: حاول البيع واطبع النتيجة
	// إذا نجح البيع، اطبع المعلومات المحدثة
	// If sale fails, print "Sale failed: insufficient stock"

	fmt.Println() // استبدل بالمخرجات الفعلية
}
quiz iconاختبر نفسك

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

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

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