التسمية
الأسماء في Go لا تقلّ أهميةً عنها في أي لغة أخرى. بل إنها ذات أثر دلالي أيضًا: فظهور اسم خارج حزمة ما يتوقف على ما إذا كان أول حرف من حروفه كبيرةً أم صغيرة. ومن ثمّ،,يستحق الأمر بضع دقائق للحديث عن أعراف التسمية في برامج Go.
أسماء الحزم
عند استيراد حزمة، يصير اسمُها مُعرِّفًا للوصول إلى محتواها. فبعد
import "bytes"
يستطيع الحزمة المستورِدة أن تتحدّث عن bytes.Buffer. ومن المفيد أن يستطيع كل من يستخدم الحزمة أن يستعمل الاسم نفسه للإشارة إلى محتواها، وهذا يعني أن اسم الحزمة ينبغي أن يكون جيدًا: قصيرًا، موجزًا، مثيرًا للدلالة. وبالعرف، تُمنح الحزم أسماء من كلمة واحدة بحروف صغيرة؛ ولا حاجة إلى شرطات سفلية ولا إلى mixedCaps.
فأخطئ في اتجاه الاختصار، لأن كل من يستخدم حزمتك سيضطر إلى كتابة ذلك الاسم. ولا تقلق مسبقًا من تعارض الأسماء. اسم الحزمة ليس سوى الاسم الافتراضي للاستيراد؛ ولا يلزم أن يكون فريدًا في كامل الشيفرة المصدرية، وفي ندرتك قد ينشأ تعارض، فيستطيع الحزمة المستورِدة أن تختار اسمًا آخر محليًا. وعلى أي حال، فإن الالتباس نادر، لأن اسم الملف في جملة الاستيراد يحدّد بدقة أي حزمة مستخدَمة.
ثمة عرف آخر: أن يكون اسم الحزمة هو الاسم الأساسي لمجلد مصدرها؛ فالحزمة في src/encoding/base64 تُستورد باسم "encoding/base64" لكن اسمها base64، لا encoding_base64 ولا encodingBase64.
مستورد الحزمة سيستخدم الاسم للإشارة إلى محتواها، ومن ثمّ تستطيع الأسماء المصدَّرة في الحزمة أن تستثمر هذه الحقيقة لتتجنّب التكرار. (لا تستعمل صيغة import .، التي قد تبسّط اختبارات يجب أن تعمل خارج الحزمة التي تختبرها، لكن يُتجنّب استخدامها فيما عدا ذلك.)
فمثلًا، نوع القارئ المُخزَّن مؤقتًا في حزمة bufio يُسمّى Reader لا BufReader، لأن المستخدم يراه bufio.Reader، وهو اسم واضح وموجز. فضلًا عن ذلك، ولأن العناصر المستوردة تُنادى دائمًا باسم حزمتها، لا يتعارض bufio.Reader مع io.Reader.
وكذلك الأمر مع الدالة التي تُنشئ نسخًا جديدة من ring.Ring — وهي بعينها تعريف الـ constructor في Go — فسيُتوقّع أن تُسمّى NewRing، لكن بما أن Ring هو النوع الوحيد الذي تصدّره الحزمة، وبما أن اسم الحزمة هو ring، فقد سُمّيت New فقط، فيراها مستخدمو الحزمة ring.New. استعمل بنية الحزمة في اختيار أسماء سليمة.
ومثال آخر قصير هو once.Do: فـ once.Do(setup) تُقرأ بسلاسة، ولن تتحسّن كتابة once.DoOrWaitUntilDone(setup). فالأسماء الطويلة لا تجعل الأمر أكثر قابلية للقراءة بالضرورة. وتعليق توثيقي جيد قد يكون أنفع من اسم طويل إضافي.
الدوال الحافظة للقيم
لا توفّر Go دعمًا تلقائيًا لدوال الـ getter والـ setter. ولا بأس في أن تكتبها بنفسك، بل ذلك ملائم في أحيان كثيرة، لكنه ليس من أعراف Go ولا ضروريًا: فلا داعي لوضع Get في اسم الـ getter. إن كان لديك حقل اسمه owner (بحرف صغير، غير مصدَّر)، فينبغي أن تسمّي دالة الـ getter Owner (بحرف كبير، مصدَّرة) لا GetOwner. فاستعمال الأحرف الكبيرة للتصدير يوفّر الوسيلة التي تُميّز بها بين الحقل والدالة.
أما دالة الـ setter، إن احتجت إليها، فغالبًا ما تُسمّى SetOwner. والاسمان يجعلان القراءة سليمة في الممارسة:
owner := obj.Owner()
if owner != user {
obj.SetOwner(user)
}
أسماء الواجهات
بحسب العرف، تُسمّى الواجهة ذات الدالة الواحدة باسم الدالة مضافًا إليه اللاحقة -er أو تعديل مشابه يُنشئ اسم فاعل: Reader، وWriter، وFormatter، وCloseNotifier وغيرها.
وتوجد أعداد كبيرة من هذه الأسماء، ومن المُنتِج أن نلتزم بها وبأسماء الدوال التي تعبّر عنها. فـ Read وWrite وClose وFlush وString وما إليها لها تواقيع ومعانٍ قانونية. ولتجنّب الالتباس، لا تطلق على دالتك أحد هذه الأسماء ما لم يكن له التوّيع والمعنى نفسيهما.
وبالمقابل، إن كان نوعك ينفّذ دالة ذات المعنى نفسه لدالةٍ على نوع معروف، فامنحه الاسم والتوقيع نفسيهما؛ فسمِّ دالة التحويل إلى نص String لا ToString.
خلط الأحرف الكبيرة والصغيرة
أخيرًا، العرف في Go هو استعمال MixedCaps أو mixedCaps بدل الشرطات السفلية في كتابة الأسماء متعددة الكلمات.