কম্পিউটার টিউটোরিয়াল

রুবি ক্লাস ভেরিয়েবল এবং তাদের স্কোপ বুঝুন: একটি স্পষ্ট গাইড

রুবির ক্লাস ভেরিয়েবল বিভ্রান্তিকর। এক্সপার্ট রুবি ইউজারও কখনও কখনও সেগুলো বুঝতে কষ্ট পান। সবচেয়ে স্পষ্ট উদাহরণ ইনহেরিটেন্সের সাথে জড়িত:

class Fruit
  @@kind = nil
  def self.kind
    @@kind
  end
end

class Apple < Fruit
  @@kind = "apple"
end

Apple.kind # => "apple"
Fruit.kind # => "apple"

kind ভেরিয়েবল চাইল্ড ক্লাসে পরিবর্তন করলে পেরেন্ট ক্লাসেও পরিবর্তন হয়ে যায়। এটি খুব গোলমাল। কিন্তু এটিই ভাষার নিয়ম। এটি অনেক আগে একটি ডিজাইন সিদ্ধান্ত ছিল Smalltalk অনুকরণের জন্য।

এটি আরও খারাপ

ক্লাস ভেরিয়েবলের অন্যান্য উদাহরণ আছে যা আর্কিটেকচারাল চয়েস না হয়ে ইমপ্লিমেন্টেশন কুইর্কস মনে হয়। আজ আমি 그중 একটি নিয়ে আলোচনা করব যা আমার মতে আকর্ষণীয়।

আমরা দুটি কোডের টুকরো তুলনা করব। সেগুলো দেখতে একই ফলাফল দেবে বলে লাগে কিন্তু দেয় না।

প্রথম উদাহরণে আমরা একটি ক্লাস ভেরিয়েবল সেট করি এবং সেটি রিটার্ন করার একটি মেথড তৈরি করি। এখানে কোনো ফ্যান্সি কিছু নেই। এবং সব কিছু প্রত্যাশা অনুযায়ী কাজ করে।

class Foo
  @@val = 1234
  # ক্লাস মেথড ডিক্লেয়ার করার শর্টহ্যান্ড।
  class << self
    def val
      @@val
    end
  end
end

Foo.val # => 1234

হয়তো আপনি জানতেন না, class << self ক্লাস ডেফিনিশনের ভেতরে থাকতে হয় না। নিচের উদাহরণে আমরা এটিকে বাইরে নিয়ে এলাম। একটি ক্লাস মেথড যোগ হয়েছে কিন্তু সেটি ক্লাস ভেরিয়েবল অ্যাক্সেস করতে পারছে না।

class Bar
  @@val = 1234
end

class << Bar
  def val
    @@val
  end
end

Bar.val
# warning: class variable access from toplevel
# NameError: uninitialized class variable @@val in Object

যখন আমরা আমাদের ফাংশন থেকে ক্লাস ভেরিয়েবল অ্যাক্সেস করতে চাই, একটি ওয়ার্নিং এবং এক্সসেপশন পাই। কী হচ্ছে?

লেক্সিক্যাল স্কোপ প্রবেশ

আমি আরো বেশি নিশ্চিত হচ্ছি যে লেক্সিক্যাল স্কোপ রুবির ৯৯% विचিত্র এবং বিভ্রান্তিকর দিকের উৎস।

যদি আপনি টার্মটি জানতে না থাকেন, লেক্সিক্যাল স্কোপিং বলতে বোঝায় জিনিসগুলো কোডে কোথায় দেখায় তার উপর ভিত্তি করে গ্রুপ করা, নাকি তারা একটি অ্যাবস্ট্রাক্ট অবজেক্ট মডেলে কোথায় বেলং করে তার উপর ভিত্তি করে। একটি উদাহরণ দেখানো সহজ:

class B
  # x এবং y একই লেক্সিক্যাল স্কোপ শেয়ার করে
  x = 1
  y = 1
end

class B
  # z-এর লেক্সিক্যাল স্কোপ x এবং y থেকে ভিন্ন, যদিও একই ক্লাসে।
  z = 3
end

