تعلم أساسيات البرمجة بلغة روبي – البرمجة غرضية التوجه

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

بالإضافة إلى هذه التدوينة، تتكون السلسلة من التدوينات التالية:

  1. تعلم أساسيات البرمجة بلغة روبي – مُقدِّمة
  2. تعلم أساسيات البرمجة بلغة روبي – أهلاً بالعالم!
  3. تعلم أساسيات البرمجة بلغة روبي – ثوابت، متغيرات، وأنواع بيانات
  4. تعلم أساسيات البرمجة بلغة روبي – بنى البيانات، بعض التكرار، وقليل من المنطق
  5. تعلم أساسيات البرمجة بلغة روبي – إرشادات لما بعد اﻷساسيات

أولا: الطُرق (Methods)

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

لاستخدام طريقة خاصة بك في روبي، كل ما عليك فعله هو الإعلان عنها ثم استدعاؤها.

def talk
    puts "The art of simplicity is a puzzle of complexity."
end

talk
# Prints the following:
# The art of simplicity is a puzzle of complexity.

عادة ما تصمم الطرق لتنفذ مهمة ما على مُعامِل (parameter) أو أكثر.

def say_hi_to(first_name, last_name)
  puts "Hi, " + first_name + " " + last_name + "!"
end

say_hi_to("Ruby", "Newbie") # Prints: Hi, Ruby Newbie!

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

def get_name
  return "Ruby Newbie"
end

puts get_name # Prints : Ruby Newbie

من الجدير بالذكر أن روبي ستعيد قيمة آخر تعبير في الطريقة بشكل افتراضي. لذلك، يمكننا كتابة طريقة get_name البسيطة في المثال السابق كما يلي:

def get_name
  "Ruby Newbie"
end

ثانيا: الأصناف (Classes) والكائنات (Objects)

يمكن النظر إلى الصنف على أنه “المخطط” الذي ستُبنى الكائنات استناداً عليه. يتم تعريف الصنف في روبي باستخدام الكلمة المفتاحية class، متبوعة بإسم الصنف مبدوءً بحرف كبير (upper case)، وينتهي التعريف بـend

class Person
end

يمكن إنشاء كائن من الصنف Person كما يلي:

someone = Person.new

قد يبدو الكائن someone خالياً وغير قادر على القيام بأي شيء، لكن الحقيقة غير ذلك. لنقم ببعض التجارب في irb

>> someone.object_id
=> -606876258
>> someone.nil?
=> false
>> someone.is_a? Integer
=> false
>> someone.class
=> Person

كل صنف في روبي هو صنف فرعي (subclass) من الصنف Object، وبذلك فهو يكتسب كل طرقه عبر الوراثة (inheritance) – المزيد عن ذلك في آخر التدوينة.

>> someone.methods
=> ["inspect", "tap", "clone", "poc", "public_methods", "__send__", "object_id", "instance_variable_defined?", "equal?", "freeze", "extend", "send", "say_hi_to", "methods", "pretty_print", "hash", "taguri", "dup", "to_enum", "instance_variables", "pretty_print_cycle", "eql?", "taguri=", "instance_eval", "id", "singleton_methods", "ri", "taint", "pretty_print_inspect", "to_yaml_style", "enum_for", "frozen?", "instance_variable_get", "instance_of?", "display", "to_a", "method", "type", "to_yaml_properties", "instance_exec", "protected_methods", "==", "===", "instance_variable_set", "kind_of?", "respond_to?", "to_yaml", "to_s", "class", "po", "pretty_print_instance_variables", "private_methods", "=~", "tainted?", "__id__", "untaint", "nil?", "pretty_inspect", "is_a?", "talk"]

معلومة: ذكرنا سابقا أن كل شيء في روبي عبارة عن كائن، وهذا يشمل اﻷصناف أيضاً. كل صنف هو نسخة من الصنف Class.

ثالثا: دمج الطرق والمتغيرات مع الكائنات

ماذا إذا أردنا أن نجعل كائنات الصنف Person أكثر إثارة للإهتمام بإضافة طرقنا الخاصة؟

class Person
  def say_hi
    puts "Hi!"
  end
end

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

class Person
  def initialize
    puts "I'm.. I'm alive!"
  end

  def say_hi
    puts "Hi!"
  end
end

متغيرات الكائن – أو النسخة – (instance variables) هي متغيرات يتم تعريفها في الصنف وتكون متوفرة لكل كائن من هذا الصنف فقط. يتم تعريف متغيرات الكائن في روبي بإضافة @ أمام اسم المتغير. لجعل هذه المتغيرات متاحة للقراءة من خارج الصنف المعين، يجب وضعها في طرق استرجاع (getter methods).

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

  def name
    @name
  end

  def age
    @age
  end
end

اﻵن بإمكاننا إنشاء كائن جديد مع إعطائه إسماً وعمراً، ومن ثمّ التحقق من إسمه وعمره.

someone = Person.new("Ruby Newbie", 13)
puts someone.name # Prints: Ruby Newbie
puts someone.age  # Prints: 13

