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

دوال الـ Getter والـ Setter

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 49 من 107.

بما أن الحقول غير المصدرة (unexported fields) لا يمكن الوصول إليها مباشرة من خارج الحزمة، فأنت بحاجة إلى توفير دوال (methods) تسمح بالوصول المتحكم فيه. تُسمى هذه عادةً getters و setters.

في لغة Go، العرف المتبع للمسترجعات (getters) بسيط: استخدم اسم الحقل مع جعل الحرف الأول كبيراً. لا تضع البادئة "Get" - فقط استخدم Balance()، وليس GetBalance():

type BankAccount struct {
    balance int  // غير مُصدّر (unexported)
}

// Getter - يعيد القيمة
func (b *BankAccount) Balance() int {
    return b.balance
}

// Setter - يقوم بتعديل القيمة مع التحقق من صحتها
func (b *BankAccount) SetBalance(amount int) {
    if amount >= 0 {
        b.balance = amount
    }
}

تكمن القوة الحقيقية لـ setters في إضافة التحقق من الصحة أو منطق العمل. فبدلاً من السماح للكود الخارجي بتعيين أي قيمة مباشرةً، يمكنك التحكم فيما هو مسموح به:

func (b *BankAccount) Deposit(amount int) bool {
    if amount <= 0 {
        return false
    }
    b.balance += amount
    return true
}

تعمل طريقة Deposit هذه بمثابة مُعيّن (setter) متخصص يفرض القواعد—حيث لا يُسمح بالإيداعات السلبية. يجب أن يمر الكود الخارجي عبر طرقك (methods)، مما يضمن بقاء الـ struct دائمًا في حالة صالحة. هذا هو التغليف (encapsulation) قيد التنفيذ: إخفاء البيانات مع عرض سلوك محكوم.

challenge icon

التحدي

سهل

لنقم ببناء محول لدرجة الحرارة يوضح التغليف (encapsulation) الصحيح باستخدام دوال الجلب (getter) والتعيين (setter). ستقوم بإنشاء هيكل (struct) يخزن درجة الحرارة داخلياً بالدرجة المئوية (Celsius) ولكنه يوفر وصولاً متحكماً فيه من خلال دوال مع التحقق من الصحة.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • temperature.go: قم بإنشاء هيكل Temperature مع حقل celsius غير مصدر (unexported) من نوع (float64). قم بتنفيذ الدوال التالية:
    • Celsius() - دالة جلب (getter) تعيد درجة الحرارة الحالية بالدرجة المئوية.
    • SetCelsius(value float64) - دالة تعيين (setter) تقبل فقط القيم التي تساوي أو تزيد عن الصفر المطلق (-273.15). إذا كانت القيمة أقل من هذا الحد، يجب أن تظل درجة الحرارة دون تغيير.
    • Fahrenheit() - تعيد درجة الحرارة محولة إلى فهرنهايت باستخدام الصيغة: (celsius * 9/5) + 32
    • SetFahrenheit(value float64) - تحول المدخل من فهرنهايت إلى مئوية وتخزنها، ولكن فقط إذا كانت القيمة المئوية الناتجة تساوي أو تزيد عن -273.15
    أضف أيضاً منشئاً (constructor) باسم NewTemperature(celsius float64) *Temperature يقوم بإنشاء درجة حرارة مهيأة للقيمة المعطاة (افترض أن المدخلات صالحة للمنشئ).
  • main.go: اقرأ قيم درجة الحرارة من المدخلات واستعرض دوال الجلب والتعيين. قم بإنشاء درجة حرارة، واعرض قيمها بكلتا الوحدتين، ثم حاول تعيين قيم جديدة واعرض النتائج.

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

  • السطر 1: القيمة المئوية الأولية
  • السطر 2: القيمة المئوية الجديدة المراد تعيينها
  • السطر 3: قيمة فهرنهايت الجديدة المراد تعيينها

قم بتنسيق جميع مخرجات درجة الحرارة بمنزلتين عشريتين. اطبع التسلسل التالي:

  1. بعد الإنشاء: C: [celsius] F: [fahrenheit]
  2. بعد تعيين القيمة المئوية: C: [celsius] F: [fahrenheit]
  3. بعد تعيين قيمة فهرنهايت: C: [celsius] F: [fahrenheit]

على سبيل المثال، إذا كانت المدخلات 25 و -300 و 212، يجب أن يكون مخرجك:

C: 25.00 F: 77.00
C: 25.00 F: 77.00
C: 100.00 F: 212.00

لاحظ أن السطر الثاني يظهر قيمًا غير متغيرة لأن -300 أقل من الصفر المطلق، لذا رفضت دالة التعيين (setter) هذه القيمة. يظهر السطر الثالث درجة الحرارة بعد تعيين 212°F بنجاح (والتي تتحول إلى 100°C). هذه هي قوة دوال التعيين—فهي تفرض القواعد التي تحافظ على صحة بياناتك.

ورقة مرجعية

تتطلب الحقول غير المصدرة (Unexported fields) طرق getter و setter للوصول المتحكم به من خارج الحزمة.

اتفاقية الـ Getter

في لغة Go، تستخدم الـ getters اسم الحقل مع جعل الحرف الأول كبيراً. لا تضع البادئة "Get":

type BankAccount struct {
    balance int  // غير مصدر
}

// Getter - يعيد القيمة
func (b *BankAccount) Balance() int {
    return b.balance
}

Setter مع التحقق من الصحة

تقوم الـ setters بتعديل القيم ويمكن أن تتضمن التحقق من الصحة أو منطق العمل:

// Setter - يقوم بتعديل القيمة مع التحقق من الصحة
func (b *BankAccount) SetBalance(amount int) {
    if amount >= 0 {
        b.balance = amount
    }
}

Setters متخصصة

قم بإنشاء طرق تفرض قواعد محددة بدلاً من السماح بتعيين القيمة مباشرة:

func (b *BankAccount) Deposit(amount int) bool {
    if amount <= 0 {
        return false
    }
    b.balance += amount
    return true
}

يضمن هذا النهج التغليف (encapsulation): إخفاء البيانات مع عرض سلوك متحكم به، مما يحافظ على الهياكل (structs) في حالة صالحة.

جرّب بنفسك

package main

import (
	"fmt"
)

func main() {
	// قراءة قيم المدخلات
	var initialCelsius float64
	var newCelsius float64
	var newFahrenheit float64
	fmt.Scanln(&initialCelsius)
	fmt.Scanln(&newCelsius)
	fmt.Scanln(&newFahrenheit)

	// TODO: إنشاء Temperature جديدة باستخدام الـ constructor

	// TODO: طباعة القيم الأولية بالتنسيق "C: [celsius] F: [fahrenheit]" مع منزلتين عشريتين

	// TODO: محاولة تعيين قيمة Celsius جديدة وطباعة النتيجة

	// TODO: محاولة تعيين قيمة Fahrenheit جديدة وطباعة النتيجة
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming