مساحة اعلانية

آخر المواضيع

تعلم Swift من الصفر: مقدمة عن الأصناف والبنى

تعلم Swift من الصفر: مقدمة عن الأصناف و البنى
تعلم Swift من الصفر: مقدمة عن الأصناف و البنى

في الدروس السابقة التابعة لهذه السلسلة، قمنا بتغطية أساسيات لغة البرمجة Swift. إذا كنت قد تابعت معنا، فيجب أن يكون لديك الآن فهماً جيداً لكل من المتغيرات، الثوابت، الدوال والإغلاقات. حان الوقت الآن لتستخدم ما تعلمته حتى الآن وتطبق تلك المعرفة لمفاهيم كائنية التوجيه المتاحة في Swift



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


1. المقدمة

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

في Objective-C، تعد الأصناف والبنى مختلفة جداً. ولكن هذا ليس صحيحاً بالنسبة لـ: Swift ففي Swift، على سبيل المثال، يمكن لكلا الأصناف والبنى أن يكون لديهما خصائص وطرق. على عكس بنى C ، فإن البنى في Swift يمكن أن تمدد وتتبع بروتوكولات.

والسؤال هو "ما هو الإختلاف بين الأصناف والبنى؟" سنرجع لهذا السؤال لاحقاً في هذا الدرس. دعونا أولاً نستطلع كيف يبدو الصنف في Swift.


2. المصطلحات

قبل البدء بالعمل مع الأصناف والبنى، أرغب بتوضيح بعض المصطلحات المستخدمة في البرمجة كائنية التوجه. غالباً ما يحير الناس الجدد على البرمجة كائنية التوجه مصطلحات كالأصناف والكائنات والنماذج و لذلك يجب أن تعرف كيف يستخدم Swift هذه المصطلحات.

الكائنات والنماذج

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

الطرق والدوال

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

3. تعريف الصنف

دعونا نبدأ بتعريف الصنف. قم بإطلاق Xcode وابتكار ملعباً جديداً. قم بإزالة محتويات الملعب وأضف تعريف الصنف التالي.

class Person {  

}

تشير كلمة class الأساسية إلى أننا نعرف صنف يدعى Person (شخص). تطبيق الصنف ملفوف بزوجين من الأقواس المعوجة. على الرغم من أن صنف person غير مفيد جداً بشكله الحالي، إلا أنه صنف Swift مناسب وفعال.


الخصائص

وكما في معظم لغات البرمجة كائنية التوجه، فإنه يمكن للصنف أن يكون لديه خصائص وطرق. في المثال المحدث أسفلاً، سنقوم بتعريف ثلاثة خصائص:

  • firstName: خاصية  متاحة من نوع ؟String
  • lastName: خاصية  متاحة من نوع String?
  • gender: خاصية ثابتة من نوع String

class Person {
    var firstName: String?
    var lastName: String?
    let gender = "female"}

كما يوضح المثال، فإن تعريف الخصائص في تعريف الصنف يشبه إلى حد كبير تعريف المتغيرات والثوابت النظامية. نقوم باستخدام الكلمة الأساسية var لتعريف خاصية متغير ما والكلمة الأساسية let لتعريف خاصية ثابت ما .

تعرف الخصائص التي بالأعلى بالخصائص stored (المخزنة). لاحقاً في هذه السلسلة، سنتعلم عن computed (المحسوبة). وكما يوحي الاسم، فإن الخصائص المخزنة هي عبارة عن خصائص تخزن من قبل نموذج الصنف. وهذه الخصائص تشبه إلى حد كبير تلك الموجودة في Objective-C

من الضروري ملاحظته أن كل خاصية مخزنة تحتاج إلى قيمة بعد عملية البدء initialization أو بعد أن يتم تعريفها على أنها نوع إختياري.  في المثال الذي في الأعلى، سنعطي خاصية gender القيمة البدائية "female". وهذا سيخبر Swift أن خاصية gender تنتمي إلى نوع String. لاحقاً في ها الدرس، سنلق نظرة على initialization بشكل أعمق وسنستطلع كيف يرتبط مع الخصائص البدائية.

على الرغم من أننا نعرف خاصية gender كثابت، فإنه من الممكن أن تتغير قيمتها خلال عملية initialization للنموذج Person حالما تتم عملية بدء النموذج، فلا يمكن حينها تعديل خاصية gender ذلك لأننا قمنا بتعريف الخاصية على أنها ثابت من خلال استخدام الكلمة الأساسية let. سيصبح ذلك أوضح لاحقاً في هذا الدرس عندما نناقش عملية initialization.


الطرق

يمكننا إضافة سلوك أو وظيفة لصنف ما من خلال الدوال والطرق. في عدة لغات برمجة، يستخدم مصطلح الطريقة عوضاً عن الدالة في مجال الأصناف والنماذج. يعد تعريف الطريقة تقريباً مطابقاً لتعريف الدالة.  في المثال التالي، سنقوم بتعريف طريقة fullName في الصنف Person.
   
class Person {

    var firstName: String?

    var lastName: String?

    let gender = "female"

    func fullName() -> String {

        var parts: [String] = []

        if let firstName = self.firstName {

            parts += [firstName]

        }

        if let lastName = self.lastName {

            parts += [lastName]

        }

        return " ".join(parts)

    }

}

يتم إدخال طريقة fullName في تعريف الصنف. أنها لا تقبل أية معاملات وتعود كـ: String يعد تطبيق طريقة fullName واضحاً. من خلال الربط الإختياري، الذي ناقشناه مسبقاً في هذه السلسلة، يمكننا الولوج إلى القيم المخزنة في خصائص firstName و lastName .

نقوم بتخزين الاسم الأول والأخير للنموذج Person في مصفوفة ونضم الأجزاء من خلال إضافة فراغ. سبب هذا التطبيق الغريب بعض الشيء يجب أن يكون واضحاً، حيث أنه يمكن للاسم الأول والأخير أن يكونا فارغين، ولهذا السبب كلا الخصائص ينتميان لنوع String


التحويل

لقد قمنا بتعريف الصنف مع بعض الخصائص وطريقته. كيف يمكننا أن نبتكر نموذج لصنف Person؟ إذا كنت على معرفة بـ: Objective-C، فإنك ستحب إيجاز المقطع التالي.

let john = Person()

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

في مثالنا، يشير الثابت john إلى نموذج من الصنف Person هل يعني هذا أنه بإمكاننا تغيير أي من خصائصه؟ سيجيب المثال التالي عن هذا السؤال.

 
john.firstName = "John"

john.lastName = "Doe"

john.gender = "male"

يمكننا الولوج إلى خصائص أية نموذج من خلال استخدام تركيبة كود النقطة. في المثال، قمنا بتعيين firstName  لـ: "John" ،  lastName لـ: "Doe"، و gender لـ: "male". قبل أن نصل إلى أية نتائج مرتكزة على هذا المثال، يجب علينا التحقق من عدم وجود أية أخطاء في هذا الملعب.

تعلم Swift من الصفر: مقدمة عن الأصناف و البنى
تعلم Swift من الصفر: مقدمة عن الأصناف و البنى

لا يبدو أن تعيين كلا من firstName و lastName يسبب أية مشكلة. ولكن تعيين "male" لخاصية gender نتج عنه خطأ. شرح ذلك بسيط. على الرغم من أنه تم التصريح عن john على أنه ثابت، إلا أن ذلك لا يمنعنا من تعديل نموذج Person. هنالك تحذير واحد فقط على الرغم من ذلك، يمكن تعديل الخصائص المتغيرة فقط بعد بدء النموذج. ولكن الخصائص المعرفة على أنها ثوابت لا يمكن تعديلها بعد عملية البدء.

لاحظ إنني أكدت على كلمة بعد في الجملة السابقة. يمكن للخصائص الثابتة أن تعرف خلال عملية بدء النموذج. في حين أنه لا يمكن تغيير خاصية gender حالما يتم إبتكار نموذج Person فإن الصنف لن يكون مفيداً جداً إذا أمكننا فقط تحويل نماذج أنثى Person. دعونا نجعل صنف Person مرنا أكثر بقليل.


البدء (Initialization)

تعتبر عملية البدء خطوة هامة طيلة حياة نموذج صنف أو بنية. خلال عملية البدء، نقوم بتحضير النموذج للإستخدام من خلال إضافة قيمة بدائية لخصائصه. يمكن أن يتم تعديل عملية بدء نموذج من خلال تطبيق initializer وهو نوع خاص من الطرق. دعونا نضيف initializer للصنف Person


