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

يُعد بناء تجريدات abstractions لتفاصيل البرنامج الخاص بك أعظم أداة يمكن أن تقدمها لغة البرمجة للمطور، إذ تسمح البُنى structs لمطوري لغة جو بوصف العالم الذي يعمل فيه البرنامج؛ فبدلًا من استخدام السلاسل النصية strings لوصف أشياء، مثل الشارع Street والمدينة City والرمز البريدي PostalCode، يمكن استخدام بنية تجمع كل هذه الأشياء تحت مفهوم "العنوان Address".

يمكن تعريف البنية على أنها هيكل بيانات يُستخدم لتعريف نوع بيانات جديد يحتوي مجموعةً محددةً من القيم مختلفة النوع، ويمكن الوصول لهذه العناصر أو القيم عن طريق اسمها. تساعد البنى المطورين المستقبليين (بما في ذلك نحن) بتحديد البيانات المهمة للبرامج الخاصة بنا وكيف يجب أن تستخدم الشيفرات المستقبلية هذه البيانات بالطريقة الصحيحة.

يمكن تعريف البنى واستخدامها بعدة طرق مختلفة. سنلقي نظرةً في هذا المقال على كل من هذه التقنيات.

تعريف البنى

يمكنك أن تتخيل البنى مثل نماذج جوجل التي يُطلب منك ملؤها أحيانًا. تتضمّن هذه النماذج حقولًا يُطلب منك تعبئتها، مثل الاسم، أو العنوان، أو البريد الإلكتروني، أو مربعات فارغة يمكن تحديد إحداها لوصف حالتك الزوجية (أعزب، متزوج، أرمل) …إلخ. يمكن أن تتضمن البُنى أيضًا حقولًا يمكنك تعبئتها. تهيئة متغير ببنية جديدة، أشبه باستخراج نسخة من نموذج جاهز للتعبئة.

لإنشاء بنية جديدة يجب أولاً إعطاء لغة جو مُخططًا يصف الحقول التي تحتوي عليها البنية. يبدأ تعريف البنية بالكلمة المفتاحية type متبوعًا باسم البنية الذي تختاره Creature ثم الكلمة struct ثم قوسين {} نضع بداخلهما الحقول التي نريدها في البنية. يمكنك استخدام البنية بعد الانتهاء من التصريح عنها مع المتغيرات كما لو أنها نوع بيانات بحد ذاته.

package main

import "fmt"

type Creature struct {
    Name string
}

func main() {
    c := Creature{
        Name: "Sammy the Shark",
    }
    fmt.Println(c.Name)
}

ستحصل عند تشغيل البرنامج على الخرج التالي:

Sammy the Shark

صرّحنا في هذا المثال أولًا عن بنية اسمها Creature تتضمّن حقلًا اسمه Name من نوع سلسلة نصية string. نُعرّف داخل الدالة main مُتغيرًا c من النوع Creature ونهيئ الحقل Name فيه بالقيمة "Sammy the Shark"، إذ نفتح قوسين {} ونضع هذه المعلومات بينهما كما في الشيفرة أعلاه. أخيرًا استدعينا الدالة fmt.Println وطبعنا من خلالها الحقل Name من خلال المتغير c وذلك عن طريق وضع اسم المتغير متبوعًا بنقطة . ومتبوعًا باسم الحقل، مثل c.Name، الذي يعيد في هذه الحالة حقل Name.

عندما نأخذ نسخةً من بنية، نذكر غالبًا اسم كل حقل ونسند له قيمة (كما فعلنا في المثال السابق). عمومًا، إذا كنت ستؤمن قيمة كل حقل أثناء تعريف نسخة من بنية فيمكنك عندها تجاهل كتابة أسماء الحقول، كما يلي:

package main

import "fmt"

type Creature struct {
    Name string
    Type string
}

func main() {
    c := Creature{"Sammy", "Shark"}
    fmt.Println(c.Name, "the", c.Type)
}

وسيكون الخرج على النحو التالي:

Sammy the Shark

أضفنا حقلًا جديدًا للبنية Creature باسم Type وحددنا نوعه string. أنشأنا داخل الدالة main نسخةً من هذه البنية وهيّأنا قيم الحقلين Name و Type بالقيمتين "Sammy" و "shark" على التوالي من خلال التعليمة:

Creature{"Sammy", "Shark"}

واستغنينا عن كتابة أسماء الحقول صراحةً.

نُسمي هذه الطريقة بالطريقة المُختصرة للتصريح عن نسخة من بنية، وهذه الطريقة غير شائعة كثيرًا لأنها تتضمن عيوبًا مثل ضرورة تحديد قيم لجميع الحقول وبالترتيب وعدم نسيان أي حقل. نستنتج سريعًا أن استخدام هذه الطريقة لا يكون جيدًا عندما يكون لدينا عددٌ كبيرٌ من الحقول لأننا سنكون عُرضةً للخطأ والنسيان والتشتت عندما نقرأ الشيفرة مرةً أخرى. إذًا، يُفضّل استخدام هذه الطريقة فقط عندما يكون عدد الحقول قليل.

