اذهب إلى المحتوى

كيف تبدو البرمجة بلغة Go؟ وكيف أقوم بتشغيل برنامج كُتب بـ Go؟ هذا ما سنتعرف عليه خلال هذا الدرس من سلسلة مدخل إلى لغة البرمجة Go، وقبل متابعة الدرس، أحب أن أتأكد أنك تابعت الدرسين السابقين منها أولا.

golang-3.thumb.png.5b688518dfa2ec34b351f

أهلا بالعالم

سنحاول اتباع العادة التي جرت عليها دروس تعلم لغات البرمجة، وهي ببساطة كتابة جملة "أهلا بالعالم" (بالانجليزية "Hello World") باللغة المُراد تعلمها، ببساطة هذا ما سيبدو عليه برنامج أهلا بالعالم في Go:

package main

import "fmt"

func main() {
    fmt.Print("أهلا بالعالم\n")
}

ضع هذا في ملف باسم helloworld.go داخل مجلد باسم hellogo مثلا، تحت مسار:

$GOPATH/src/github.com/YOUR_USER/

بالتالي سيكون المسار النهائي للملف الجديد الذي أنشأناه هو:

$GOPATH/src/github.com/YOUR_USER/hellogo/helloworld.go

كل ما عليك فعله الآن هو التنقل إلى مسار الملف وكتابة أمر go run helloworld.go

$ go run helloworld.go
أهلا بالعالم

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

لنشرح البرنامج سطرًا بسطر:

package main

ببساطة:

  • لابد لكل ملف شيفرة برمجية في Go أن ينتمي إلى حزمة (package) ما.
  • ولابد لكل حزمة في Go أن تنتمي إلى مجلد ما.
  • لا يمكن لحزمتين التواجد على مستوى نفس المجلد، لكن يمكن لعدة ملفات أن تنتمي إلى نفس الحزمة (يعني مجلد به عدة ملفات)، كما يمكن أن تتواجد حزمة داخل حزمة أخرى لكن كلٌ في مجلد فرعي على حِدى.

في مثالنا السابق، أعطينا main كاسم لحزمتنا، وهو اسم خاص، حيث يُعامل مُجمّع (compiler) لغة Go هذه الحزمة على أنها مدخل البرنامج (program entry)، أي أن التعليمات الموجودة في هذه الحزمة يتم تشغيلها أولا. أسبقنا اسم الحزمة بالكملة المفتاحية package.

import "fmt"
  • import: كلمة مفتاحية أخرى وتعني "استرِد" أو "اجلِب" المكتبة الفلانية أو الحزمة الفلانية.
  • "fmt" اختصار لـ format أو formatting وهي مكتبة قياسية (standard library) تأتي مع تنصيب Go، خاصة ببناء وطباعة النص. لاحظ أن اسم المكتبات أو الحزم المُراد استيرادها دائما ما يتم إحاطتها بعلامة اقتباس "".
func main() {

هنا قمنا بإنشاء دالة تحمل اسم main:

  • لإنشاء الدالة استعملنا الكملة المفتاحية func ثم مسافة، ثم اسم الدالة.
  • مثل حزمة main فإن دالة main أيضا يعاملها المجمع (Compiler) معاملة خاصة، حيث تعتبر هي نقطة دخول البرنامج (Entry point)، أي أن التعليمات الموجودة في هذه الدالة تحديدا، داخل حزمة main تحديدًا، يتم تشغيلها أولا، إلا في حالة وجود دالة أخرى باسم ()init والتي سنتعرض لها في دروس أخرى.
  • مثل جافاسكريبت وباقي اللغات، فإن } يفتح جسم الدالة (function body).