class Person {
    var firstName: String?
    var lastName: String?
    let gender = "female"
    init() {
        gender = "male"
    }}

لاحظ أن اسم initializer) init) لا يسبق بالكلمة الأساسية func. على عكس initializers المستخدمة في Objective-C، فإن initializer المستخدم في Swift لا يرجع النموذج الذي يتم بدأه.

هناك تفصيل آخر مهم وهو كيفية تعيين قيمة بدائية لخاصية gender. على الرغم من أن خاصية gender معرفة كخاصية ثابتة، إلا أنه يمكن أن نعين قيمتها في initializer. نقوم بتعيين خاصية gender من خلال استخدام اسم الخاصية، ولكن يمكنك أيضاً أن تكون أكثر وضوحاً وتكتب كالتالي:



init() {
    self.gender = "male"}

في المثال الذي في الأعلى، تشير self إلى النموذج الذي يتم بدأه. هذا يعني أن self.gender تشير إلى خاصية gender التابع للنموذج. يمكننا حذف self، كما هو الحال في المثال الأول، ذلك لأنه ليس هنالك حيرة فيما تعلق بالخاصية التي نشير إليها. ومع ذلك هذه ليست هي الحال دائماً. دعوني أشرح ما أعنيه بذلك.

المعاملات

في عدة حالات، ترغب بتمرير القيمة البدائية للـ: initializer من أجل تعديل النموذج الذي يتم تحويله. وهذا يعتبر ممكننا من خلال إبتكار custom initializer يقبل معاملة أو أكثر. في المثال التالي سنبتكر custom initializer يقبل معاملة واحدة (gender) من نوع String


init(gender: String) {
    self.gender = gender}

هنالك شيئان يجب أن تلاحظهما. أولاً، يجب علينا أن نصل إلى خاصية gender من خلال self.gender من أجل تفادي الغموض بما أن اسم المعاملة المحلية مساوياً للـ: gender ثانياً، على الرغم من أننا لم نحدد أسماً للمعاملة الخارجية، فإن Swift من خلال الافتراضي (default) يبتكر أسماً للمعاملة الخارجية ويكون مساوياً لاسم المعاملة المحلية. وبذلك تكون النتيجة ذاتها وكأننا أسبقنا المعاملة gender بالرمز #.

في المثال التالي، سنقوم بتحويل نموذج Person آخر من خلال استدعاء custom initializer الذي قمنا بتعريفه للتوه.
   
let bart = Person(gender: "male")

println(bart.gender) // Outputs "male"

على الرغم من أن القيمة البدائية للخاصية gender معينة لـ: "female" في تعريف الصنف، فإنه من خلال تمرير قيمة للمعاملة gender، بإمكاننا تعيين قيمة custom لخاصية الثابت gender خلال عملية البدء.


Initializers متعددة

كما هو الحال في Objective-C، يمكن للنصف أو البنية أن يكون لديها initializers متعددة.  في المثال التالي، سنبتكر نموذجين Person. في السطر الأول، سنستخدم initializer الافتراضي. في السطر الثاني، سنستخدم custom initializer الذي عرفناه مسبقاً.

let p1 = Person()

let p2 = Person(gender: "male")

4. تعريف البنية

تعد البنى مشابهة للأصناف بشكل مفاجئ، ومع ذلك هنالك بعض الإختلافات الأساسية. دعونا نبدأ بتعريف البنية الأساسية.
   
struct Wallet {

    var dollars: Int

    var cents: Int

    init() {

        dollars = 0

        cents = 0

    }

}

ستلاحظ من المحة الأولى، أن أول إختلاف هو استخدام الكلمة الأساسية struct بدلاً من الكلمة الأساسية class. يعرض المثال أيضاً طرقة بديلة من أجل إضافة القيم البدائية للخصائص. بدلاً من إضافة قيمة بدائية لكل خاصية، يمكننا أن نعطي الخصائص قيمة بدائية في initializer البنية. لن ينظر Swift إلى هذا على أنه خطأ لأن ذلك سيحفز أيضاً initializer ليحدد القيمة البدائية — والنوع — لكل خاصية.


5. الأصناف والبنى

يمكنك أن تبدأ بالتساؤل عن ما هو الفرق حقاً بين الأصناف والبنى. سترى من اللمحة الأولى، أنهما يبدوان متطابقان من حيث الشكل والوظيفة، باستثناء واحد هو الكلمتين الأساسيتين class و struct. على الرغم من ذلك هنالك عدة إختلافات رئيسية.


