الحزم package في لغة غو

ماهي الحزم Packages في لغة غو Go ؟

حتى الان, في الدروس والأمثلة السابقة عن لغة غو, كانت دائماً برامجنا مؤلفة من ملف واحد بهدف تبسيط شرح المبادئ والاساسيات, ولكن في الحياة العملية والبرامج الفعلية عادةً ماتكون هذه البرامج مؤلفة من عدة ملفات برمجية, حيث أنه من شبه المستحيل ان يتسع برنامج ضخم في ملف برمجي واحد لأنه عندها سيصعب جداً التعديل عليه وتصحيح أخطاءه وماشابه ذلك ومن هنا تأتي أهمية استخدام الحزم في لغة غو !

الحزم في لغة غو تٌستخدم لتنظيم وترتيب الأكواد وملفات .go البرمجية لتحسين وتبسيط عملية البرمجة وتصحيح الأخطاء وإضافة التعديلات على برامجنا ونظام الحزم في لغة غو ليس جديد أو غريب, فنرى لغات برمجية اخرى تستخدم هذا النظام مثل لغة بايثون Python ولغة سي شارب C# وغيرهما الكثير ..

دالة main وحزمة main

كل برنامج يُكتب بلغة غو يجب أن يحتوي على دالة main وهي تعتبر بمثابة نقطة الدخول entry-point بالنسبة للبرنامج, فمنها يبدأ عمل البرنامج كما في لغات عديدة مثل سي بلس بلس C++ وغيرها ودالة main يجب أن تكون ضمن حزمة main

ولكي ننسب ملف برمجي ما الى حزمة ما, نستخدم التصريح الاتي في بداية الملف

package packagename

وهو أول سطر برمجي يُكتب في بداية كل ملف برمجي .go إذاً لنبدأ بإنشاء دالة main و حزمة main لبرنامجنا …

  • 1- أنشئ مجلد جديد داخل مجلد src وسمّه ماتريد حسب مشروعك, وليكن مثلاً geometry
  • 2- أنشئ ملف جديد باسم geometry.go داخل مجلد geometry
  • 3- افتح ملف geometry.go واكتب فيه الكود التالي:
package main

import "fmt"

func main() {
    fmt.Println("Geometrical shape properties")
}

نلاحظ أن أول سطر هو package main , وهو يحدد أن هذا الملف يتبع للحزمة main

ونلاحظ تعليمة استدعاء الحزم import , حيث من خلالها نستطيع استدعاء حزم اخرى لنستخدمها في برنامجنا, وفي حالتنا هذه قمنا باستدعاء الحزمة fmt وهي تعني (Format) أي تنسيق, وهي حزمة أساسية قياسية تأتي مع لغة غو بشكل تلقائي, ومن خلالها نستطيع عرض النصوص وتنسيقها و..الخ وداخل حزمة fmt القياسية يوجد دالة تٌدعى Println اختصاراً لـ (Print Line) حيث تقوم هذه الدالة بالكتابة على سطر جديد, ومن خلالها قمنا بكتابة عبارة “Geometrical shape properties”.

  • 4- نقوم بعمل تجميع Compile للبرنامج عبر الأمر التالي
go install geometry 

وهذا الأمر يقوم بالبحث عن الملف الذي يحتوي على الدالة main داخل مجلد geometry, وفي حالتنا هذه سيجد الملف geometry.go وسيقوم بعملية تجميعه وإنشاء ملف تنفيذي Executable Binary وسيضعه في مجلد bin المناظر لمجلد src

ملاحظة: في بعض الحالات قد يتم انشاء الملف التنفيذي داخل مجلد src وذلك يعود لوجود أخطاء في تعيين متغيرات البيئة Enviroment Variables الخاصة بلغة غو مثل GOPATH و GOBIN

ملاحظة: أيضاً في بعض الحالات قد لايكون الملف التنفيذي قابل للتشغيل وسبب ذلك أيضاً يعود لسوء ضبط متغيرات البيئة الخاصة بلغة غو, وخاصة المتغير GOOS والمتغير GOARCH

انتقل الان عبر محرر الأوامر الى مجلد bin وشغّل الملف التنفيذي geometry وانظر الى النتيجة …

انشاء حزمة جديد Custom Package

في الأمثلة السابقة استخدمنا حزم قياسية Standard Packages تأتي بشكل افتراضي مع لغو غو .. أما الان سننشئ حزمة خاصة بنا حيث ستحتوي على بعض وظائف المستطيلات ومايتعلق بها, وسنسمي الحزمة rectangle