fmt.Print("أهلا بالعالم\n")
  • هنا قمنا باستعمال حزمة "fmt" التي قمنا باستيرادها أعلاه، لاستعمال أي حزمة في Go يكفي كتابة اسمها، ثم نقطة، ثم اسم الدالة التي تريدها استعمالها من تلك الحزمة، في حالتنا هذه، أردنا ببساطة طباعة نص "أهلا بالعالم" والرجوع إلى السطر، لذلك استعملنا دالة Print ومررلنا لها القيمة "n\أهلا بالعالم":
  • مررنا القيمة بين علامتي اقتباس "" ﻷنها عبارة عن سلسلة نصية (string).
  • أنهينا سلسلتنا النصية بعلامة n\ وهي علامة خاصة في أغلب لغات البرمجة، وتعني "سطر جديد"، استعملناها للرجوع إلى السطر.
  • نُمرّر القيم والمعاملات للداوال عبر وضعها بين قوسين مباشرة بجنب اسم الدالة.
}
  • أغلقنا جسم الدالة main باستعمال علامة {، أي أن تعليمات هذه الدالة قد انتهت.
  • لاحظ غياب علامة الفاصلة المنقوطة ";" بعد نهاية كل تعليمة، لا حاجة لها في Go.

مفهوم الحزم (Packages) في Go

الحزم (packages) طريقة بسيطة لتجزئة برنامجك إلى أجزاء أصغر مُقسّمة حسب الغرض أو الوظيفة، يُمكن الاشارة للحزم على أنهم "مكتبات" (libraries) أو وحدات (modules)، تركيب الحزم مع بعضها يشكل مُجمل برنامجك.

تتبع Go القواعد التالية في كيفية تقسيم برنامج إلى حزم:

  • الحزمة غالبا عبارة عن مجلد داخل مشروعك به الملفات التي تحمل اسم الحزمة.
  • لابد من وجود حزمة واحدة على الأقل في أي برنامج أو مكتبة.
  • إذا كان البرنامج عبارة عن برنامج تنفيذي (وليس مكتبة - library) فإنه يجب وجود حزمة باسم main (أي package main) تكون هي مدخل البرنامج كما رأيناه في مثالنا الأول.

وكتذكير لما ذكرناه سابقا:

  • لابد لكل ملف شفرة برمجية في Go أن ينتمي إلى حزمة (package) ما.
  • ولابد لكل حزمة في Go أن تنتمي إلى مجلد ما.
  • لا يمكن لحزمتين التواجد على مستوى نفس المجلد، لكن يمكن لعدة ملفات أن تنتمي إلى نفس الحزمة (يعني مجلد به عدة ملفات).
  • يمكن أن تتواجد حزمة داخل حزمة أخرى لكن كلٌ في مجلد فرعي على حِدى.
  • يمكن للمجلد الرئيسي لمشروعك أن يحتوي على حزمة ما، واحدة فقط، باقي الحزم الخاصة به يمكنها أن تتواجد في مجلدات فرعية.
  • غياب حزمة main من برنامج ما، يجعل منه مكتبة فقط وليس برنامجا تشغيليا قائما بحد ذاته.

عند كتابة الحزم، أي اسم دالة أو متغير أو بُنية (Struct) يبدأ بحرف كبير (Uppercase) يعني أن هذه الدالة/المتغير/البنية متوفر بشكل عام لجميع من يقوم باستيراد هذه الحزمة عبر الكلمة المفتاحية import. وأي اسم دالة/متغير يبدأ بحالة أحرف صغيرة (Lowercase) يعني أنه خاص ولن يتم تصديره (لن يكون متاحا) لباقي الحزم والبرامج.

في مثالنا السابق، وفّرت حزمة "fmt" دالة Print لنا بشكل مُتاح، لاحظ أن Print تبدأ بحرف P كبير (Uppercase). وكان فقط يكفي كتابة (...)fmt.Print

مكتبة (حزمة) أهلا بالعالم

ماذا لو أردنا قول "أهلا بالعالم" في أكثر من مشروع؟ ربما سيكون من الأمثل جعل طباعة "أهلا بالعالم" في مكتبة مستقلة نقوم باستيرادها في مشاريع أخرى، هذا مثال فقط لكيفية بناء مكتبة في Go، فطبعا لن تحتاج إلى مكتبة كل ما تقوم به هو "أهلا بالعالم".

دعنا نحاول كتابة هذا في دالة بسيطة، نسميها SayHello ولنضها في حزمة خاصة، نسميها sayhello.

دعنا نبني هذه المكتبة في مشروع مستقل، لتكون مكتبة يُمكن جلبها في أكثر من برنامج أو مشروع آخر. سنسمي المشروع sayhello، أي بنفس اسم الحزمة لتسهيل الأمر. لن نحتاج إلى package main ﻷننا سنبني مكتبة فقط وليس برنامجا تنفيذيا في حد ذاته.

بالتالي قم بإنشاء مجلد مشروع جديد تحت مسار:

$GOPATH/src/github.com/YOUR_USER/

بنفس الطريقة التي شرحناها سابقا.

أنشئ ملفا باسم sayhello.go تحت مجلد sayhello، ليكون مسار الملف أشبه بهذا:

$GOPATH/src/github.com/YOUR_USER/sayhelo/sayhello.go

والآن، نفتح ملف sayhello.go بمحرر النصوص المفضل لديك ثم نشرع في كتابة دالتنا المذهلة، لا يُفترض بك فهم كل شيء حاليا، فالغرض شرح مفهوم الحزم والمكتبات، مع ذلك سنحاول شرح تعليمات هذه الدالة:

package sayhello

// SayHello returns Hello World in Arabic.
func SayHello() string {
    return "أهلا بالعالم\n"
}

لاحظ أن دالتنا لا تختلف كثيرًا عن برنامج "أهلا بالعالم" الذي كتبناه أول مرة:

  • استعملنا package sayhello عوض package main.
  • سمينا الدالة ()func SayHello عوض ()func main. لاحظ أننا جعلنا اسم الدالة يبدأ بحرف كبير (Uppercase) ذلك ﻷننا نريد تصدير هذه الدالة وإتاحتها لباقي المشاريع التي تسترد حزمة sayhello.
  • قمنا بتصريح أن الدالة تُرجع سلسلة نصية عبر كتابة الكلمة المفتاحية string على يمين الدالة.
  • قمنا بإرجاع السلسلة النصية "n\أهلا بالعالم" عبر الكلمة المفتاحية return عوض طباعتها باستخدام مكتبة fmt كما فعلنا أول مرة.

أصبح الآن لدينا "أهلا بالعالم" في شكل مكتبة! لكن كيف نجعلها قابلة للجلب؟ أي كيف نضعها في مجلد pkg حتى تستطيع باقي المشاريع جلبها والاستفادة منها؟

كل ما علينا فعله هو كتابة go install داخل مجلد مشروع sayhello

طبعا لن يكون بإمكانك تشغيل هذه المكتبة عبر أمر go run sayhello.go مثل ما فعلنا أو مرّة، ﻷن ما قمنا بكتابته هذه المرة عبارة عن مكتبة وليس برنامجًا تنفيذيًا!

فحص مكتبة "أهلا بالعالم"

هل تعمل دالتنا بشكل جيد؟ كيف يمكن التحقق من برامجنا وحزمنا التي نكتبها في Go؟ هل مكتبة "أهلا بالعالم" تطبع حقا "أهلا بالعالم"؟ دعنا نتأكد من ذلك عبر كتابة فحص مؤتمت لهذه الدالة!

في لغة Go:

  • يمكن كتابة فحوصات واختبار وحدات (Unit testing) بسهولة، يكفي إنشاء ملف بنفس اسم الملف المُراد اختباره مع إضافة test_ له.
  • يمكن فحص دالة معينة بمجرد كتابة Test ثم اسم الدالة.

في حالتنا هذه، وفي نفس مسار مشروع sayhello أنشئ ملفا باسم sayhello_test.go بجانب ملف sayhello.go نفسه لنقوم باختبار حزمة sayhello.

افتح ملف sayhello_test.go بمحرر النصوص المفضل لديك. كل ما علينا فعله لتجربة دالتنا هو جلبها وفحص إذا كانت نتيجة الطباعة هي فعلا "أهلا بالعالم"! كما هو موضح في الشفرة التالية:

package sayhello

import "testing"

func TestSayHello(t *testing.T) {
    greeting := SayHello()
    if greeting != "أهلا بالعالم\n" {
        t.Error("TEST FAILED!")
    }
}

في الشِيفرة أعلاه:

  • قمنا باستيراد حزمة "testing" القياسية التي توفرها لغة Go بشكل افتراضي لأغراض إنشاء الفحوصات المؤتمتة.
  • بما أننا نريد فحص دالة SayHello قمنا بإنشاء دالة TestSayHello مع تمرير testing.T لها، أي أن هذه الدالة تُعتبر "حالة فحص"، لا يجب عليك فهم هذا التعبير حاليا، يمكنك تجاهله، الغرض هو فهم آلية عمل الفحوصات المؤتمتة في Go.

داخل هذه الدالة الفاحصة، قمنا ببساطة بنداء دالتنا SayHello وفحص إذا كانت القيمة التي تُرجعها عبارة عن "n\أهلا بالعالم" فإن كانت كذلك، يمر الفحص بسلام ولن نطبع أي شيء، وإن لم تكن كذلك، نطبع "!TEST FAILED" باستخدام المعامل t التابع لحزمة testing القياسية باستعمال الدالة Error، والتي تُخبر بدورها أن حالة الفحص هذه قد فشلت.

الآن، في نفس المجلد الذي أنت به يكفي كتابة go test لفحص حزمة sayhello الخاص بنا! يُفترض أن تتلقى نتيجة مشابهة لهذه:

PASS
ok  	github.com/01walid/sayhello	0.001s

يمكن إضافة v- أو cover-- إلى الأمر السابق لطباعة المزيد من المعلومات حول الفحوصات التي يقوم بها المُجمع، مثلا go test -v --cover

=== RUN   TestSayHello
--- PASS: TestSayHello (0.00s)
PASS
coverage: 100.0% of statements
ok  	github.com/01walid/sayhello	0.001s

لاحظ أننا تحصلنا على coverage: 100.0%! أي أن الفحوص التي قمنا بها تغطي جميع الدوال التي قمنا بكتابتها في هذا المشروع (بما أن لدينا دالة واحدة فقط وفحص واحد، فالنتيجة طبيعية)، من المستحسن دائما إبقاء نسبة تغطية الفحوصات مرتفعة في برامجك، حتى تضمن أن استقرارها جيد ويمكن التعويل عليها.

إنشاء توثيق لمكتبة "أهلا بالعالم"

أنهينا مكتبتنا المذهلة، تأكدنا من صحة عملها، ماذا لو أمكننا توليد ومشاركة توثيق لها؟ لن تحتاج الكثير لفعل ذلك!

كل ما عليك فعله هو كتابة الأمر  godoc -http=:6060 ثم زيارة localhost:6060 على متصفحك! ستحتاج إلى التنقل إلى توثيق مكتبتك بشكل خاص عبر زيادة 

http://localhost:6060/pkg/github.com/YOUR_USER/sayhello/

طبعا قم بتغيير YOUR_USER إلى اسم المستخدم الخاص بك، تماما مثل مسار المكتبة في بيئة GOPATH$ الخاص بك.

استعمال مكتبة "أهلا بالعالم"

دعنا نستعمل هذه المكتبة في برنامجنا الأول، سنحتاج إلى تغييرات بسيطة لجعله يستغل مكتبة "أهلا بالعالم" كما هو موضح (تذكر: مشروعنا الأول في مجلد لوحده باسم sayhello):

package main

import "fmt"
import "github.com/01walid/sayhello"


func main() {
   fmt.Print(sayhello.SayHello())
}

قمنا فقط بجلب مكتبتنا الجديدة عبر كتابة:

import "github.com/YOUR_USER/sayhello"

ثم استعمالها عبر مناداة دالة SayHello منها داخل fmt.Print عوض كتابة "أهلا بالعالم" مباشرة:

   fmt.Print(sayhello.SayHello())

الآن شغّل البرنامج مجددا عبر كتابة go run helloworld.go، هذا كل ما في الأمر!

تهانينا لك أول برنامج وأول مكتبة لك باستخدام لغة البرمجة Go!

إضافة (Bonus)

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

ستجد من هنا قائمة بهذه الإضافات، مثلا إن كنت من مستعملي SublimeText فقم بتنصيب إضافة GoSublime وإن كنت من مستعملي محرر Atom فقط بتنصيب Go-plus.

خاتمة

تعرفنا في هذا الدرس على كيفية كتابة برنامج باستخدام لغة Go، ثم جعل البرنامج على شكل مكتبة عوض تركه في شكله التنفيذي.

من المهم جعل برامجك عبارة عن مجموعة مكتبات منفصلة حسب الغرض، عوض جعل كل شيء متداخل في حزمة واحد أو في برنامج تنفيذي. فكما رأينا إنشاء برنامج تنفيذي أسهل ما يكون، إذ يكفي كتابة ملف به package main ثم func main واستدعاء باقي الحزم، بالتالي الأهم من ذلك هو التفكير التجريدي وفصل المهام عن بعضها في مكتبات وحزم حسب الغرض، كلٌ يقوم بمهمة/غرض ما، ويقوم به بشكل جيد ومُختبر (unit tests).

في درسنا هذا، كانت المكتبة منفصلة تماما عن البرنامج التنفيذي. مشاريعك القادمة ستكون على الأغلب مقسمة إلى حزم/مكتبات داخل مجلد المشروع نفسه وليس بالضرورة في مجلد/مشروع آخر، لكن المفهوم هو نفسه.

رأينا أيضا، أن التعليقات في Go وسيلة ممتازة لتوليد التوثيق بأدنى جهد، لذلك يجدر بك كمبرمج عدم إهمالها والاهتمام بها وبتفاصيلها.

كل هذا كان سهلا! لعلك كنت تسمع بهذه الأمور فتفزع (tests, docs, cover.. الخ) لكني قدمت هذه المفاهيم عمدا في الدروس الأولى لترى أن لا شيء يستحق الهروب منه. وأن البرامج ذات الجودة العالية مجرد اتباع لسلوكيات مهذبة وممارسات محبذة بخطوات ثابتة ينبغي أن تُعوّد نفسك عليها من الآن وتجعل منها أسلوب تطوير/برمجة.

في الدروس القادمة سنتطرق إلى أنواع المتغيرات، هيكلة البيانات، كيفية إنشاء متغير، الحلقات والجمل الشرطية.

تم التعديل في بواسطة وليد زيوش


تفاعل الأعضاء

أفضل التعليقات



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...