কম্পিউটার

লেক্সিকাল স্কোপিং এবং রুবি ক্লাস ভেরিয়েবল

রুবির ক্লাস ভেরিয়েবলগুলি বিভ্রান্তিকর। এমনকি বিশেষজ্ঞ রুবি ব্যবহারকারীরা তাদের স্বজ্ঞাত করা কঠিন খুঁজে পেতে পারেন। সবচেয়ে সুস্পষ্ট উদাহরণটি জড়তার সাথে সম্পর্কিত:

class Fruit
  @@kind = nil

  def self.kind
    @@kind
  end
end

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

Apple.kind
# => "apple" 

Fruit.kind
# => "apple" 

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

এটা আরও খারাপ হয়

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

আমরা এখন কোডের দুটি টুকরা তুলনা করতে যাচ্ছি। তারা দেখে মনে হচ্ছে তাদের একটি অভিন্ন ফলাফল পাওয়া উচিত কিন্তু তারা তা করে না।

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

class Foo
  @@val = 1234

  # This is shorthand for declaring a class method.
  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

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

লেক্সিকাল স্কোপ লিখুন

আমি আরও বেশি করে নিশ্চিত হয়ে উঠছি যে রুবির অদ্ভুত এবং বিভ্রান্তিকর দিকগুলির 99% উৎস হল আভিধানিক সুযোগ।

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

class B
  # x and y share the same lexical scope
  x = 1
  y = 1
end

class B
  # z has a different lexical scope from x and y, even though it's in the same class. 
  z = 3
end

ক্লাসটি আভিধানিকভাবে নির্ধারিত হয়

তাহলে আমাদের ক্লাস ভেরিয়েবল উদাহরণে আভিধানিক সুযোগ কীভাবে কাজ করে?

ভাল, একটি ক্লাস ভেরিয়েবল পুনরুদ্ধার করার জন্য রুবিকে জানতে হবে কোন ক্লাস থেকে এটি পেতে হবে। এটি ক্লাস খুঁজে পেতে আভিধানিক স্কোপিং ব্যবহার করে।

আমরা যদি কাজের উদাহরণটি আরও ঘনিষ্ঠভাবে দেখি তবে আমরা দেখতে পাই যে ক্লাস ভেরিয়েবল অ্যাক্সেস করে এমন কোডটি শারীরিকভাবে ক্লাস সংজ্ঞায় রয়েছে৷

class Foo  
  class << self
    def val
      # I'm lexically scoped to Foo!
      @@val
    end
  end
end

অকার্যকর উদাহরণে, যে কোডটি ক্লাস ভেরিয়েবল অ্যাক্সেস করে তা আভিধানিকভাবে ক্লাসে স্কোপ করা হয় না।

class << Bar
  def val 
    # Foo is nowhere in sight. 
    @@val
  end
end

কিন্তু যদি এটি শ্রেণীতে আভিধানিকভাবে স্কোপ করা না হয়, তাহলে এটি কিসের জন্য স্কোপড? রুবি যে সতর্কতাটি প্রিন্ট করে তা আমাদের একটি সূত্র দেয়:warning: class variable access from toplevel .

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

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

class Bar
end

class << Bar
  def val=(n)
    # This code is lexically scoped to the top level object. 
    # That means, that `@@val` will be set on `Object`, not `Bar`
    @@val = i
  end
end

Bar.val = 100

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

আরো উদাহরণ

ক্লাসের আভিধানিক সুযোগের বাইরে ক্লাস ভেরিয়েবল উল্লেখ করার অনেক উপায় রয়েছে। এরা সবাই তোমাকে কষ্ট দেবে।

আপনার উপভোগের জন্য এখানে কয়েকটি উদাহরণ রয়েছে:

class Foo
  @@foo = :foo
end

# This won't work
Foo.class_eval { puts @@foo }

# neither will this
Foo.send :define_method, :x do 
  puts @@foo
end

# ..and you weren't thinking about using modules, were you? 
module Printable
  def foo
    puts @@foo
  end
end

class Foo
  @@foo = :foo
  include Printable
end

Foo.new.foo

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


  1. রুবিতে কাস্টম ব্যতিক্রম

  2. রুবি মেথড লুকআপ বোঝা

  3. রুবিতে পরিবেশের ভেরিয়েবলগুলি কীভাবে ব্যবহার করবেন

  4. রুবিতে ডেকোরেটর ডিজাইন প্যাটার্ন