4 דברים שלמדתי על רובי מרובוקופ

05/09/2016

יש שני מדדים ליכולת שלנו בתור מתכנתים: הראשון הוא הידע, כלומר איזה שפות תכנות אנו מכירים וכמה שנות נסיון יש בכל שפה. זה מה שמופיע בקורות חיים ומה שמבקשים במודעות דרושים.

השני חשוב בהרבה וזו הטכניקה. טכניקה היא היכולת שלנו לכתוב קוד שיחזיק מעמד לאורך זמן, המדד שבודק כמה אנו שמים לב לפרטים וכמה קל יהיה לנו ללמוד טכנולוגיות חדשות. קשה לאמוד טכניקה של מתכנתים אחרים (או של עצמנו) ובאופן כללי הרבה יותר קשה לשפר טכניקה מאשר ללמוד שפת תכנות חדשה.

דרך אחת שעובדת לשפר טכניקה היא לתת למישהו מנוסה יותר לקרוא את הקוד שלנו. דרך משוב חוזר על הקוד אנו לומדים לשים לב לדברים שלא ראינו קודם ולשפר אותם. בהיעדר חבר מנוסה או ראש צוות, אפשר להיעזר גם בתוכנות אוטומטיות. רובוקופ היא תוכנה כזו לקוד רובי ואלה חמישה דברים שהיא לימדה אותי על כתיבת קוד.

1. בקוד האורך כן קובע

רובוקופ הכניסה לחיי מדד שנקרא ABC. הוא מודד את כמות ההשמות, הסתעפויות ותנאים שיש בכל פונקציה. אם נסמן השמות ב A, הסתעפויות ב B ותנאים ב C נקבל:

|ABC| = sqrt((A*A)+(B*B)+(C*C))

מסתבר שהמספר מהווה מדד לא רע לכמה פונקציה מסובכת. ברירת המחדל של רובוקופ הגדירה אותו על 15, אבל לא הצלחתי לעמוד בזה אז העליתי ל 20. גם ערך זה זיהה בקלות פונקציות ארוכות או מסורבלות מדי שכתבתי ואילץ אותי לשכתב את הקוד. התוצאה היא כמובן קוד קריא יותר וקל יותר לבדיקה ותחזוקה.

2. העברת אוביקטים לפונקציה הרבה יותר פשוטה ברובי

בהנתן הרקע שלי ב JavaScript היה לי קל מאוד להבין מה עושה הקוד הבא ברובי:

def print_score(game_info)
  puts "Bravo #{game_info[:name]}!"
  puts "You scored #{game_info[:score]} points"
end

print_score({ :name => 'ynon', :score => 30 })

רובוקופ לא מרשה קוד כזה בטענה שהסוגריים המסולסלים מיותרים. מסתבר שברובי ההמרה לאוביקט מתבצעת באופן אוטומטי ואפשר להפעיל את הפונקציה גם כך:

print_score(:name => 'ynon', :score => 30)

3. אין צורך לכתוב self לפני שמות פונקציות

בניגוד לפייתון רובי יודעת לבד להוסיף את self כשמפעילים פוקנציות מאותה מחלקה ולכן הקוד הבא עובד ממש בסדר:

class Game
  attr_reader :game_info

  def initialize
    @game_info = { :name => 'ynon', :score => 0 }
  end

  def play
    @game_info[:score] += 1
    print_score
  end

  def print_score
    puts "Bravo #{game_info[:name]}!"
    puts "You scored #{game_info[:score]} points"
  end
end

4. אפשר להעביר מתודה ישירות ל map

היחס לפונקציות ברובי הוא יותר מורכב מאשר ב JavaScript או Python. בשפות ההן כתיבת שם הפונקציה ללא סוגריים של הפעלה מתיחסת לפוקנציה עצמה, אותה אפשר לשמור במשתנה או להעביר לפונקציה אחרת. ברובי כתיבת שם הפונקציה ללא סוגריים פשוט מפעילה אותה ללא פרמטרים.

יש כל מיני דרכים לשמור פונקציות במשתנים ולהתיחס אליהן ממקומות אחרים, כל אחת עם הבעיות שלה, אבל מה שבדרך כלל עושים זה להעביר בלוק לפונקציה, למשל בקוד הבא שמניח שלכל אחד מהאוביקטים יש פונקציית score שמחזירה את מספר הנקודות:

score_array = [g, h, q].map { |game| game.score }
sum = score_array.reduce { |a, e| a + e }
puts "Total score for all objects = #{sum}"

ומסתבר שברובי אפשר להשתמש גם בקיצור הדרך הבא לביצוע אותו החישוב:

puts [g, h, q].map(&:score).reduce(&:+)

זה עובד כי המרת symbol לבלוק ברובי פשוט מחזירה בלוק שמקבל פרמטרים ומפעיל את הפונקציה שקיבל על הפרמטרים.

העבודה עם תוכנות אוטומטיות שיקראו את הקוד שלכם יכולה להיות מועילה מאוד. חשוב לנסות להבין כל אחת מההערות, לא רק לתקן את הקוד אלא גם לחפש ברשת מה החשיבות של התיקון ומה משמעות ההבדל בין מה שכתבתם למה שמוצע. היכרות עם ה Best Practices ושיטות העבודה המקובלות בכל שפה תשפר את הטכניקה שלכם בשפת התכנות שבחרתם.