هام: في لغة غو, الحزم يجب أن توضع في مجلد خاص بها, ويجب أن يكون اسم المجلد بنفس اسم الحزمة

نعود الان لمجلد geometry , ولننشئ بداخله مجلد جديد ونسمّه rectangle ويجب الانتباه هنا الى أن جميع الملفات البرمجية التي سننشئها يجب أن تتبع للحزمة rectangle وذلك نحدده بواسطة السطر الأول من كل ملف (اذا كانت الحزمة تحتوي عدة ملفات)

package rectangle

لننشئ الان ملف باسم rectprops.go داخل مجلد rectangle ونكتب فيه الكود التالي

package rectangle

import "math"

func Area(len, width float64) float64 {
    area := len * width
    return area
}

func Diagonal(len, width float64) float64 {
    diagonal := math.Sqrt((len * len) + width * width))
    return diagonal
}

في الكود أعلاه, في السطر الأول قمنا بتحديد تبعية الملف الى الحزمة rectangle وقمنا باستيراد المكتبة القياسية المسماة math حيث أنها تساعدنا في العمليات الحسابية ومن ثم أنشئنا دالتين, دالة Area لحساب مساحة المستطيل, ودالة Diagonal لحساب قطر المستطيل ونلاحظ أنه داخل دالة حساب قطر المستطيل, قمنا باستخدام الدالة Sqrt التابعة للحزمة القياسية math, ومهمة هذه الدالة هي حساب الجذر التربيعي

ملاحظة: بدأنا تسمية دالتي Area وDiagonal بأحرف كبيرة, ولهذا الأمر دلالة خاصة بلغة غو, حيث أن اذا كان اسم الدالة يبدأ بحرف كبير فهي تعتبر public وان كان بحرف صغير فهي تعتبر private, وسيتم شرح هذا الامر بالتفصيل لاحقاً.

استيراد الحزم Importing Custom Packages

لاستيراد/استخدام الحزم نستخدم التعليمة import ومن ثم “اسم الحزمة” بين علامتي اقتباس مضاعفتين Double-quotes ويجب أن نشير الى مسار الحزمة المراد استيرادها من وجهة نظر مجلد المسار GOPATH فنجد أن حزمتنا تقع في المسار geometry/rectangle

ولكننا قد نحتاج أيضاً استخدام المكتبة القياسية fmt لعرض النتائج ويمكننا استيراد عدة حزم من خلال أمر import واحد عبر الصيغة التالية

import (
    "fmt"
    "geometry/rectangle"
)

نعود الان للملف geometry.go ونكتب فيه الاتي من جديد ..

package main

import (
    "fmt"
    "geometry/rectangle"
)

func main() {
    var rectLen, rectWidth float64 = 6, 7
    fmt.Println("Geometrical shape properties")

    fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))

    fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}

ونقوم بحفظه وتجميعه Compile وتشغيله لنرى النتيجة التالية ..

Geometrical shape properties
area of rectangle 42.00
diagonal of the rectangle 9.22

مفهوم “الأسماء” المصدّرة Exported Names

في اغلب اللغات البرمجية الاخرى, يتم استخدام الكلمات المفتاحية مثل public وprivate وprotected وذلك لتحديد نطاق عمل الدوال والمتغيرات في لغة غو نستخدم طريقة اخرى وهي الأسماء المصدّرة حيث أن اسم الدالة ذا كان يبدأ بحرف كبير Capitalised , إذاً فهذه الدالة يُمكن استخدامها في عدة ملفات برمجية اخرى اذا تم استيراد المكتبات التابعة لها (كما في مثالنا السابق حيث أن الدالة Area كانت بملف rectProps.go وقمنا باستخدامها في ملف geometry.go) أما اذا كان اسم الدالة يبدأ بحرف صغير, فهذا يعني أن هذه الدالة لايمكن استدعاؤها او استخدامها إلا داخل الملف الذي كُتبت فيه وحسبنا أن ننظر الى كافة الدروس السابقة حيث استخدمنا الدالة Println التي استدعيناها من حزمة fmt, نلاحظ أنه في كل المرات كنا نكتب اسم الدالة بحرف كبير وليس حرف صغير !

