البحث في الموقع
المحتوى عن 'class'.
-
كان الغرض من البرمجة الكائنية (Object oriented programing اختصارًا OOP) هو السماح للمبرمجين بتسهيل تقسيم البرامج حسب وظيفتها؛ فالمبرمجون يُنشؤون "كائنات" ويضبطون بعض الخاصيات ثم يطلبون من تلك الكائنات أن تقوم بأشياءٍ معيّنة. مثلًا لدينا "الأكواب" عبارة عن كائنات، وتلك الأكواب لها خاصيات معيّنة مثل المادة المصنوعة منها (زجاج، أو بلاستيك، أو معدن) والسعة القصوى لها، كما يمكن إجراء عمليات عليها مثل ملء كوب. لكن عمومًا تنطوي كل تلك الأنواع تحت لواء "الأكواب" وإن اختلفت خاصياتها. أنُشِئت الأصناف (classes) في PHP لغرض التجريد (abstraction) والتغليف (encapsulation). والصنف هو مجموعة من المتغيرات والدوال التي تؤدي وظيفة مشابهة؛ وتساعد الأصناف في تجنب المشاكل الأمنية وذلك بفصل الواجهة (interface) عن طريقة التطبيق (implementation)، وتضيف الأصناف قيودًا إلى الوصول إلى المتغيرات والدوال. يُعرَّف الصنف بكتابة الكلمة المحجوزة class يليها اسم الصنف، ومن المستحسن اتباع طرق الكتابة التقليدية في أسماء الأصناف، حيث يبدأ اسم الصنف بحرفٍ كبير؛ هذا مثالٌ عن تعريف صنف بسيط: <?php class SimpleClass { // التعليمات البرمجية } ?> الشيفرة الموجودة ضمن الصنف لا تنفذ مباشرةً، إذ علينا أولًا أن قومة بإنشاء بإشاء كائن (object) من ذاك الصنف، الكائن هو نسخة من الصنف تكون جميع متغيرات ودوال ذاك الصنف جزءًا من خاصياتها (properties). يمكن إنشاء كائن من صنف كالآتي: object_name = new ClassName(); الخاصيات والدوال المتغيرات التي تكون عضوًا بالصنف تسمى خاصيات (properties)، وتُعرَّف عبر استخدام إحدى محددات الوصول public (عام) أو protected (محمي) أو private (خاص)، ويأتي بعدها التعريف الاعتيادي للمتغيرات، ويمكن أن تُسنَد القيم إليها مباشرةً، لكن يجب أن تكون تلك القيم ثابتة، أي لا تحتوي تعابير رياضية أو قيم معادة من دوال. أما الدوال الأعضاء في الأصناف، فتسمى توابع methods، وتُعرَّف كغيرها من الدوال، لكن مع الانتباه إلى ضرورة تحديد مجال الدالة (عامة أو محمية أو خاصة) كما في المثال الآتي: <?php class SimpleClass { // تعريف متغير أو خاصية public $var = 'a default value'; // تعريف دالة public function displayVar() { echo $this->var; } } ?> المتغير $this متوفر داخل دوال الصنف تلقائيًا (أي ليس عليك إنشاؤه)، وهو يشير إلى الكائن الذي قام بإنشاء نسخة من الصنف، ويُستعمل للوصول إلى الخاصيات أو الدوال الموجودة في الصنف. لاحظ عدم وجود رمز الدولار ($) قبل اسم المتغير عند الوصول إليه عبر $this. مثالٌ عن ما سبق: <?php class ClassName { // تعريف متغير public public $class_variable; // الدالة البانية function __construct() { $this->class_variable = 60 * 60; echo "this is the constructor <br>"; } // إعادة متغير في الصنف function get_global() { return $this->class_variable; } // تعديل متغير في الصنف function set_global($value) { $this->class_variable = $value; } // إظهار قيمة متغير في الصنف public function reset_display() { $this->private_function(); echo $this->get_global()." <br>"; } // دالة خاصة private function private_function() { $this->class_variable = 60 * 60; } } $object_name = new ClassName(); echo $object_name->get_global()."<br>"; $object_name->set_global(231); echo $object_name->get_global()."<br>"; $object_name->reset_display(); ?> لاحظ المفاهيم الآتية في المثال السابق التي شرحنا بعضها أعلاه: المتغير الذي يكون متاحًا للوصول في كل الصنف ($class_variable) يُعرَّف بالكلمة المحجوزة public؛ أما المتغيرات المحلية المُعرَّفة داخل دالة لا يمكن الوصول إليها إلا من تلك الدالة. يمكن الوصول إلى متغيرات أو دوال الصنف باستخدام $this->variable_name; و $this->function_name(); على التوالي وبالترتيب. إذ أنَّ $this يُشير إلى الصنف نفسه. الدالة البانية (constructor) هي دالة ذات معنى خاص في الأصناف؛ حيث تُشغَّل هذه الدالة عندما نُنشِئ كائنًا من ذاك الصنف، ويمكن أيضًا إرسال وسائط إلى الدالة البانية. سنشرح الدالة البانية والهادمة لاحقًا. ناتج السكربت السابق: it is constructor 3600 231 3600 ملاحظة: لا يُنصح بتعديل قيم خاصيات الفئات يدويًا، وإنما استعمل دوالًا خاصةً لهذا الغرض، فمثلًا لو كان عندك صنفٌ وظيفته حساب كمية الماء اللازمة لمدينة ما، ولديك خاصية اسمها population تُمثِّل عدد سكان المدينة، فلا تسمح بالوصول إليها مباشرةً، وإنما اكتب دالةً اسمها setPopulation مثلًا، واجعلها تُعدِّل قيمة عدد السكان وكل ما يتعلق بها من الحسابات تلقائيًا: $obj->setPopulation(200000); الوراثة نحاول دومًا عندما نبرمج ألّا نعيد كتابة الشيفرة مرارًا وتكرارًا، وأن نفصل بين تطبيق الشيفرة والواجهة (interface) لأسباب تتعلق بالحماية. الخيار الجديد المتاح أمامنا الآن هو استعمال الوراثة للقيام بالأمور السابقة بكفاءة. الوراثة في البرمجة كالوراثة في حياتنا، إذ يرث والدك بعض الخاصيات من جدك ويُعدِّلها أيضًا، وأنت أيضًا ترث بعض الخاصيات من والدك وتعدلها وتضيف غيرها. تسمى هذه العملية في البرمجة بالمصطلح inheritance. يمكن لأي صنف أن يوسِّع أو يشتق (extend) أصنافًا أخرى ويمكنه أن يصل إلى المتغيرات والدوال العامة والمحمية فيها فقط (أي لا يستطيع أن يصل إلى الدوال الخاصة private). <?php /** * الوراثة في PHP */ class Grandfather { // متغير عام public $global_variable; // الدالة البانية للصنف grandfather function __construct() { $this->global_variable = 56; echo "I am grandfather <br>"; } function default_function() { echo "this is default function in grandfather <br>"; } private function private_function() { echo "this is private function in grandfather <br>"; } protected function protected_function() { echo "this is protected function in grandfather <br>"; } public function public_function() { echo "this is public function in grandfather <br>"; } } /** * هذا صنف فرعي مشتق من الصنف Grandfather * وسيرث كل خاصياته عدا الخاصة (private) منها */ class Father extends Grandfather { // متغير عام public $father_var; function __construct() { // السطر الآتي مساوٌ للسطر => parent::__construct(); Grandfather::__construct(); $this->father_var = 256; echo "I am father <br>"; } public function display_all() { $this->default_function(); $this->protected_function(); $this->public_function(); echo "I am father's display_all <br>"; parent::public_function(); } } /** * هذا الصنف الابن يرث من الصنف الأب * ويرث أيضًا (بشكلٍ غير مباشر) من الصنف الجد */ class Child extends Father { // الدالة البانية في الصنف الابن function __construct() { Grandfather::__construct(); echo "I am child <br>"; } // يُعدِّل الابن في دالة موجودة في الأب // تسمى هذه العملية «إعادة تعريف الدوال» function display_all() { echo "function from father<br>"; // استدعاء دالة من الصنف الأب parent::display_all(); echo "new added in child<br>"; } } $obj = new Father(); $obj->display_all(); echo "<br><br><br>Child object call<br><br>"; $obj2 = new Child(); $obj2->display_all(); ?> الناتج: I am grandfather I am father this is default function in grandfather this is protected function in grandfather this is public function in grandfather I am father's display_all this is public function in grandfather Child object call I am grandfather I am child function from father this is default function in grandfather this is protected function in grandfather this is public function in grandfather I am father's display_all this is public function in grandfather new added in child يعطي المثال السابق صورةً كاملةً عن الوراثة، لنحاول فهمه: الصنف Child يرِث من الصنف Father، الذي بدوره يرث الصنف Grandfather؛ وهذا يُسمى الوراثة متعددة المستويات. الصنف الفرعي (subclass أي الصنف الذي يقوم بالوراثة) يمكنه الوصول إلى جميع الخاصيات غير الخاصة (private) للصنف الموروث (يسمى أيضًا superclass). يمكن للصنف الفرعي أن يستدعي دوال الصنف الموروث عبر استعمال الصيغة الآتية parent::function_name() (يمكن استعمالها لمستوى وراثة وحيد فقط، أي يمكن للصنف Child أن يستعملها لاستدعاء دوال الصنف Father، ويمكن للصنف Father أن يستعملها للصنف Grandfather؛ لكن لا يمكن أن يستعملها الصنف Child لاستدعاء دوال الصنف Grandfather.) أو الصيغة الآتية SuperClass_name:function_name() التي يمكن استعمالها لاستدعاء دوال الصنف Grandfather من الصنف Child. يمكن أن يُعدِّل الصنف الفرعي في دوال الصنف الأب، وذلك يُعرَف بإعادة التعريف (overriding). لا تدعم لغة PHP الوراثة المتعددة؛ أي لا يمكن للصنف أن يرث أكثر من صنف واحد. محددات الوصول لقد تطرقنا سابقًا إلى موضوع محددات الوصول بشكل مبسط، حان الوقت الآن لشرحها بالتفصيل. هنالك كلماتٌ محجوزةٌ تسمى محددات الوصول توضع قبل تعريف المتغيرات أو الدوال الأعضاء في الصنف، وتعيّن مَن الذي يستطيع الوصول إلى ذاك المتغير أو الدالة، وهي: public (عام): ذاك المتغير أو الدالة يمكن الوصول إليه من داخل الصنف أو من خارجه private (خاص): لا يمكن الوصول إلى المتغير أو الدالة إلا من داخل الصنف نفسه protected (محمي): يسمح بالوصول إلى المتغير أو الدالة من الصنف نفسه والصنف المُشتَق منه فقط final (نهائي): هذه الدالة لا يمكن إسناد قيمة أخرى إليه أو تعريفها في الأصناف المُشتقَة (لا يمكن استخدام final مع الخصائص/المُتغيّرات). نستعمل عادةً المُحدِّد public للخاصيات أو الدوال التي تريد الوصول إليها من خارج الصنف، أما private فتستعمل للأجزاء الداخلية التي لا يلزم الوصول إليها من خارج الصنف، أما protected فهي للخاصيات التي تستعمل في بنية الصنف (كما في private) لكن من المقبول توريثها إلى أصنافٍ أخرى كي يعدلوها بما يلائم. فمثلًا، لو كنا نكتب صنفًا للاتصال بقاعدة البيانات، فسيكون مقبض الاتصال (connection handle) لقاعدة البيانات مخزنًا في متغير خاص، لأنه يستعمل داخل الصنف فقط، ولا يجب أن يكون متوفرًا لمستخدم هذا الصنف؛ أما عنوان الشبكة لمضيف خادوم قواعد البيانات، فهو خاص بالصنف، ولا يجب على المستخدم تعديله، لكن من المقبول تعديله من صنفٍ يرث هذا الصنف. أما الطلبيات التي تُجرى على قاعدة البيانات، فيجب أن تكون عامة، كي يستطيع مستخدم الصنف الوصول إليها. الدالة البانية والدالة الهادمة ذكرنا الدالة البانية سابقًا ومررنا عليها سريعًا، وقلنا وقتها أنَّ الدالة البانية تُنفَّذ عند إنشاء كائن من الصنف، وهي مناسبة لعمليات تهيئة المتغيرات وإعطائها قيمةً ابتدائيةً. تحمل هذه الدالة اسم __construct. يجدر بالذكر أنَّ الدالة البانية للصنف الأب لا تستدعى تلقائيًا عند إنشاء كائن للصنف الابن، ويجب استدعاؤها يدويًا عبر parent::__construct() ضمن الدالة البانية للصنف الابن. لكن إن لم تُعرَّف دالة بانية للصنف الابن، فسيرث الدالة البانية للصنف الأب تلقائيًا. مثالٌ عليها سيوضح ما سبق: <?php class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } class OtherSubClass extends BaseClass { // سيرث هذا الصنف الدالة البانية للصنف الأب } // ستنفذ الدالة البانية للصنف BaseClass $obj = new BaseClass(); // ستنفذ الدالة البانية للصنف BaseClass // وستنفذ الدالة البانية للصنف SubClass $obj = new SubClass(); // ستنفذ الدالة البانية للصنف BaseClass $obj = new OtherSubClass(); ?> أما الدالة الهادمة، فهي الدالة التي تُنفَّذ عندما لا يعود الكائن موجودًا، وتُعرَّف -كما الدالة البانية- بذكر اسمها __destruct، ولا تستدعى الدالة الهادمة للصنف الأب إن أُعيد تعريفها في الصنف الابن. <?php class MyDestructableClass { function __construct() { print "In constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "Destroying " . $this->name . "\n"; } } $obj = new MyDestructableClass(); // الناتج: In constructor echo "We will destroy \$obj \n"; unset($obj); // الناتج: Destroying MyDestructableClass echo '$obj Destroyed'; ?> معرفة نوع الكائنات لقد رأيت كيف أنَّ الوراثة هي أمرٌ مهمٌ في البرمجة الكائنية، ولكن قد تختلط عليك الكائنات، ولن تدري لأي صنفٍ تنتمي. يأتي الحل مع الكلمة المحجوزة instanceof التي يمكن استعمالها كأحد المعاملات، فستُعيد TRUE إن كان الكائن المذكور اسمه على يسارها تابعًا لصنفٍ ما أو لصنفٍ مشتقٍ من الصنف المذكور على يمينها. على سبيل المثال: $obj = new SubClass(); if ($obj instanceof SubClass) { } if ($obj instanceof BaseClass) { } ناتج العبارتان الشرطيتان السابقتان هو TRUE لأن $obj هو كائنٌ ينتمي إلى الصنف SubClass (أول عبارة شرطية)، وينتمي إلى صنفٍ مشتقٍ من BaseClass (العبارة الشرطية الثانية). أما لو أردت أن تعلم إن كان الكائن ينتمي إلى صنفٍ مشتقٍ من الصنف المذكور، فاستعمل الدالة is_subclass_of()، الذي تقبل وسيطين، أولهما هو الكائن الذي سنختبره، والثاني هو اسم الصنف الأب. <?php class BaseClass {} class SubClass extends BaseClass {} $obj = new SubClass(); if ($obj instanceof SubClass) { echo 'instanceof is TRUE'; } if (is_subclass_of($obj, 'SubClass')) { echo 'is_subclass_of is TRUE'; } ?> ستجد أن instanceof في المثال السابق ستعطي TRUE، بينما ستعطي is_subclass_of() القيمة FALSE، لأن $obj ليس كائنًا لصنف مشتق من الصنف SubClass، وإنما هو من الصنف SubClass نفسه. تحديد الصنف في معاملات الدوال لنقل أنك تريد تمرير كائن من صنف معيّن إلى دالة، ولا تريد السماح بتمرير سلسلة نصية أو رقم لها بدلًا من الكائن ذي الصنف المحدد؛ تستطيع فعل ذلك بذكر اسم الصنف قبل المعامل أثناء تعريف الدالة، كما يلي: <?php class Numbers { public function print_random() { echo rand(, 10); } } class Chars {} function random (Numbers $num) { $num->print_random(); } $char = new Chars(); random($char); // fatal error: Argument 1 passed to random() must be an instance of Numbers ?> الدوال "السحرية" عندما تشاهد دالةً يبدأ اسمها بشرطتين سفليتين، فاعلم أنها دالة سحرية (Magic function)، وهي متوفرة من PHP، وتحجز PHP جميع أسماء الدوال التي تبدأ بشرطتين سفليتين على أنها دالة سحرية، لذا يُنصَح بتجنب استعمال هذا النمط من التسمية لتلافي حدوث تضارب في المستقبل مع الدوال السحرية التي قد تعرفها الإصدارات الحديثة من PHP. لقد رأينا سابقًا الدالتين البانية __construct() والهادمة __destruct()، وسنتحدث هنا عن الدوال __get() و __set() و __call() و __toString(). تُعرَّف الدالتان __get() و __set() داخل الأصناف، تستعمل الدالة __get() عندما تحاول قراءة متغير غير مُعرَّف من متغيرات الصنف، وتُستعمَل الدالة __set() عند محاولة إسناد قيمة لمتغير غير موجود، انظر إلى المثال الآتي: <?php class MagicClass { public $name = 'Magic'; private $age = 123; public function __get($var) { echo "getting: $var "; echo $this->$var; } public function __set($var, $value) { echo "setting: $var to $value"; $this->$var = $value; } } $obj = new MagicClass(); echo $obj->name; // الناتج: Magic $obj->age = 123; // أصبح بإمكاننا -باستعمال الدوال السحرية المُعرفة في الصنف- الوصول إلى متغير ذي وصولٍ خاص، لا يجدر بنا فعل ذلك عمومًا. echo $obj->age; // الناتج: getting: age 123 echo $obj->last_name; // ستحاول الدالة __get الحصول على قيمة الخاصية last_name، لكنها غير موجودة، وسيظهر خطأ من مرتبة Notice لإشارة إلى ذلك. ?> قد نستفيد من الدالتين __get() و __set() بإظهار رسالة خطأ عند محاولة الوصول إلى عناصر غير موجودة (أو لا يُمسَح لنا بالوصول إليها). أما دالة __call() فغرضها مشابه لغرض __get() إلا أنها تُستعمَل عند محاولة استدعاء دالة غير موجودة. ستستدعى الدالة السحرية __toString() في حال تمت محاولة طباعة الكائن كسلسلة نصية. هذه الدالة بسيطة جدًا وتعمل كما يلي: <?php class MagicClass { public function __toString() { return "I'm Magical! :-)"; } // ... } $obj = new MagicClass(); echo $obj; ?> مصادر مقالات Classes in PHP و Inheritance in PHP لصاحبها Harish Kumar.فص ل Objects في كتاب Practical PHP Programming. فصل البرمجة غرضية التوجه في كتاب تعلم البرمجة بلغة PHP. صفحات Introduction و The Basics و Constructors and Destructors و Visibility و Object Inheritance و Magic Methods و Type Hinting في دليل PHP وغيرها.
- 4 تعليقات
-
- 8
-
- php
- البرمجة كائنية التوجه
- (و 9 أكثر)
-
تعرّفنا فيما سبق من دروس هذه السلسلة على أساسيات لغة بايثون، من مُتغيّرات وحلقات تكرار إلى الدوال، وقد حان الوقتُ للدخول إلى أساسيات البرمجة كائنية التوجّه Object Oriented Programming وهي ببساطة طريقة أخرى للبرمجة بحيث تكون أجزاء الشّيفرة مجموعة داخل دوال تُسمّى التوابع methods والدوال تكون داخل صنف معيّن Class. عند إنشاء كائن object من هذا الصنف فإنّنا نستطيع أن نُنفّذ عليه مُختلف العمليات الموجودة داخل التوابع والتي بدورها توجد داخل الصنف. هناك تسميّات عربية أخرى لهذا النّوع من البرمجة، لذا لا تقلق إذا صادَفتَ أحد هذه التّسميات في أماكن أخرى، فكلّها تُشير إلى نفس المعنى: برمجة غرضيّة التوجه برمجة شيئية المنحى برمجة كائنيّة المنحى بنية الصنف Class الصنف ببساطة يحتوي على أجزاء مُختلفة من الشيفرة تماما مثل الدالة، الفرق هنا هو أنّ الصنف يحتوي على دوال كذلك، وهذه الدوال تُسمى التّوابع، ويحتوي كذلك على مُتغيّرات وتنقسم هذه الأخيرة إلى نوعين، مُتغيّر الصنف، والمُتغيّر العادي، الفرق بينهما هو أنّك تستطيع الوصول إلى مُتغيّر الصنف في أي مكان داخل الصنف (سواء داخل التوابع أو خارجها). إنشاء صنف لإنشاء صنف في لغة بايثون كلّ ما عليك فعله هو كتابة كلمة class وبعدها اسم الصنف ثمّ إلحاق نقطتين، بعدها اكتب الشيفرة بإزاحة أربع مسافات: >>> class My_class: ... pass أنشأنا أعلاه صنفًا بسيطا باسم My_class وهو لا يفعل أي شيء يُذكر (كلمة pass تُخبر بايثون بالمرور دون تنفيذ أي شيء). إذا كتبت اسم الصنف على مفسّر بايثون فستجد مُخرجا كالتّالي: >>> My_class <class __main__.My_class at 0x7fd0efc41460> لاحظ بأنّ الكلمة الأولى من المُخرج هي class أي أنّنا أصبحنا نمتلك صنفًا جديدًا، ما يتبع at هو المكان في الذاكرة الذي وُضع فيه الصنف ويتغيّر بين الحين والآخر. إنشاء كائن من صنف بعد أن أنشأنا الصنف سنتمكّن الآن من إنشاء كائن من هذا الصنف، والكائن مُجرّد اسم تماما كالمُتغيّر: my_object = My_class() الآن الكائن my_object هو من صنف My_class. تعريف المتغيرات داخل صنف يُمكننا أن نُعرّف مُتغيّرات في الصنف تماما كما نُعرّف المُتغيّرات بشكل عادي. class My_class: my_variable = 'This is my variable' للوصول إلى المُتغيّر ننشئ أولا كائنا من الصنف وبعدها نكتب اسم الكائن ثمّ نقطة ثمّ اسم المُتغيّر: my_object = My_class() print my_object.my_variable المُخرج: This is my variable يُمكن كذلك الحصول على النّتيجة ذاتها في سطر واحد: print My_class().my_variable إنشاء التوابع التوابع هي دوال خاصّة بالصنف، ويُمكننا إنشاء التابع بنفس الطّريقة التي نُنشئ بها الدالة، الإختلاف هنا هو أنّ جميع التوابع يجب أن تُعرّف مع مُعامل باسم self وذلك للإشارة إلى أنّ الدالة/التابع تابع للصنف، لننشئ تابعا داخل صنف الآن. class My_class: my_variable = 'This is my variable' def my_method(self): print 'This is my method' الآن إذا أنشأنا كائنا فإنّنا سنتمكّن من الوصول إلى التابع، وتذكّر بأنّ التابع تلحقه الأقواس: my_object = My_class() my_object.my_method() المُخرج: This is my method يُمكن كذلك الحصول على النّتيجة ذاتها في سطر واحد: My_class().my_method() كما تُلاحظ فقد نُفّذت الشيفرة الموجودة داخل التّابع my_method ويُمكننا كذلك أن نجعل التّابع يقبل المُعاملات، لكن تذكّر الحفاظ على الكلمة self كمُتغيّر أول. class My_class: my_variable = 'This is my variable' def my_method(self, my_parameter): print 'This is my method ; {} is my parameter'.format(my_parameter) يُمكنك استدعاء التّابع كالتّالي: my_object = My_class() my_object.my_method('Parameter1') my_object.my_method('Parameter2') المُخرج: This is my method ; Parameter1 is my parameter This is my method ; Parameter2 is my parameter في البرنامج السّابق، أنشأنا أولا صنفًا باسم My_class وقُمنا بتعريف مُتغيّر، ثمّ بتعريف تابع باسم my_method يقبل مُعاملين self و my_parameter، بالنّسبة لاستدعاء التّابع، فنحتاج فقط إلى تمرير المُعاملات الموجودة بعد المُعامل selfولا نحتاج إلى تعيين قيمة لهذا المُعامل. مُلاحظة: يُمكنك إعادة تسميّة المُعامل الأول كما تشاء، أي أنّ البرنامج التّالي سيعمل دون مشاكل. class My_class: def my_method(this, my_parameter): print '{} is my parameter'.format(my_parameter) ولكن رغم ذلك فالمُتعارف عليه بين مُبرمجي لغة بايثون هو استعمال self، وفي كثير من اللغات الأخرى تُستعمل this عوضا عن self، أما في برامِجك فمن المُفضّل الإبقاء على هذه التّسميّة المُتعارف عنها، وذلك لتكون شيفراته سهلة القراءة. الوصول إلى متغيرات الصنف داخل التوابع تأمّل الصنف التّالي: class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) البرنامج أعلاه بسيط جدا، أولا نعرّف صنف باسم Person وبعدها نقوم بتعيين قيمتين للمُتغيّرين name و lastname، وبعدها عرّفنا تابعا باسم say_hello يطبع جملة Hello, My name is Abdelhadi. كلّ شيء جيد، لكن ماذا لو أردنا أن نصل إلى المُتغيّرات الأخرى الموجودة خارج التّابع، فلا يُمكننا مثلا أن نقوم بالأمر كالتّالي: class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) print lastname print job ستحصل على الخطأ التّالي: global name 'lastname' is not defined لتفادي هذا الخطأ سنستعمل كلمة self قبل المُتغيّر. class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) print 'My Last name is {} '.format(self.lastname) print 'I am a {}'.format(self.job) استدعاء التّابع: me = Person() me.say_hello() المُخرج: Hello, My name is Abdelhadi My Last name is Dyouri I am a Writer, Developer لاحظ بأنّنا قُمنا بالوصول إلى مُتغيّر lastname عن طريق استدعائه بـ self.lastname وكذا الحال مع المُتغيّر job، وهذه الطّريقة مُشابهة لاستخدام كلمة global الفرق هنا أنّ هذه الأخيرة تُمكن من الوصول إلى المُتغيّر في كامل البرنامج، أمّا كلمة self فتُشير إلى المُتغيّر المُعرّف في الصنف الحاليّة فقط. لتفهم أكثر كيفيّة عمل الكلمة self فقط تخيّل بأنّها تحمل نفس اسم الصنف، مثلا: class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) print 'My Last name is {} '.format(Abd.lastname) print 'I am a {}'.format(Abd.job) لاحظ بأنّنا غيّرنا كلمة self إلى اسم الصنف واستمرّ عمل البرنامج دون مشاكل. وبنفس الطّريقة يُمكنك أن تستدعي تابعا داخل تابع آخر في نفس الصنف: class Person: def say_name(self): print 'Abdelhadi' def say_hello(self): print 'Hello My name is:' self.say_name() المُخرج: Hello My name is: Abdelhadi ما حدث هو أنّ التّابع say_hello قام بطباعة جملة :Hello My name is ثمّ قام باستدعاء التّابع say_name الذي قام بدوره بطباعة الاسم Abdelhadi. لماذا تستعمل البرمجة الكائنية، ومتى يجب علي استخدامها قد تُلاحظ بأنّ ما يُمكنك فعله بالبرمجة الكائنيّة يُمكن القيام به بالدوال والمُتغيّرات فقط. هذا صحيح، وهو أمر واضح، البرمجة الكائنيّة تُستعمل أساسا إذا كان البرنامج الذي تبنيه مُعقّدا مع العديد من الوظائف المُتعلّقة ببعضها (كمكتبة برمجيّة)، مثلا لنقل بأنّك تُطوّر برنامجا مسؤولا عن جلب بيانات من موقع مُعيّن، وبعدها التّعديل عليها، ثمّ إخراج مستند PDF يحتوي على هذه البيانات بشكل يُسهّل قراءتها، هنا ستحتاج إلى صنف لجلب البيانات والتّعديل عليها، وصنف أخرى لتحويل البيانات إلى نصّ مقروء واضح ثمّ إلى ملفّ PDF. إذا نشرت برنامجك مع صديقك وأراد أن يعمل على الجزء الثاني لإضافة وظيفة تُمكن المُستخدم من طباعة المُستند فلا يُعقل أن يضطر للمرور على كل ما يتعلّق بجلب البيانات فقط لأنّه يريد أن يضيف خاصيّة لا علاقة لها بجلب البيانات. استعمال البرمجة الكائنيّة في هذا المشروع سيسمح لك بالتّركيز على الجزء الأول، وسيسمح لصديقك بالتّركيز على تطوير الجزء الثاني. خلاصة الأمر هي أنّك لست مضطرا لاستعمال البرمجة الكائنيّة إلا إذا كان برنامجك طويلا يحتوي على وظائف تتعلّق ببعضها البعض (وظائف من نفس الصنف)، ونسبة استخدام الآخرين لشيفرتك عالية. تمارين التمرين 1 أنشئ صنفًا باسمك، وقم بتعريف مُتغيّرين lastname (الاسم العائلي) و age (العمر)، ثم أنشئ كائنا باسم me (أنا) وقم بطباعة اسمك العائلي وعمرك. التمرين 2 أنشئ صنفًا باسم Car (سيارة) وقم بتعريف مُتغيّرات لصفات السّيارة، مثلا brand لاسم الشّركة، release_date لتاريخ الإعلان عن السّيارة. التمرين 3 أضف توابع إلى الصنف Car التي أنشأتها في التّمرين الثّاني، يُمكن أن تكون التوابع عبارة عن عمليّات تقوم بها السّيارة مثلا move للحركة، stop للتوقّف، slow_down لتخفيض السّرعة، وقم بطباعة جمل تفيد بأنّ العمليّة قد نجحت. المفروض أن يتمكّن الآخرون من إنشاء كائنات خاصّة بهم بحيث تُستخدم بهذه الطّريقة: bmw = Car() bmw.move() bmw.slow_down() bmw.stop() خاتمة تعرّفنا في هذا الدّرس على بعض من أهم أساسيات البرمجة الكائنيّة التوجه في لغة بايثون وهذا الموضوع أطول من أن أشرحه في درس واحد لذلك سيكون الدّرس التّالي تكملة لما تعلّمناه في هذا الدّرس حول تعلم بايثون وسيغطي مفاهيم أخرى حول البرمجة كائنية التوجّه.
-
تعلّمنا في الدرسين السابقين كيفية إنشاء ونشر موقعنا الأوّل. في الحقيقة موقعنا حاليًّا بسيط وذو محتوى قليل وليس جذّابًا بعد. سنتعلّم في هذ الدرس كيف نستخدم تنسيقات CSS لتنسيق الصفحة. كما سنتعلّم في درس لاحق كيف نضيف المزيد من التنسيقات إلى موقعنا وذلك بمساعدة إطار عمل اسمه Bootstrap. البنية والتنسيق Structure and Style لنتذكّر معًا: يُعبّر HTML عن بنية صفحة الويب، في حين تُعرّف CSS المظهر العامّ للصفحة. يُعتبر فصل بنية الصفحة عن تنسيقها أمر في غاية الأهميّة وله الكثير من المزايا رغم أنّه ليس إلزاميًّا. لذلك سنعمل دومًا على جعل رُماز HTML وتنسيقات CSS في ملفات منفصلة. الربط مع ملف CSS ملف CSS هو ملف نص عادي له الامتداد (css.) ونربط معه من داخل ملف HTML. أنشئ ملف جديد ضمن المجلّد Portfolio وسمّه main.css. افتح الملف index.html واضف العنصر <link> ضمن العنصر <head>. سنخبر الصفحة index.html عن طريق العنصر <link> أن تُحمّل ملف CSS، أي سننشئ رابط بينهما: <link rel="stylesheet" href="main.css"> أضف العنصر السابق إلى العنصر <head>. ستبدو الشيفرة لديك مُشابهة لما يلي: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="main.css"> <title>Web Portfolio of Marco</title> </head> <body> <h1>Web Portfolio of Marco</h1> <h2>Welcome!</h2> <p>Thanks for stopping by.</p> <p>Please have a look around. In the blog section I document my experiences in programming. You may also look at my web projects. Have fun.</p> <img src="marco.jpg" alt="Picture of me"> <p>Marco :-)</p> </body> </html> ملاحظة مهمّة: لكي يكون من الممكن الوصول لملف CSS، يجب أن تحتوي السمة href على المسار الصحيح للملف. وهذا يعني بدوره أنّه إذا كان ملف CSS في مجلّد فرعي، يحب أن نضم هذا المجلّد الفرعي إلى مسار الملف كما هو واضح. يمكنك مراجعة هذا الموضوع في الدرس الأوّل في فقرة عناوين URL النسبيّة والمطلقة. الألوان إلى الآن ما يزال ملف CSS فارغًا، لنبدأ الآن بكتابة أولى تنسيقات CSS. تحوي CSS ما يُعرف بالقواعد CSS Rules. يمكننا باستخدام قاعدة CSS أن نُخبر المتصفّح كيف يُعالج جزءًا مُحدّدًا من مستند HTML. كمثال على ذلك، سنغيّر لون الخلفيّة ولون النص للعنوان من المستوى الثاني h2 ضمن الملف index.html الخاص بنا. أضف ما يلي إلى ملف CSS: الملف main.css h2 { background-color: #607d8b; color: #ffffff; } يجب أن تحصل على شكل شبيه بما يلي: أكواد الألوان Color Codes تظهر الألوان على الشاشة كمزيج من نسب متفاوتة من الأحمر والأخضر والأزرق (RGB). يمكن التعبير عن المزج بين هذه الألوان الثلاثة في CSS بطريقتين: كقيم RGB عشرية (محصورة بين 0 إلى 255 لكل لون) أو كقيم RGB ستة عشرية hex values. وفي الواقع تُستَخدم القيم الستة عشرية في الغالب. إذا نظرنا إلى مثالنا الأخير، فنجد أنّنا قد عيّنا لونًا أزرقًا مائلًا للرمادي كلون خلفية باستخدام كود اللون 607d8b# بالترميز الستة عشري. أمّا لو أردنا استخدام الترميز العشري المُكافئ له فسنكتب (rgb(96, 125, 139. في التمثيل الستة عشري، يُعبّر أوّل رمزين من اليسار عن اللون الأحمر، والرمزين في الوسط عن اللون الأخضر، ويُعبّر الرمزان الأخيران عن اللون الأزرق. العمل مع أكواد الألوان يُعتبر التعامل مع الألوان كأكواد، شائع جدًا لكنه صعب في الحقيقة. لأنّك تتعامل مع قيم ورموز وليس مع ألوان، يُنصح دوماً استخدام أداة لانتقاء الألوان color picker. إذا كنت تستخدم المحرّر Brackets عندها يمكن أن تفتح أداة انتقاء اللون الموجودة ضمن ملف CSS الذي تعمل ضمنه. فقط تحتاج لأن تنقر بزر الفأرة الأيمن على كود اللون وتختار تحرير سريع Quick Edit أو أن تضغط Ctrl+E من لوحة المفاتيح. يؤدي ذلك إلى ظهور أداة اختيار الألوان حيث يمكنك اختيار اللون الذي يناسبك بسهولة. كما يمكنك اختيار ترميز اللون بالصيغة التي ترغبها من خلال ثلاثة أزرار موجودة في الأسفل. أدوات اختيار الألوان من الانترنت إذا لم تتوفّر أداة لاختيار الألوان ضمن المحرّر لديك، فيمكنك استخدام عدد كبير جدًّا من هذه الأدوات ولكن على الانترنت، فعلى سبيل المثال لا الحصر: HTML Color Codes ColorPicker ألواح الألوان من الانترنت نحتاج في كثير من الأحيان إلى أكثر من لون واحد لاستخدامه في موقعنا. لذلك نحتاج إلى أداة تزوّدنا بمجموعة من الألوان المنسجمة، والتي تتناغم مع بعضها لإضفاء تأثير جميل على الموقع. وهذا ما تفعله ألواح الألوان color palettes. هناك بعض المواقع على الانترنت التي توفّر مثل هذه الميزة. أمثلة على مواقع بمجموعات ألوان معرّفة مسبقًا: Colour Lovers Design Seeds Google Color Palette أمثلة على مواقع تولّد مجموعات ألوان حسب رغبة المستخدم: Kuler: يُعرّف قاعدة لون (سنتكلّم عن القواعد بعد قليل)، ويسمح لنا بالحصول على خمسة ألوان منسجمة معًا. Paletton: أداة متقدّمة للحصول على مجموعات ألوان منسجمة. Colourco.de: مولّد ألواح ألوان عملي أيضًا. قواعد CSS رأينا كيف تُغيّر CSS من هيئة عناصر HTML من خلال تأثيرها على الألوان. لنلقي نظرة عن كثب حول CSS وطريقة عملها. تُعتبر القاعدة rule البنية الأساسية لـ CSS وهي تتكوّن من ثلاثة عناصر: المحدِّد Selector والخاصية Property والقيمة Value. تُشير القاعدة السابقة إلى أنّ جميع عناصر <h2> يجب أن يكون لها لون خلفية له الكود 607d8b#. محددات CSS تُقرّر المحدّدات أيّ عناصر HTML ستُطبّق عليها قاعدة CSS. ملاحظة: سنتعلّم هنا المحدّدات الأكثر أهمية. من أجل الاطلاع على أنواع المُحدّدات الأخرى ابحث في Google عن CSS Selectors. محددات النوع Type Selectors تستهدف محدّدات النوع جميع العناصر التي يكون لها وسم HTML مُحدّد. فلو أردنا مثلًا استهداف جميع عناصر الفقرة p ضمن المستند بتنسيقات معيّنة، فسنكتب شبيه بما يلي: CSS p { ... } HTML <p>...</p> <p>...</p> محددات الصنف Class Selectors تُستخدم هذه المحدّدات بشكل متكرّر، وتُعتبر أنّها مُخصّصة أكثر قليلًا من مُحدّدات النوع، كما أنّه بإمكانها استهداف مجموعة مخصّصة من العناصر بدلًا من عناصر النوع الواحد فقط. لاستخدام محدّدات الصنف نضيف السمة class إلى العنصر المراد استهدافه، ونسند إليها قيمة مُعبّرة من اختيارنا. عند استخدام محدّد الصنف فإنّنا نستخدم نقطة عادية يليها قيمة السمة class التي أضفناها إلى العنصر. سيستهدف محدّد الصنف في المثال التالي، جميع العناصر التي لها سمة class تحمل القيمة highlight: CSS .highlight { ... } HTML <p class="highlight">...</p> <p>...</p> <p class="highlight new">...</p> لاحظ من شيفرة HTML في الأعلى، أنّ التنسيق سيُطبّق على عنصر p الأوّل والثالث فحسب، لأنّ عنصر p الثاني لا يمتلك سمة class لها القيمة highlight. كما نلاحظ أنّ عنصر p الثالث له أكثر من قيمة ضمن سمة class الخاصة به (highlight وnew). في الحقيقة يمكن استخدام عدة قيم لعنصر واحد بشرط أن نفصل بينها بفراغات. محددات معرف العنصر ID Selectors تشبه هذه المحدّدات محدّدات الصنف. فهي تستهدف جميع عناصر HTML التي تملك السمة id بقيمة مُعيّنة. من المهم أن نوضّح أنّ السمة id عبارة عن مُعرّف العنصر ضمن شيفرة HTML ورغم أنّه ليس من الملزم وضعها مع كل عنصر، إلّا أنّه في حال وجودها يجب أن تكون القيمة التي تحملها فريدةً ضمن كامل مستند HTML. نستنتج إذًا أنّ أي محدّد معرّف عنصر سيستهدف في الواقع عنصر واحد فقط ضمن مستند HTML. عند استخدام محدّد معرّف العنصر فإننا نكتب الرمز # يليه معرّف العنصر (قيمة السمة id). سيستهدف محدّد معرّف العنصر في المثال التالي عنصر واحد فقط ضمن مستند HTML بحيث تكون قيمة السمة id له هي navigation: CSS #navigation { ... } HTML <p id="navigation">...</p> <p>...</p> محددات الفروع Descendant Selectors يمكنك باستخدام هذا النوع من المحدّدات استهداف العناصر الأبناء لعنصر مُعيّن. ونقصد بالعناصر الأبناء لعنصر معيّن، تلك العناصر التي تقع ضمن هذا العنصر بصرف النظر عن المستوى الذي تقع فيه. انظر المثال التالي: <p id="level1"> <p id="level2"> <a href="myfile.html">My File</a> </p> </p> نلاحظ بأنّ كلًّا من العنصر <p> (ذو المعرّف level2) والعنصر <a> هما ابنان للعنصر <p> (ذو المعرّف level1). لأنّهما يقعان ضمنه بصرف النظر عن المستوى. في حين يُعتبر العنصر (ذو المعرّف level2) ابنًا مباشرًا للعنصر <p> (ذو المعرّف level1). أمّا بالنسبة لمحدّدات الفروع فنلق نظرة خاطفة على هذ المثال البسيط قبل أن نبدأ بالشرح: CSS p a { ... } HTML <p> <a href="http://code.makery.ch">My Website</a> </p> <a href="http://www.example.com">Example Website</a> نلاحظ بأنّ محدّد الفروع p a يستهدف جميع عناصر <a> أينما وُجدَت داخل عنصر <p>. وبناءً عليه نجد أنّ التنسيق سيُطبّق على My Website وليس على Example Website لأنّ الأخيرة موجودة ضمن عنصر <a> لا يقع ضمن أي عنصر <p> كما هو واضح. ملاحظة: ليس من الضروري أن يكون العنصر <a> في المثال في الأعلى ابنًا مباشرًا للعنصر<p>. فمحدّد الفروع السابق يستهدف أي عنصر <a> موجود ضمن أي مستوى ضمن العنصر <p>. إذا أردنا استهداف الابن المباشر فحسب، فيمكننا استخدام مُحدّد الابن ( > ). خصائص CSS تُقرّر قواعد CSS كيف يجب أن تظهر عناصر HTML. هناك العديد من الخصائص التي يمكن تعريفها في CSS. في الواقع لقد صادفنا اثنتان منهم: background-color وcolor. لن نتوسّع في الحديث عن خصائص CSS المختلفة، لأنّ هذا الدرس ليس مرجعًا لهذه الخصائص، لكن سنتعلّم كيف نجد المزيد من المعلومات عنها بأنفسنا. سنستفيد بشكل أكبر عندما نتعلّم هذه الخصائص عندما نحتاج إليهم فعليّا. توجد حالتان مختلفتان يمكن أن نصادفهما لنتعلُّم المزيد عن CSS. الحالة الأولى: عندما نصادف خاصية CSS لا نعرفها في هذه الحالة نبحث عن معلومات عنها في الانترنت، اكتب اسم الخاصية متبوعًا بكلمة css ضمن محرّك البحث. يمكنك أن تجرّب هذه الطريقة الآن لتحصل على معلومات حول خاصية CSS جديدة وهي: padding: 5px; استخدم css padding كعبارة بحث. لا تكتفي بالاطلاع على نتيجة البحث الأولى، بل اطّلع على النتائج الخمس الأولى أو أكثر إن أحببت. بعد ذلك قرّر أيُّ المواقع ستوفّر المعلومات المناسبة لك. جرّب تطبيق قاعدة CSS السابقة (أي القاعدة padding: 5px) على عنصر h2 في مشروعنا. الحالة الثانية: نريد تغيير أسلوب عرض أحد العناصر ولكن لا نعرف خاصية CSS المناسبة ابحث في الانترنت عن الشيء الذي ترغب أن تقوم به، متبوعًا بكلمة css. ربما تحتاج إلى إجراء عدة عمليات بحث قبل أن تجد ضالّتك. فإذا أردنا مثلًا تغيير حجم النص. في هذه الحالة استخدم الكلمات التالية text size css في محرّك البحث. ومرّة أخرى لا تكتفي بالنتيجة الأولى فقط من نتائج البحث. قيم CSS لقد صادفنا قبل قليل بعضًا من قيم CSS، فمثلًا القيم الست عشرية مثل ffffff# أو تلك القيم المتعلّقة بتعيين قياسات الأحجام مثل 5px. يتوجّب علينا أحيانًا وبحسب خاصية CSS المستخدمة استخدام كلمات مُعيّنة مثل left أو right لمحاذاة النصوص على سبيل المثال. أكثر قيم CSS المستخدمة هي القيم المتعلّقة بتعيين قياسات الأحجام. لذلك سنتناولها بشيء من التفصيل. غالبًا ما يُعرّف الحجم باستخدام البيكسل Pixel، ونرمز له اختصارًا بالرمز px. p { font-size: 16px; } توجد إمكانية أخرى وهي تعيين الأحجام باستخدام النسبة المئوية Percentage. دائمًا ما تكون النسب المئوية نسبيّةً بالنسبة للعنصر الأب. في المثال التالي سيشغل العنصر p ما مقداره 60% من عرض width العنصر الأب الذي يقع ضمنه، في حين سيشغل العنصر الأب عرض النافذة كاملًا. p { width: 60%; } توجد وحدة قياس مشهورة أخرى للأحجام وهي em. والـ em ترتبط دومًا بحجم الخط. ويعني ذلك أنّنا إذا غيّرنا حجم الخط فإنّ حجم العنصر المُقاس بالوحدة em سيتغيّر أيضًا. كمثال على ذلك إذا كان لعنصر ما حجم خط مقداره 20px وعرض 5em، فسيكون عرض العنصر الفعلي في هذه الحالة 100px (أي 20 مضروبة بـ 5). انظر إلى الشيفرة التالية: p { font-size: 20px; width: 5em; } محددات الصنف في مشروعنا إذا أردنا تكبير العنوان الرئيسي في موقعنا: "Web Portfolio of Marco". فسيكون ذلك ممكنًا بتعريف قاعدة CSS للعنصر h1 كما يلي: h1 { font-size: 65px; } المشكلة التي ستصادفنا هنا أنّ جميع عناصر h1 الموجودة في المستند ستكبُر بتأثير هذه القاعدة. لذلك فإذا أردنا أن نُكبّر عنوان h1 الأوّل الموجود في المستند فسنستخدم لهذا الغرض مُحدّد صنف class selector. سنضيف السمة class إلى عنصر h1 المراد استهدافه، ويمكننا بالطبع إسناد أي قيمة لهذه السمة، سنختار القيمة title: <h1 class="title">Web Portfolio of Marco</h1> يمكننا الآن استهداف عنصر h1 هذا بكتابة قاعدة CSS بالشكل التالي: .title { font-size: 65px; } إذا جرت الأمور على ما يرام فستحصل على شكل شبيه بما يلي: سننتقل الآن إلى الدرس الرابع، والذي يتحدّث عن أدوات التطوير البرمجية المتاحة ضمن متصفّح الانترنت. ترجمة -وبتصرّف- للمقال HTML & CSS Tutorial - Part 3: Introduction to CSS لصاحبه Marco Jakob.
-
نواصل في هذا المقال استعراض أساسيات لغة جافا التي يحتاج كل مُطوّر أندرويد الإلمام بها. إذا لم تطّلع على الجزء الأول فأنصحك بقراءته أوّلا قبل مواصلة قراءة هذا المقال. المصفوفات في بعض الأحيان نحتاج إلى تخزين عدة بيانات من نفس النوع والتعامل معها، في هذه الحالة لا يتم استخدام عدة متغيرات للتعامل معها ولكن يتم استخدام المصفوفات Arrays. فالمصفوفات مجموعة متغيرات من نفس النوع وتربطها علاقة ببعضها فيتم تخزينها داخل متغير واحد من النوع مصفوفة، ويتم تعريف المصفوفة بالشكل التالي DataType [] arrayName = new DataType [arraySize]; فمثلا لتعريف مصفوفة من النوع int وتحتوي على 6 عناصر: int [] numArr = new int [6]; هكذا تم حجز 6 أماكن في الذاكرة للمصفوفة numArr، وللوصول لهذه الأماكن للتخزين فيها أو التعامل مع قيمها. numArr[0] = 10; numArr[1] = 5; ... numArr[5] = 3; والعنصر الأول يبدأ من صفر وتنتهي العناصر عند الرقم 5 لتصبح ست عناصر، ويتم التعامل بعد ذلك مع عناصر المصفوفة مثل المتغيرات فمثلًا لجمع رقم ما على إحدى قيمها numArr[3] = numArr[0] + 4; ويتم احتساب خطأ إذا تم الوصول إلى عنصر خارج حدود المصفوفة التي تم تعريفها مثل: numArr[7] = 2 ; //Error وتصنف النوع السابق من المصفوفات على أنه من المصفوفات الساكنة التي عند تحديد عدد عناصرها عند تعريفها فلا يمكن زيادة هذا العدد أو إزالة عناصر من المصفوفة. الدوال الدالة هي مجموعة من الأوامر التي تنفذ فقط عندما نقوم باستدعائها وتستخدم للقيام بالأوامر التي يتكرر استخدامها في مواضع مختلفة في البرنامج بسهولة دون إعادة كتابة الأسطر البرمجية مجددًا مما يجعل البرنامج أكثر وضوحًا. تتميز جافا باحتوائها على مجموعة كبيرة من الدوال الجاهزة التي يمكنك استعمالها مباشرة، كما يمكننا من إنشاء دوال خاصة تؤدي وظائف محددة. تعريف الدوال يتم على النحو التالي AccessModifier ReturnType methodName ( parameters List){ //Do some Actions here } ويحدد AccessModifier طريقة الوصول لهذه الدالة. ونوع البيانات التي سترجعه الدالة يتم تحديده في ReturnType. ثم يتم تحديد الاسم الخاص بالدالة، والـ parameters هي البيانات التي يتم تمريرها للدالة. public int add( int a , int b ) { int sum = a + b; return sum; } في المثال السابق كلمة public تعني أن هذه الدالة يمكن استدعاؤها من أي مكان في البرنامج وهي عكس private والتي تعني أن هذه الدالة لا يمكن استدعاؤها إلا من داخل الصّنف class الذي قام بتعريفها. بعد ذلك تم تحديد النوع الذي ستعيده الدالة عند استدعائها وهو int، والاسم الخاص بهذه الدالة add وتأخذ هذه الدالة قيمتين من النوع int لتعيد ناتج جمعهما. لاستدعاء هذه الدالة من أي مكان داخل البرنامج يتم كتابة اسم الدالة كما بالشكل: int result = add (10 , 15); الأصناف وهو الوحدة الأساسية المبني عليها باقي مفاهيم البرمجة كائنية التّوجّه، وهو عبارة عن وعاء كبير يحتوي على متغيرات ودوال وكائنات. وعند تعريف صنف Class جديد يصبح لديك نوع بيانات جديد يمكنك استخدامه مع باق الأنواع الموجودة بالفعل. لتعريف صنف يتم كتابة كلمة class واختيار اسم له، ثم فتح أقواس تحدد بدايته ونهايته. class ClassName { } تسمى المتغيرات التي يتم تعريفها داخل الصّنف بالخصائص attributes، وتسمى الدوال بالتّوابع methods (يعني سنستعمل "دالة" و "تابع" للدّلالة على نفس المفهوم). وتسمى المتغيرات من الأصناف بالكائنات Objects، فلا يمكن إنشاء كائن دون وجود صنف له. إذا أردنا أن نكتب برنامجًا يعبر عن مكتبة وما تحتويه من كتب فيمكن اعتبار الكتاب على أنه كائن ولإنشاء هذا الكائن ينبغي وجود Class له يحتوي على الخصائص الأساسية لجميع الكتب. class Book{ private String bookName; private String authorName; private int bookCode; public void setBookName(String name){ bookName = name; } public String getBookName(){ return bookName; } public void setAuthorName(String name){ authorName = name; } public String getAuthorName(){ return authorName; } public void setBookCode(int code){ bookCode= code } public int getBookCode(){ return bookCode; } } هكذا قمنا بصنع الصّنف الأساسي لجميع الكتب ويحتوي على خصائص مثل اسم الكتاب واسم الكاتب وهذه البيانات معرّفة private أي كما ذكرنا سابقًا لا يمكن الوصول لها أو تغييرها إلا داخل الصّنف المعرّفة بداخله فقط، وهناك عدة توابع فمنها ما يقوم بتخزين قيم في المتغيرات الخاصة بصنف ومنها ما يعيد القيم المخزنة. لاحظ أننا قمنا بتعريف التوابع كـ public وذلك حتى نستطيع الوصول لها واستدعائها من أي مكان في البرنامج. هناك بعض الدوال التي تم تعريف نوع المُخرجات returnType لها من النوع void وتعني أنها لا تعيد شيئًا. ولإنشاء كائن من هذا الصّنف نكتب: Book b1 = new Book(); Book book2 = new Book(); النصف الأيسر كما اعتدنا سابقًا عند إنشاء متغير جديد، أما النصف الأيمن فيتكون من شقين كلمة new وتعني إنشاء هذا المتغير وحجز مساحة له في الذاكرة واسم الصّنف وبعده أقواس وهو ما يسمى بالـ Constructor. ولأي من هذه الكائنات إذا أردنا وضع قيم للخصائص التي بداخلها يتم على النحو التالي: b1.setBookName(“Learn Java”); book2.setBookName(“Intro to programming Language”); book2.setBookCode(101); String name = b1.getBookName(); وهذه بعض الأمثلة لكيفية استدعاء التّوابع المختلفة من داخل class وباستخدام الكائن، لكل كائن خصائصه الخاصة لا يتشارك فيها مع باق الكائنات التي من نفس Class فلكل منها اسم كتاب يتم تخزين فيه نص معين لا يتشاركان فيه. ولاستدعاء التوابع يتم استخدام (.) بعد اسم الكائن ثم كتابة اسم التابع وتمرير المتغيرات التي تتعامل معها إن وجدت حسب تعريفنا للتّابع داخل الصّنف. لاحظ أن عند إنشاء صنف جديد نبدأ اسمه بحرف كبير دائمًا، ويمكنك إنشاء أي عدد من الكائنات من الصّنف كما ذكرنا في المثال السابق. تابع البناء Constructor تتواجد داخل كل صنف تابع خاص يُدعى Constructor يتم استدعاؤه أثناء إنشاء كائن جديد فهو تابع مهم جدًا لإنشاء الكائنات ويقوم المترجم بإنشاء هذا تابع بناء فارغ بشكل افتراضي إذا لم يتم تعريفها من قبل المطوّر. وتكتب كالتالي: public ClassName(parameter List){ //Do some Actions here } يجب أن يكون اسم تابع البناءconstructor نفس اسم الصنف ومن النوع public ويمكنك تعريف أكثر من constructor داخل نفس الصّنف ولا يوجد نوع إرجاع returnType لـلـ constructor. يمكننا من خلال constructor إدخال قيم مباشرة في الخصائص الموجودة في الكائن بدلًا من استدعاء دالة لكل خاصية. class Book{ private String bookName; private String authorName; private int bookCode; public Book(String name,String author,int code){ bookName = name; authorName = author; bookCode = code; } public String getBookName(){ return bookName; } public String getAuthorName(){ return authorName; } public int getBookCode(){ return bookCode; } } في كل مرة يتم إنشاء كائن جديد، يجب استدعاء تابع البناء constructor حتى يتم إنشاء هذا الكائن. لإنشاء كائنات بعد هذا التعديل: Book b1 = new Book (“Learn Java”,”M.K Dave”, 150); هنا تم تعريف كافة الخصائص لحظة إنشاء الكائن، ويتم التعامل مع هذا الكائن بشكل طبيعي كما تعاملنا معه مسبقًا. الوراثة يعتبر مفهوم الوراثة من أهم المفاهيم في البرمجة كائنية التّوجّه ويعني يمكننا إنشاء أصناف ترث من صنف آخر الخصائص والتّوابع المتواجدة به دون تعريفها من جديد، مما يسمح بالتركيز على الخصائص والتّوابع التي يتميز بها الصّنف الجديد. وتستخدم الكلمة extends لتطبيق مفهوم الوراثة: class Shape{ protected int width; protected int height; public void setWidth(int a){ width = a; } public void setHeight(int b){ height = b; } } إذا كان لدينا هذا الصّنف وهو يُمثل شكلًا عامًا له طول وعرض وأردنا أن ننشئ شكلًا آخر (مربع) يرث من هذا الصّنف ويضيف خصائص أكثر: class Square extends Shape{ public int getArea(){ return width * height; } } وبذلك أصبح Square يحتوي على الدالتين setWidth و setHeight بالإضافة للدالة الجديدة التي قام بتعريفها getArea، وبذلك استطعنا أن نعطي المزيد من الخصائص للشكل الجديد. وإذا أردنا أن نصنع شكلًا آخر (مثلث): class Triangle extends Shape{ public int getArea(){ return 0.5*width * height; } } لا يمكن للصّنف أن يرث من أكثر من أب. وبتطبيق هذا المفهوم على الدرس السابق نجد أن أندرويد يحتوي على صنف اسمه View وهو الأب لكل العناصر الخاصة بواجهة المستخدم ويحدد الخصائص الأساسية والمشتركة بينهم جميعًا ويرث منه العناصر الخاصة مثل TextView وهو View ولكن يعرض نصًا فقط أو Button وأيًضا View قابل للضغط ويقوم بمهمة محددة عند الضغط عليه. وإذا قمنا بالتعديل على المثال السابق واستخدمنا في Shape تابع البناء لتعريف الطول والعرض: class Shape{ protected int width; protected int height; public Shape(int a ,int b){ width = a; height = b; } } ينبغي علينا أن نعرف دالة بناء للصّنف التي ترث منه: class Square extends Shape{ public Shape(int w ,int h){ super(w,h); } public int getArea(){ return width * height; } } وتستخدم super لاستدعاء Constructor الخاص بالأب وتمرير له القيم التي يحتاجها، وذلك لأن عند إنشاء كائن من Square والذي يرث من Shape: Sqaure s = new Square(10,10); يتم بناء الأب أولًا Shape ثم الابن Square حتى يكتمل بناء الكائن s بشكل صحيح. لذا يتم تمرير في دالة البناء الخاصة بالابن ما تحتاجه دالة البناء الخاصة بالأب، ولم نقوم بذلك قبل التعديل لأننا كنا نستعمل دالة بناء فارغة في الأب. التحويل من نوع بيانات إلى آخر تمكننا لغات البرمجة من التحويل من نوع بيانات إلى آخر باستخدام مفهوم Casting، فمثلًا إذا كان لدينا متغير من النوع double: double d = 12.5478; ونريد تحويله إلى رقم صحيح: int x = (int) d; نفعل ذلك عن طريق كتابة نوع المتغير بين القوسين وبعدها اسم المتغير الذي نريد تحويله كما في المثال. وهناك بعض الشروط على التحويل بين الأنواع بهذه الطريقة فلا يمكن تحويل String إلى int مثلًا لاختلاف النوعين عن بعضهما. ويمكن تحويل من صنف إلى آخر شريطة أن يكون بينهما علاقة الوراثة. Shape sh = new Shape(15,15); Square s = (Square) sh; بهذا نكون قد وصلنا إلى نهاية هذا الدّرس، في انتظار تجربتكم وآرائكم. إن كانت لديك أيّة أسئلة فلا تتردّد في طرحها.
-
تستخدم الأصناف في البرمجة الغرضية التوجيه من أجل إنشاء الأغراض، وإعطاء القيم الابتدائية للمتغيرات المستنسخة. لكل صنف اسم خاص يعبر عن نوعه ووظيفته. تعريف الأصناف جميع الأصناف في Ruby مستنسخة من الصنف الرئيسي Class، حيث يتم تعريف الأصناف في لغة Ruby كما يلي: يبدأ الصنف بالكلمة الدلالية class وبعدها يكتب اسم الصنف. يجب أن يبدأ اسم الصنف بحرف كبير. وفي حال كان اسم الصنف مكون من عدة كلمات، يتم تمييز هذه الكلمات بكتابة أول حرف من كل كلمة بالنمط الكبير وباقي الأحرف صغيرة، كونه لا يمكن استخدام فراغات فاصلة بين الكلمات في أسماء الأصناف. يمكن أن يحتوي الصنف على متغير الصنف، والمتغير المستنسخ، ودالة الصنف، بالإضافة لإمكانية استدعاء الدوال الأخرى مثل الدالة attr_accessor (دوال الولوج). ينتهي الصنف بالكلمة الدلالية end. يوضح المثالي التالي طريقة تعريف الأصناف: class SampleClass # some code describing the class behavior def sample_method end end الاستنساخ يتم إنشاء الأغراض من الأصناف بعملية تسمى الاستنساخ، حيث يصبح الغرض عبارة عن نسخة من الصنف الذي تم استنساخه عنه. تنفذ عملية الاستنساخ باستخدام الدالة new مع اسم الصنف المراد إنشاء الغرض منه كما هو موضح بالمثال التالي: std1 = Student.new(parameters) المتغيرات المستنسخة وهي المتغيرات التي يتم تعريفها ضمن دوال الأصناف بإضافة الرمز @ لبداية اسم المتغير. وينحصر نطاقها فقط على الغرض المُنشأ من الصنف، ولا يمكن الولوج إليها وإظهار قيمها أو تعديلها إلاّ من خلال الدالة التي عُرّفت فيها (دوال الولوج). سيتم توضيح هذه المتغيرات من خلال المثال التالي: class Calculation @x = 100 def add_num @x = 200 end def show_data puts 'Value of x is : '، @x end end instance = Calculation.new instance.show_data instance.add_num instance.show_data Output Value of x is : Value of x is : 200 نلاحظ أن الخرج الأول الدالة show_data من الغرض instance والمُنشأ من الصنف Calculation فارغ، لأن قيمة المتغير المستنسخ @x مازالت nil (لا شيء) بالرغم من أنه تم إسناد القيمة 100 لهذا المتغير في بداية تعريف الصنف Calculation، وذلك لأن طريقة التعريف هذه خاطئة للمتغير المستنسخ @x، حيث يجب أن يتم تعريفه ضمن دوال الصنف فقط، بالتالي تم تجاهل هذا التعريف. أما الخرج الثاني لهذه الدالة أصبح 200 لأنه تم تنفيذ الدالة add_num (التي عُرّف فيها) من الغرض instance والتي قامت بتهيئة المتغير المستنسخ @x بالقيمة 200. دوال الولوج وهي دوال الصنف التي تعرَّف ضمنها المتغيرات المستنسخة، حيث تُستخدم هذه الدال من أجل الولوج إلى المتغيرات المستنسخة للأغراض من أجل قراءة قيمها أو تعديلها. يوضح المثال التالي هذه الدوال: class MyData def set_data(n) @x = n end def get_data @x end end d = MyData.new puts d.set_data(1000) puts d.get_data Output 1000 1000 حيث يحتوي الصنف MyData على دالتي ولوج هما: الدالة set_data لتعديل قيمة المتغير المستنسخ @x، والدالة get_data لقراءة قيمة هذا المتغير المستنسخ. متغيرات الأصناف وهي المتغيرات المعرفة في بداية الصنف بإضافة الرمز @@ لبداية اسم المتغير، حيث يتم مشاركة قيمها مع جميع الأغراض المُنشأة من هذا الصنف وكذلك مع جميع الأصناف المورَّثة من هذا الصنف، على عكس المتغيرات المستنسخة التي لا يتم مشاركة قيمها بين الأغراض. والمثال التالي يوضح هذه المتغيرات: class MyData @@x = 1 def add_n @@x = @@x + 10 end def value @@x end end ins1 = MyData.new ins2 = MyData.new puts ins1.value ins1.add_n puts ins1.value puts ins2.value Output 1 11 11 المتغيرات المستنسخة للأصناف تختلف هذه المتغيرات عن متغيرات الأصناف، بأنه يتم مشاركة قيمها مع الأغراض المُنشأة من هذا الصنف فقط، كما هو موضح بالمثال التالي: class Employee class << self; attr_accessor :instances; end def store self.class.instances ||= [] self.class.instances << self end def initialize name @name = name end end class Overhead < Employee; end class Programmer < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size # => 2 puts Programmer.instances.size # => 1 Output 2 1 دوال الأصناف يتم تعريف دوال الأصناف بنفس طريقة تعريف دوال Ruby الاعتيادية باختلاف وحيد، وهو إضافة اسم الصنف أو الكلمة الدلالية self وبعدها نقطة قبل اسم الدالة. يتم استدعاء دوال الأصناف على مستوى الصنف الذي عرفت فيه دون الحاجة لوجود غرض مُنشأ من هذا الصنف. كما يمكن لدوال الأصناف الولوج إلى متغيرات الأصناف لكنها لا تستطيع الولوج إلى المتغيرات المستنسخة (فقط دول الولوج تستطيع ذلك). يوضح المثال التالي كيفية تعريف واستدعاء دوال الأصناف: class Student def self.no_students puts 'There are five hundred students in my school.' end end Student.no_students Output There are five hundred students in my school. مستويات الوصول لدوال الأصناف هنالك ثلاث مستويات للوصول إلى دوال الأصناف هي: الدوال العامة: إن دوال الأصناف في Ruby هي دوال عامة بشكل افتراضي، أي يمكن لأي أحد الوصول إليها، وتشكل واجهة التخاطب للأصناف، حيث يتم من خلالها تبادل المعطيات بين الصنف والعالم الخارجي. الدوال الخاصة: يتم تحديدها بالكلمة الدلالية private، وهي للاستخدام الداخلي فقط ضمن الصنف (يمكن استدعائها خارجياً عن طريق دالة عامة تحوي على هذه الدالة الخاصة مثل whisper_louder)، حيث يمكن الوصول إليها واستخدامها فقط من قبل الدوال الموجودة ضمن تعريف الصنف نفسه أو الأصناف المورَّثة منه، بشرط أن يكون استدعائها صريح (مثل whisper) دون أن تكون مضمّنة في غرض آخر حتى ولو كان من نفس الصنف (مثل self.whisper). عند الرغبة باستدعاء هذه الدوال خارج الصنف عن طريق استدعائها بشكل مضمّن في الدوال العامة كما هو موضح بالمثال التالي: class Person def speak puts "Hey، Tj!" end def whisper_louder whisper end def whisper_louder_with_self self.whisper end # private methods are for internal usage within the defining class private def whisper puts "His name's not really 'Tj'." end end a_hater = Person.new a_hater.speak # استدعاء دالة عامة a_hater.whisper # استدعاء دالة خاصة خارج الصنف a_hater.whisper_louder # استدعاء دالة عامة تحوي على دالة خاصة صريحة a_hater. whisper_louder_with_self # استدعاء دالة عامة تحوي على دالة خاصة مضمنة Output "Hey، Tj!" NoMethodError "His name's not really 'Tj'." NoMethodError الدوال المحمية: ويتم تحديدها بالكلمة الدلالية protected، وهي نفس الدوال الخاصة بفارق أساسي هو إمكانية استدعائها داخليا بشكل مضمّن في غرض من نفس الصنف كما هو موضح بالمثال التالي: class User def say_secret_with_self self.secret end protected def secret "secret" end end u = User.new u.say_secret_with_self # => استدعاء دالة عامة تحوي على دالة محمية مضمنة Output "secret" دوال القراءة والكتابة الشخصية تُنشأ المتغيرات المستنسخة لكل غرض مُنشأ من صنف، لتستخدم بشكل أساسي ضمن هذا الغرض، لكنها لا تشبه الدوال الخاصة من حيث اقتصارها على الاستخدام الداخلي. حيث يمكن إظهار وتعديل قيمها من خارج الغرض، وذلك عن طريق تعريف دوال القراءة والكتابة الشخصية (مشابه لدوال الولوج). يوضّح المثال التالي طريقة تعريف دالة القراءة (student_name) التي تقوم بقراءة قيمة المتغير المستنسخ: class Student def initialize(student_name، id) @student_name = student_name @id = id end def student_name @student_name end end std1 = Student.new("Sara"، 5) puts std1.student_name Output Sara والمثال التالي يوضّح طريقة تعريف دالة الكتابة (sname=) والتي تقوم بتغيير قيمة المتغيّر المستنسخ: class Student def initialize(sname) @sname = sname end def sname @sname end def sname=(new_name) @sname = new_name puts @sname end end # you could the access @sname variable as a = Student.new('Sara') puts a.sname a.sname ='Robin' Output Sara Robin دوال القراءة والكتابة الافتراضية يمكن تنفيذ جميع وظائف قراءة وتعديل قيم المتغيرات المستنسخة عن طريق الدوال attr_accessor و attr_reader و attr_writer والموجودة في لغة Ruby بشكل افتراضي، حيث تستخدم الدالة attr_accessor لتنفيذ عمليتي القراءة والكتابة معًا في هذه المتغيرات كما هو موضح بالمثال التالي: class Student def initialize(sname) @sname = sname end attr_accessor :sname end # attr_accessor generates variable @sname accessor methods automatically: a = Student.new('Sara') puts a.sname a.sname = 'Robin' puts a.sname Output Sara Robin أما الدالة attr_reader فتستخدم لتنفيذ عملية القراء فقط، والدالة attr_writer فتستخدم لتنفيذ عملية الكتابة فقط كما هو موضّح بالمثال التالي: class Student attr_reader :sname attr_writer :sname end s1 = Student.new s2 = Student.new s1.sname = "Sara" s2.sname = "Robin" puts s1.sname puts s2.sname Output Sara Robin ثوابت أصناف لغة Ruby يمكن أن تعرّف الثوابت ضمن الأصناف، لكن يمكن الولوج إليها من خارج الأصناف على خلاف المتغيرات المستنسخة التي تحتاج لدوال خاصة لتنفيذ ذلك، ويوضح المثال التالي طريقة تعريف الثوابت ضمن الأصناف، واستخدامها داخل وخارج الأصناف: class ConstClass Color1='Red' Color2='Green' Color3='White' def show puts "#{Color1} #{Color2} #{Color3}" end end puts( ConstClass::Color1) ConstClass.new.show Output Red Red Green White الوراثة وهي العلاقة بين صنفين، حيث يمكن لصنف أن يرث الوظائف والمتغيرات من صنف أعلى منه مرتبة، ويسمى عندها الصنف الموروث بصنف الأب أو الصنف الأساس، ويسمى الصنف الوارث بصنف الابن، هذا ولا تسمح Ruby بالوراثة المتعددة، أي لكل صنف في Ruby صنف أساس واحد. يوضح المثال التالي طريقة توريث جميع الوظائف والمتغيرات الغير خاصة لصنف آخر (صنف الابن): class Student def schooldetails puts 'There are 700 students in our school.' end end class Classfive < Student # < means inherit def class5 puts 'There are 75 students in Class V.' end end instance = Classfive.new instance.class5 instance.schooldetails Output There are 75 students in Class V. There are 700 students in our school. حيث تم في هذا المثال توريث الصنف Classfive من الصنف Student. يستطيع الصنف الابن الولوج إلى دوال الصنف الأب، عن طريق الكلمة الدلالية super، كما هو موضّح بالمثال التالي: class Student def schooldetails puts 'There are 700 students in our school.' end end class Classfive < Student # < means inherit def schooldetails super puts 'There are 75 students in Class V.' end end instance = Classfive.new instance.schooldetails Output There are 700 students in our school. There are 75 students in Class V. يمكن في الوراثة المتعددة الولوج إلى دوال الصنف الأب باستخدام أسماء بديلة عن طريق الدالة alias، كما هو موضّح بالمثال التالي: class V def ccccc "Class V " end end class VI < V alias vcv ccccc def ccccc vcv + "VI " end end class VII < VI def ccccc vcv + "VII " end end puts V.new.ccccc puts VI.new.ccccc puts VII.new.ccccc Output Class V Class V VI Class V VII حيث تم استخدام الاسم vcv كبدل لاسم الدالة ccccc المعرّفة في الصنف V، ثم تم استخدام هذا الاسم البديل في الأصناف الوارثة كون اسم دوالها هو نفس الاسم الأصلي لدالة الصنف V. الأصناف الفريدة تحتوي لغة Ruby على طريقة لتعريف دوال محددة من أجل غرض معين، حيث تسمى هذه الدوال بالدوال الفريدة. فعند القيام بتعريف دوال فريدة في غرض ما، فإن لغة Ruby تقوم تلقائياً بإنشاء صنف جديد لاحتواء هذه الدوال الفريدة فقط، والذي يسمى عندها بالصنف الفريد. ويفيد هذا الصنف عند الرغبة باستدعاء دواله في أقسام مختلفة من التطبيق البرمجي، مثل تأمين الاتصال مع النظام الخارجي أو الولوج لقاعدة البيانات أو غيرها. ويوضح المثال التالي هذه الأصناف: tutorial1 = String.new def tutorial1.size "Learn Ruby" End tutorial2 = String.new puts tutorial1.singleton_methods puts tutorial2.singleton_methods Output Learn Ruby String 0 حيث تم استخدام الدالة singleton_method للحصول على قائمة أسماء جميع الدوال الفريدة الموجودة في الغرض.هذا ويمكن الولوج إلى صنف الغرض الفريد باستخدام الصيغة التالي: class << object كما هو موضح بالمثال التالي: arra1 = [] class << arra1 def foo "Hello World!" end end puts arra1.singleton_methods Output foo ترجمة –وبتصرّف- للمقال Ruby Classes حقوق خلفية الصورة البارزة محفوظة لـ all-free-download.com
-
في هذا الدرس من سلسلة تعلّم CSS، سنتطرق إلى كيفيّة تطبيق الأنماط على العناصر بطريقة انتقائية، وكيف يكون للمحدّدات المختلفة درجات مختلفة من الأولويّة. سنضيف بعض الخواص (attributes) إلى المستند ثم نستخدمها في ورقة الأنماط. فهرس السلسلة: مدخل إلى أوراق الأنماط المتتالية (CSS). آلية عمل تعليمات CSS داخل المتصفحات. المحددات (Selectors) في CSS. (هذا الدرس) كيفية كتابة تعليمات CSS يسهل قراءتها. تنسيق نصوص صفحات الويب باستخدام CSS. التعامل مع الألوان في CSS. إضافة محتوى إلى صفحة ويب باستخدام CSS. تنسيق القوائم (Lists) في CSS. تعرف على الصناديق (Boxes) في CSS. رصف العناصر (Layout) في CSS. الجداول (Tables) في CSS. التعامل مع أجهزة العرض المختلفة والمطبوعات في CSS. ما هي المحددات؟ لـCSS مجموعة من المصطلحات الخاصة الّتي تصف اللّغة. كنّا قد أنشأنا سطرًا في ورقة الأنماط محتواه: strong { color: red; } وفي مصطلحات CSS نقول عن هذا السّطر أنّه قاعدة (rule). تبدأ هذه القاعدة بالكلمة strong والّتي نُسمّيها المحدِّد (selector)، لأنّه يحدّد العناصر في DOM الّتي تُطبّق عليها القاعدة المعنيّة. تفاصيل أكثر نُسمّي الجزء ضمن القوسين من القاعدة التّصريح (declaration). الكلمة color تُسمّى خاصّة (property)، وقيمتها (value) هي red. تفصل الفاصلة اللاتينية المنقوطة ; بين أزواجٍ من القيم والخواص ضمن التّصريح ذاته. سنطلق على المُحدّد strong في هذه السلسلة وصف محدّد لوسم (tag selector)، إلّا أنّه في مصطلحات CSS يُسمّى محدّد لنوع (type selector). يُفصِّل هذا الدرس من السلسلة المحدّدات التي يمكن استخدامها في قواعد CSS، فبالإضافة إلى أسماء الوسوم، يمكن استخدام قيم الخواص (attributes) في المُحدّدات، وهذا ما يسمح لقواعد CSS بتحديد أكثر انتقائيّة للعناصر. للخاصّتين class وid وضع خاصّ في CSS. محددات الأصناف (Class selectors) استخدم الخاصّة class في HTML لإسناد صنف إلى العنصر، بإمكانك اختيار أي اسم للصنف، ويمكن للصّنف الواحد أن يُسند إلى عدّة عناصر في الصّفحة. لاستخدام محدّد الصنف في CSS اكتب اسم الصّنف مسبوقًا بنقطة (.). محددات المعرفات (ID selectors) استخدم الخاصّة id في عنصر لإسناد مُعرّف إلى هذا العنصر، بإمكانك اختيار أي اسم للمُعرّف، ولكن ينبغي أن لا يكون لعنصرين في الصّفحة المُعرّف ذاته. لاستخدام مُحدّد المعرّف في CSS، اكتب اسم المُعرّف مسبوقًا برمز #. مثال: لوسم HTML التالي خاصّتا الصّنف والمعرّف معًا: <p class="key" id="principal"> قيمة الخاصّة id، وهي principal يجب أن تكون فريدة لا تتكرّر في كامل المستند، ولكن يمكن استخدام الصّنف key لأي عدد من العناصر. القاعدة التالية في CSS تجعل كل العناصر في المستند خضراء اللون، حتى وإن تكن جميعها عناصر <p>: .key { color: green; } القاعدة التالية تجعل العنصر الوحيد ذا المعرّف principal عريض الخطّ: #principal { font-weight: bolder; } محددات الخواص (Attribute Selectors) لا تقتصر CSS على الخاصتين class وid، بل يمكنك تحديد خواصّ أخرى باستخدام الأقواس المربّعة []. ضع اسم الخاصة ضمن القوسين وأتبعه بعامل ثم قيمة (غير إلزاميّين). يمكن أيضًا جعل المطابقة غير حسّاسة لحالة الحروف اللّاتينية بإضافة "i" بعد القيمة، ولكن هذه الميزة غير مدعومة في معظم المتصفّحات. أمثلة: [disabled]: تحدّد كل العناصر التي تملك الخاصّة "disabled". [type='button']: تحدد كل العناصر من النوع "button". [class~=key]: تحدّد العناص ذات الصّنف "key" بالضّبط وليس أي كلمة تحوي ضمنها "key" مثل "keyed" أو "monkey". تطابق من حيث الوظيفة المُحدّد .key. :[lang|=es] تحدّد العناصر المحدّدة على أنّها باللّغة الإسبانية. هذا يتضمن العناصر "es" و"es-MX" ولكن ليس "eu-ES". [title*="example" i] يحدّد العناصر التي يحوي عنوانها "example"، متجاهلًا حالة الحروف اللاتينيّة، قد لا يطابق هذا العنصر أي شيء في المتصفّحات الّتى لا تدعم الإشارة "i". a[href^="https://"] يحدّد الرّوابط الآمنة. img[src$=".png"] يحدّد الصور بصيغة PNG بطريقة غير مباشرة، إلّا أنه لا يحدد الصور بصيغة PNG إن كانت روابطها لا تنتهي بالحروف ".png". محددات الأصناف الزائفة (pseudo-classes) تعني كلمة "الصّنف الزائف" في CSS حالة خاصّة للعناصر التي يُراد تحديدها، فمثلًا إضافة :hover إلى المحدّد يؤدّي إلى تطبيق القاعدة عندما يُمرّر المستخدم مؤشّر الفأرة فوق العنصر الذي يستهدفه المُحدّد. تسمح الأصناف الزائفة (والعناصر الزائفة) بتطبيق تنسيق على عنصر عندما يخضع لعوامل خارجيّة كتاريخ التّصفّح (:visited مثلًا)، أو حالة محتواه (مثل :checked)، أو موضع الفأرة (مثل :hover)، يمكنك مراجعة القائمة الكاملة للمحدّدات على صفحة توصيف مُحدّدات CSS3. الصيغة selector:pseudo-class { property: value; } قائمة بالأصناف الزائفة :link :visited :active :hover :focus :first-child :last-child :nth-child :nth-last-child :nth-of-type :first-of-type :last-of-type :empty :target :checked :enabled :disabled عمق التحديد (Specificity) قد يكون لعدّة قواعد مُحدّدات تستهدف العنصر نفسه، فإن كانت الخاصّة غير مكرّرة ضمن هذه القواعد، لن يحدث تعارض، وستُعيّن هذه الخاصة على العنصر، أما إن كانت الخاصة واردة في أكثر من قاعدة، فإن CSS تطبّق الخاصّة الأكثر عمقًا في التّحديد، فالمُحدّد الّذي يستهدف العنصر بُمعرّفه أكثر عمقًا من مُحدّدات الصّنف والأصناف الزائفة والخواصّ الأخرى، وهذه بدورها أكثر عمقًا من مُحدّدات الوسوم والعناصر الزائفة. تفاصيل أكثر يمكنك أيضًا جمع المُحدّدات، مُنشِئًا مُحدّدًا أكثر عمقًا، فمثلًا يحدّد key. كل العناصر من الصّنف key، والمُحدّد p.key يحدّد فقط العناصر <p> ذات الصّنف key. إن تساوت المُحدّدات بدرجة العمق، كانت الأولوية للقاعدة الّتي ترد آخرًا في ورقة الأنماط. التحديد تبعا للعلاقات بين العناصر يمكن في CSS تحديد العناصر بناءً على علاقتها بعناصر أخرى، ويمكن بذلك إنشاء مُحدِّدات أكثر عمقًا. المحددات الشائع استخدامها بناء على علاقة العناصر المُحدّد يُحدِّد A E أي عنصر E يكون من سلالة A (أي ابنه أو ابن ابنه...) A > E أي عنصر E يكون ابنًا مباشرًا للعنصر A E:first-child أي عنصر E يكون أوّل ابن لأبيه B + E أي عنصر E يكون أول أخٍ لعنصر B (أي الابن التالي لنفس الأب) يمكن أيضًا جمع أيّ من هذه المُحدّدات للتّعبير عن علاقات أكثر تعقيدًا. يمكن استخدام الرّمز * للإشارة إلى "أي عنصر". مثال لجدول HTML التالي خاصّة id، ولكن ليس لسطوره وخلاياه أيّة مُعرّفات: <table id="data-table-1"> ... <tr> <td>Prefix</td> <td>0001</td> <td>default</td> </tr> ... القواعد التالية تجعل الخليّة الأولى في كل سطر عريضة الخطّ، والثّانية ذات خطّ ثابت العرض، ولا تنطبق هذه القواعد إلا على الجدول السّابق المستند: #data-table-1 td:first-child {text-decoration: underline;} #data-table-1 td:first-child + td {text-decoration: line-through;} هذه هي النّتيجة: تفاصيل أكثر في الحالة العاديّة، كلّما زاد عمق المُحدّد، زادت أولويّته، وبهذا يمكنك تجنب إضافة العديد من الأصناف والمعرّفات على أوسم كثيرة في المستند. عندما يصبح حجم المستند ضخمًا وتكون السّرعة مطلوبة، يمكنك تحسين أداء التنسيقات بتجنبّ القواعد المعقدة المبنيّة على العلاقات بين العناصر. لمزيد من الأمثلة عن الجداول، راجع صفحة الجداول في مرجع CSS. تدريب: استخدام محددات الأصناف والمعرفات حرّر ملفّ HTML الّذي تتدرّب عليه، كرّر الفقرة بنسخها ولصقها. أضف الخاصّتين id وclass إلى النّسخة الأولى من الفقرة، وأضف id إلى الثّانية كما في المثال أدناه، أو يمكنك نسخ كامل المثال: <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Sample document</title> <link rel="stylesheet" href="style1.css"> </head> <body> <p id="first"> <strong class="carrot">C</strong>ascading <strong class="spinach">S</strong>tyle <strong class="spinach">S</strong>heets </p> <p id="second"> <strong>C</strong>ascading <strong>S</strong>tyle <strong>S</strong>heets </p> </body> </html> عدّل CSS مُستبدلًا كامل محتوى الملفّ: strong { color: red; } .carrot { color: orange; } .spinach { color: green; } #first { font-style: italic; } احفظ الملفّات وحدّث المتصفّح لترى النّتيجة: بإمكانك تغير ترتيب الأسطر في CSS للتأكد من أن ترتيبها لا يؤثّر على النّتيجة، لأن مُحدّدات الأصناف carrot. و spinach. مُقدَّمَة على مُحدّد الوسم strong، ومُحدّد المُعرّف first# مُقدّم عليها جميعًا. تمرين بدون تعديل ملف HTML، أضف قاعدة وحيدة إلى CSS تبقي كل الحروف الأولى بنفس لونها الحالي، ولكن تجعل الحروف الأخرى في الفقرة الثّانية زرقاء: عدّل القاعدة الّتي أضفتها للتوّ بحيث تجعل الفقرة الأولى زرقاء أيضًا (بدون تعديل أي شيء آخر): شاهد الحل جعل الفقرة الثانية زرقاء اللون أضف قاعدة تستهدف #second وتصريحًا color: blue; كما يلي: #second { color: blue; } يمكن أيضًا استخدام مُحدّد أكثر عُمقًا مثل p#second. جعل الفقرتين زرقاوين غيّر مُحدّد القاعدة الجديدة ليصبح مُحدّد وسم <p>: p { color: blue; } قواعد الألوان الأخرى لها مُحدّدات أكثر عمقًا، لذا فهي تطغى على لون الفقرات الأزرق. مثال عملي: استخدام محددات الأصناف الزائفة أنشئ ملف HTML بالمحتوى التالي: <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Sample document</title> <link rel="stylesheet" href="style1.css"> </head> <body> <p>Go to our <a class="homepage" href="http://www.example.com/" title="Home page">Home page</a>.</p> </body> </html> عدّل ملف CSS مستبدلًا كلّ محتواه: a.homepage:link, a.homepage:visited { padding: 1px 10px 1px 10px; color: #fff; background: #555; border-radius: 3px; border: 1px outset rgba(50,50,50,.5); font-family: georgia, serif; font-size: 14px; font-style: italic; text-decoration: none; } a.homepage:hover, a.homepage:focus, a.homepage:active { background-color: #666; } احفظ الملفّين وحدّث المتصفّح لمشاهدة النّتيجة (ضع مؤشر الفأرة فوق الرّابط لمشاهدة التأثير): Go to our Home page تدريب: استخدام المحددات بناء على العلاقات بين العناصر والأصناف الزائفة بالإمكان الوصول إلى طرق معقدة لاستهداف العناصر بناء على العلاقات بين عناصرها والأصناف الزائفة، تشيع هذه التقنيات مثلًا بهدف إنشاء قوائم مُنسدلة باستخدام CSS فقط دون JavaScript. أساس هذه التقنية يعتمد على قواعد مشابهة لهذه: div.menu-bar ul ul { display: none; } div.menu-bar li:hover > ul { display: block; } بحيث تطبّق على بنية HTML مثل هذه: <div class="menu-bar"> <ul> <li> <a href="example.html">Menu</a> <ul> <li> <a href="example.html">Link</a> </li> <li> <a class="menu-nav" href="example.html">Submenu</a> <ul> <li> <a class="menu-nav" href="example.html">Submenu</a> <ul> <li><a href="example.html">Link</a></li> <li><a href="example.html">Link</a></li> <li><a href="example.html">Link</a></li> <li><a href="example.html">Link</a></li> </ul> </li> <li><a href="example.html">Link</a></li> </ul> </li> </ul> </li> </ul> </div> اطّلع على مثالنا الكامل كمساعدة. ما التالي؟ بدأت ورقة الأنماط تصبح معقّدة ومتشابكة، سنناقش كيف نُبسّطها ونجعلها سهلة القراءة في الدّرس القادم: كيفية كتابة تعليمات CSS يسهل قراءتها. ترجمة -وبتصرّف- للمقال Selectors من سلسلة Getting started with CSS على شبكة مطوّري Mozilla.