الإرث

يمكن للأصناف أن تدعم الميراث ولكن البنى لا تستطيع ذلك. سيوضح المثال التالي ذلك. لا يمكن الإستغناء عن تصميم نمط الميراث في البرمجة كائنية التوجه وفي Swift، تعد إختلافاً أساسياً بين الأصناف والبنى.
   
class Person {

    var firstName: String?

    var lastName: String?

    let gender = "female"

    init(gender: String) {

        self.gender = gender

    }

}

class Student: Person {

    var school: String?

}

let student = Student(gender: "male")

في المثال الذي في الأعلى، يعد صنف Person الوالد أو الصنف الأعلى لصنف Student. هذا يعني أن صنف Student يرث خصائص وسلوك صنف Person. يوضح السطر الأخير ذلك. سنقوم ببدء نموذج Student من خلال استدعاء custom initializer الذي عرفناه مسبقاً في صنف Person.


النسخ والإسناد

إن المفهوم التالي ربما سيكون أهم مفهوم ستتعلمه اليوم عن Swift، الإختلاف بين القيمة وأنواع الإسناد. تعد البنى أنواع قيم، والذي يعني أنه يتم تمريرهم من خلال قيمة. ستتوضح الفكرة أكثر من خلال هذا المثال. 
   
struct Point {

    var x: Int

    var y: Int

    init(x: Int, y: Int) {

        self.x = x

        self.y = y

    }

}

var point1 = Point(x: 0, y: 0)

var point2 = point1

point1.x = 10

println(point1.x) // Outputs 10

println(point2.x) // Outputs 0

نقوم بتعريف بنية (Point) لجمع البيانات لتخزين النظير في مكان ذات بعدين.  نقوم بتحويل point1 بـ: x مساوياً لـ: 0 و y مساوياً 0. سنعين point1 لـ: point2 ونعين نظير x point1 لـ: 10. إذا استنتجنا نظير x لكلا النقطتين، سنكتشف أنهما غير متساويتان.

تمرر البنى من خلال القيم وتمرير الأصناف من خلال الإسناد. إذا كنت تخطط لمتابعة العمل مع Swift، يجب عليك أن تفهم المقولة السابقة. عندما قمنا بتعيين point1 لـ:  point2، قام Swift بإبتكار نسخة عن point1 وعينها لـ: point2. وبكلمات أخرى، تشير كلا من point 1 و point2 لنموذج مختلف من بنية Point.

دعونا نكرر هذا التمرين مع صنف class في المثال التالي، قمنا بتحويل نموذج Person وضع خصائصه، تعيين person1 لـ: person2، وتحديث خاصية firstName التابعة لـ: person1. لنرى ما الذي يمكن تمريره من خلال الإسناد المستخدم في الأصناف، نستنتج قيمة خاصية firstName لكلا نموذجي Person.


var person1 = Person(gender: "female")

person1.firstName = "Jane"

person1.lastName = "Doe"

var person2 = person1

person1.firstName = "Janine"
println(person1.firstName)

println(person2.firstName)

يثبت المثال أن الأصناف من أنواع الإسناد. هذا يعني أن person1 و person2 يشيران إلى أو يسندان إلى نفس نموذج Person. من خلال تعيين person1 لـ: person2، فإن Swift لن يبتكر نسخة عن person1. متغير person2 يشير إلى نفس نموذج Person الذي person1 يشير له. يؤثر أيضاً تغيير خاصية firstName التابعة لـ: person  بخاصية firstName التابعة لـ: person2، ذلك لأنهم يشيرون إلى نفس نموذج Person.

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


الخلاصة

في هذا القسم الذي يبحث تعلم Swift من الصفر، لقد بدأنا باستطلاع أساسيات البرمجة كائنية التوجه في Swift. تعد الأصناف والبنى حجر أساس في معظم مشاريع Swift وسنتعلم أكثر عنهم في الدروس القادمة.

في الدرس القادم، سنكمل استطلاعنا عن الأصناف والبنى من خلال أخذ نظرة أقرب عن الخصائص والإرث.

الكــاتــب

    • مشاركة

ليست هناك تعليقات:

جميع الحقوق محفوظة لــ الشبح للمعلوميات 2019 ©