إذاً بإمكاننا قراءة قيمة متغير كائن، لكن ماذا إذا ما أردنا تغييرها؟ في هذه الحالة، علينا استخدام طرق تعيين (setter methods). من الممكن فعل ذلك كما في الشيفرة أدناه، لكن ﻻ تتعب نفسك بكتابة هذه الشيفرة، ﻷن هنالك طريقة أسهل. فقط تابع القراءة!

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

  def name
    @name
  end

  def age
    @age
  end

  def name=(value)
    @name = value
  end

  def age=(value)
    @age = age
  end
end

بما أن العديد من متغيرات الكائن بحاجة إلى طرق استرجاع وتعيين، توفر روبي بعض الطرق المريحة:

  • attr_accessor: تتيح قراءة وتغيير قيمة المتغير.
  • attr_reader: تتيح قراءة قيمة المتغير.
  • attr_writer: تتيح تغيير قيمة المتغير.

اﻵن أصبح بإمكاننا كتابة الشيفرة السابقة بشكل أفضل:

class Person
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

سطر واحد أغنانا عن كتابة 4 طرق.أمر جميل، أليس كذلك؟

رابعا: دمج الطرق والمتغيرات مع الأصناف

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

class Person
  @@population = 0

  def initialize
    @@population += 1
  end

  def Person.population
    @@population
  end
end

puts Person.population # Prints: 0
someone = Person.new
puts Person.population # Prints: 1
someone_else = Person.new
puts Person.population # Prints: 2

خامسا: الوراثة (Inheritance)

تدعم لغة روبي الوراثة الموحدة (single inheritanceأي أن الصنف الفرعي (subclass) يستطيع وراثة المتغيرات والطرق من صنف واحد فقط (superclass).

لنقم بإنشاء صنف جديد وجعله صنفاً فرعياً من الصنف Person الذي يحتوي المتغيرين name وage

class Programmer < Person
end

اﻵن، يمكننا إنشاء كائن من الصنف Programmer وسيكون لديه نفس المتغيرات الموجودة في الصنف Person

programmer = Programmer.new("Ruby Newbie", 13)
puts programmer.name # prints: Ruby Newbie
puts programmer.age  # Prints: 13

لكن ما الفائدة من الصنف Programmer هنا إذا كان مطابقاً للصنف Person؟ لنضف متغيراً خاصاً للمبرمجين.

class Programmer < Person
  attr_accessor :language
 
  def initialize(name, age, language)
    super(name, age)
    @language = language
  end
end

programmer = Programmer.new("Ruby Newbie", 13, "Ruby")
puts programmer.name     # Prints: Ruby Newbie
puts programmer.age      # Prints: 13
puts programmer.language # Prints: Ruby

في الشيفرة أعلاه، استخدمنا طريقة super مع المعاملَين name وage لنطلب من الصنف اﻷب Person إسناد القيم المعطاة للمتغيرين @name و@age

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

مقالات من مؤلف التدوينة

عدد التعليقات 3

  1. Posted الخميس 24 سبتمبر 2009 at 4:31 م | Permalink

    مقال جميل أخي و لي تعليق واحد في هذا المقام لجعل الأمور أوضح لو تم وضع بعض الرسوم التوضيحية لموضع الكائن و الفئة حتى تكون الأمور أسهل على المتعلمين

  2. Posted السبت 10 أكتوبر 2009 at 8:43 م | Permalink

    @عمر
    شكراً على الاقتراح.

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

    على كل حال، لعلنا نستفيد من اقتراحك في كتابة درس خاص عن مفاهيم البرمجة الغرضية التوجه في المستقبل :)

  3. مرفع
    Posted الأربعاء 21 أكتوبر 2009 at 2:12 م | Permalink

    جزاك الله خير …

    على السلسلة الرائعة …

    استفدت منها كثيراً … بالتوفيق

تعقيب واحد

  1. بواسطة روابـــ(8)ــط « Mutati0N UNITED STATES WordPress MU في الأربعاء 23 سبتمبر 2009 11:08 م

    [...] روابـــ(8)ــط September 23, 2009 at 8:08 pm | In روابــــــط | Leave a Comment سلام من الله عليكم ورحمته وبركاته ومغفرته ورضوانه * تسجيلات صوتيه لمبرمجين محترفين * مايكروسوفت تطلق محرك البحث الجديد Bing * بدائل الـ Rails * قاموس لغات البرمجة * Reading Ruby’s Standard Library for Fun and Profit * موقع يعرفك بامتدادات الملفات وبرامجها [File Extension] * Session Manager , أضافة جميلة للفايرفوكس تمكنك من حفظ جلسات معينة , واسترجاعها  وقت الحاجة * RubyTips : يحتوي علي بعض الحلول الجميلة لبعض المشاكل * how to choose the right screenshot program * Wepopedia : الموسوعة الأولى للحاسب * جدول بلغات البرمجة * uniques ruby language features * The Groovy and Grails Podcast * Rip: A New Package Management System for Ruby * البرمجة غرضية التوجه في روبي [...]

أضف تعليقاً

بريدك الالكتروني لن يعرض للآخرين. * حقول مطلوبة

*
*