ربما لاحظت أننا نبدأ أسماء جميع الحقول بحرف كبير، وهذا مهم جدًا لأنه يلعب دورًا في تحديد إمكانية الوصول إلى هذه الحقول؛ فعندما نبدأ اسم الحقل بحرف كبير، فهذا يعني إمكانية الوصول إليه من خارج الحزمة، أما الحرف الصغير فلا يمكن الوصول إليها من خارج الحزمة.

تصدير حقول البنية

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

package main

import "fmt"

type Creature struct {
    Name string
    Type string

    password string
}

func main() {
    c := Creature{
        Name: "Sammy",
        Type: "Shark",

        password: "secret",
    }
    fmt.Println(c.Name, "the", c.Type)
    fmt.Println("Password is", c.password)
}

وسيكون الخرج على النحو التالي:

Sammy the Shark
Password is secret

أضفنا إلى البنية السابقة حقلًا جديدًا secret، وهو حقل من النوع string ويبدأ بحرف صغير؛ أي أنه غير مُصدّر، وأي حزمة أخرى تحاول إنشاء نسخة من هذه البنية Creature لن تتمكن من الوصول إلى حقل secret. عمومًا، يمكننا الوصول إلى هذا الحقل ضمن نطاق الحزمة، لذا إذا حاولنا الوصول إلى هذا الحقل من داخل الدالة main والتي بدورها موجودة ضمن نفس الحزمة بالتأكيد، فيمكننا الرجوع لهذا الحقل c.password والحصول على القيمة المُخزنة فيه. وجود حقول غير مُصدّرة أمر شائع في البنى مع إمكانية وصول بواسطة توابع مُصدّرة exported.

البنى المضمنة Inline

تُسمى أيضًا البنى السريعة. تُمكّنك لغة جو من تعريف بُنى في أي وقت تريده وفي أي مكان ودون الحاجة للتصريح عنها على أنها نوع بيانات جديد بحد ذاته، وهذا مفيد في الحالات التي تكون فيها بحاجة إلى استخدام بنية مرةً واحدةً فقط (أي لن تحتاج إلى إنشاء أكثر من نسخة)، فمثلًا، تستخدم الاختبارات غالبًا بنيةً لتعريف جميع المعاملات التي تُشكل حالة اختبار معينة. سيكون ابتكار أسماء جديدة مثل CreatureNamePrintingTestCase مرهقًا لدى استخدام هذه البنية في مكان واحد فقط.

يكون كتابة البنى المُضمّنة بنفس طريقة كتابة البنى العادية تقريبًا، إذ نكتب الكلمة المفتاحية struct متبوعةً بقوسين {} نضع بينهما الحقول وعلى يمين اسم المتغير. يجب أيضًا وضع قيم لهذه الحقول مباشرةً من خلال استخدام قوسين آخرين {} كما هو موضح في الشيفرة التالية:

package main

import "fmt"

func main() {
    c := struct {
        Name string
        Type string
    }{
        Name: "Sammy",
        Type: "Shark",
    }
    fmt.Println(c.Name, "the", c.Type)
}

ويكون الخرج كما يلي:

Sammy the Shark

لاحظ هنا أننا لم نحتاج إلى تعريف نوع بيانات جديد لتمثيل البنية، وبالتالي لم نكتب الكلمة المفتاحية type، فكل ما نحتاجه هنا هو استخدام الكلمة المفتاحية struct إشارةً إلى البنية، وإلى معامل الإسناد القصير =:. نحتاج أيضًا إلى تعريف قيم الحقول مباشرةً كما فعلنا في الشيفرة أعلاه. الآن أصبح لدينا متغيرًا اسمه c يُمثّل بنيةً يمكن الوصول لحقولها من خلال النقطة . كما هو معتاد. سترى البنى المُضمّنة غالبًا في الاختبارات، إذ تُعرّف البنى الفردية بصورةٍ متكررة لاحتواء البيانات والتوقعات لحالة اختبار معينة.

خاتمة

البُنى هي كتل بيانات غير متجانسة (أي أنها تضم حقولًا أو عناصر من أنواع بيانات مختلفة) يعرّفها المبرمجون لتنظيم المعلومات. تتعامل معظم البرامج مع أحجام هائلة من البيانات، وبدون البنى سيكون من الصعب تذكر أي من المتغيرات ترتبط معًا وأيّها غير مرتبطة أو أيّها من نوع string وأيها من نوع int. لذلك إذا كنت تتعامل مع مجموعة من المتغيرات، اسأل نفسك عما إذا كان تجميعها ضمن بنية سيكون أفضل، إذ من الممكن أن تصف هذه المتغيرات مفهومًا عالي المستوى، فيمكن مثلًا أن يشير أحد المتغيرات إلى عنوان شركة حسوب وهناك متغيّر آخر يخص عنوان شركة أُخرى.

ترجمة -وبتصرف- للمقال Defining Structs in Go لصاحبه Gopher Guides.

اقرأ أيضًا


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

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

لا توجد أية تعليقات بعد



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.


×
×
  • أضف...