ويمكنك تجربة مثلاً ان تستبدل الحرف الكبير الأول بدالة Area (الموجودة في ملف rectprops.go), أن تستبدله بحرف صغير وأن تعيد تجميع البرنامج سترى رسالة خطأ مشابهة للاتي ..

geometry.go:11: cannot refer to unexported name rectangle.area

الخلاصة, إذا أردت استدعاء دالة من مكتبة ما, يجب أن يكون اسم الدالة في المكتبة يبدأ بحرف كبير .

دالة init في الحزم

هي دالة يُمكن أن تكون في أي حزمة, وهي شبيهة بالـ Constructor في لغات البرمجة الاخرى

ومن قواعد استخدامها, ألا يكون لها أية باراميترز Parameters ألا تُرجع أية قيم Return values ألا تٌستدعى بشكل مباشر Explicit

وتُكتب بالصيغة التالية

func init() {
 // your code here
}

تٌستخدم دالة init لتقوم بالتحضير لبدأ عمل الحزمة Initialization, فمثلاً أن تتأكد من صحة القيم المدخلة أو إسناد قيم لبعض المتغيرات ومهام اخرى, قبل أن يبدأ التنفيذ .

حيث أنه عند استدعاء مكتبة, أولاً يتم تعيين قيم المتغيرات ومن ثم مباشرة يبدأ عمل دالة init (إن وُجدت), وقد تحتوي الحزمة على عدة دوال init في ملف واحد أو موزعة في عدة ملفات.

لنقوم الان ببعض التغييرات على برنامجنا السابق لنرى كيفية استخدام دالة init

نبدأ بإضافة دالة init في rectprops.go

package rectangle

import "math"
import "fmt"

/*
* init function added
*/
func init() {
    fmt.Println("rectangle package initialized")
}

func Area(len, wid float64) float64 {
    area := len * wid
    return area
}

func Diagonal(len, wid float64) float64 {
    diagonal := math.Sqrt((len * len) + (wid * wid))
    return diagonal
}

قمنا بإضافة دالة init حيث تطبع جملة “rectangle package initialised” فقط

والان لنعدل الحزمة main, نعلم مسبقاً أن طول وعرض المستطيل يجب أن يكونا أكبر من صفر, سنقوم بإضافة طريقة للتحقق من هاتين القيمتان باستخدام دالة init, وسنعيّن دالتي الطول والعرض ليكونا على مستوى الحزمة main بأكملها وليس فقط دالة main كما في السابق.

لنعدل الان ملف geometry.go

//geometry.go
package main 

import (  
    "fmt"
    "geometry/rectangle" //importing custom package
    "log"
)
/*
 * 1. package variables
*/
var rectLen, rectWidth float64 = 6, 7 

/*
*2. init function to check if length and width are greater than zero
*/
func init() {  
    println("main package initialized")
    if rectLen < 0 {
        log.Fatal("length is less than zero")
    }
    if rectWidth < 0 {
        log.Fatal("width is less than zero")
    }
}

func main() {  
    fmt.Println("Geometrical shape properties")
    fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
    fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}

قمنا بتعديلان.. الأول.. غيرنا مكان المتغيران rectLen و rectWidth من دالة main إلى خارجها, أي أصبح المتغيران على مستوى الحزمة package-level ككل وليس فقط الدالة main. الثاني.. بدالة init قمنا بإضافة تحقق اذا قيمة طول المستطيل او عرضه تساويان صفر, فسيتم إيقاف تنفيذ البرنامج وعرض رسالة خطأ وذلك باستخدام الدالة log.Fatal

الان عند تنفيذ البرنامج سنلاحظ أن النتيجة ستكون كالاتي

rectangle package initialized
main package initialized
Geometrical shape properties
area of rectangle 42.00
diagonal of the rectangle 9.22

وهذا يعني أن لغة غو, أولاً تم استيراد الحزم من import وتم تشغيل دوال الـinit الخاصة بكل حزمة وثانياً, تم تعيين قيم المتغيران rectLen وrectWidth وثالثاً, تم استدعاء الدالة init الخاصة بالملف نفسه ورابعاً, تم استدعاء الدالة main

وللترتيب هنا أهمية كبيرة.

في لغة غو, من غير القانوني أن تستدعي حزمة دون أن تستخدمها في مكان ما في برنامجك, وسبب ذلك هو للتقليل من الأكواد المبعثرة غير المفيدة وبالتالي ذلك سيفيدنا في سرعة تجميع البرنامج Compilation-time


شاهد أيضاً

comments powered by Disqus