ক্লাস লেক্সিক্যালি নির্ধারিত

তাহলে আমাদের ক্লাস ভেরিয়েবল উদাহরণে লেক্সিক্যাল স্কোপ কীভাবে কাজ করছে?

এক্সসেপশন রescue করার সময় রুবিকে জানতে হয় কোন ক্লাস থেকে ভেরিয়েবলটি নেওয়া হবে। এটি লেক্সিক্যাল স্কোপিং ব্যবহার করে ক্লাসটি খুঁজে বের করে।

যদি আমরা কাজ করছে উদাহরণটি নিকট থেকে দেখি, তবে যে কোডটি ক্লাস ভেরিয়েবল অ্যাক্সেস করছে সেটি ফিজিক্যালি ক্লাস ডেফিনিশনের ভেতরে।

class Foo
  class << self
    def val
      # আমি Foo-র লেক্সিক্যালি স্কোপড!
      @@val
    end
  end
end

নন-ওয়ার্কিং উদাহরণে, ক্লাস ভেরিয়েবল অ্যাক্সেস করার কোডটি ক্লাসের সাথে লেক্সিক্যালি স্কোপড নয়।

class << Bar
  def val
    # Foo কোথাও নেই।
    @@val
  end
end

কিন্তু যদি এটি ক্লাসের সাথে লেক্সিক্যালি স্কোপড নয়, তবে কিসের সাথে স্কোপড? রুবি যে ওয়ার্নিং প্রিন্ট করে সেটি আমাদের একটি সংকেত দেয়: warning: class variable access from toplevel।

নন-ওয়ার্কিং উদাহরণে আসলে ক্লাস ভেরিয়েবলটি টপ-লেভেল অবজেক্টের সাথে লেক্সিক্যালি স্কোপড। এটি কিছু খুব অসামান্য আচরণ করতে পারে।

উদাহরণস্বরূপ, যদি আমরা টপ-লেভেল অবজেক্টের লেক্সিক্যাল স্কোপ থেকে ক্লাস ভেরিয়েবল সেট করার চেষ্টা করি, তবে ক্লাস ভেরিয়েবলটি Object-এ সেট হয়ে যায়।

class Bar
end

class << Bar
  def val=(n)
    # এই কোড টপ-লেভেল অবজেক্টের সাথে লেক্সিক্যালি স্কোপড।
    # অর্থাৎ, `@@val` `Object`-এ সেট হবে, `Bar`-এ নয়
    @@val = n
  end
end

Bar.val = 100
# Whaa?
Object.class_variables # => [:@@foo]

আরও উদাহরণ

ক্লাসের লেক্সিক্যাল স্কোপের বাইরে থেকে ক্লাস ভেরিয়েবল রেফারেন্স করার অনেক উপায় আছে। সবই আপনাকে সমস্যা দেবে।

আপনার আনন্দের জন্য কিছু উদাহরণ:

class Foo
  @@foo = :foo
end

# এটি কাজ করবে না
Foo.class_eval { puts @@foo }

# এটিও কাজ করবে না
Foo.send :define_method, :x do
  puts @@foo
end

# ..এবং আপনি মডিউল সম্পর্কে ভাবছিলেন কি?
module Printable
  def foo
    puts @@foo
  end
end

class Foo
  @@foo = :foo
  include Printable
end

Foo.new.foo

এখন আপনি জানেন কেন সবাই বলে রুবিতে ক্লাস ভেরিয়েবল কখনো ব্যবহার করবেন না। :)

  1. রুবিতে কাস্টোম এক্সেপশন মাস্টার করুন: ধাপে ধাপে গাইড

  2. রুবির মেথড লুকআপ পাথ আয়ত্ত করা: মেথড কীভাবে রেজলভ হয়

  3. রুবিতে এনভায়রনমেন্ট ভেরিয়েবল মাস্টার করুন: একটি প্র্যাকটিক্যাল গাইড

  4. রুবি অবজেক্ট ডেকোরেটর প্যাটার্ন দিয়ে সমৃদ্ধ করুন: একটি প্র্যাক্টিক্যাল গাইড