কম্পিউটার

ক্লাস-লেভেল ইনস্ট্যান্স ভেরিয়েবলের ম্যাজিক

আগের রুবি ম্যাজিকে, আমরা বুঝতে পেরেছিলাম কিভাবে ক্লাসে মডিউলগুলিকে .new ওভাররাইট করে নির্ভরযোগ্যভাবে ইনজেক্ট করা যায় পদ্ধতি, আমাদের অতিরিক্ত আচরণের সাথে পদ্ধতিগুলি মোড়ানোর অনুমতি দেয়৷

এই সময়, আমরা সেই আচরণটিকে নিজস্ব একটি মডিউলে বের করে এটিকে আরও এক ধাপ এগিয়ে নিয়ে যাচ্ছি যাতে আমরা এটিকে পুনরায় ব্যবহার করতে পারি। আমরা একটি Wrappable তৈরি করব মডিউল যা আমাদের জন্য ক্লাস এক্সটেনশন পরিচালনা করে এবং আমরা ক্লাস-লেভেল ইনস্ট্যান্স ভেরিয়েবল সম্পর্কে সমস্ত কিছু শিখব। আসুন সরাসরি ভিতরে ডুব দিই!

প্রবর্তন করা হচ্ছে Wrappable মডিউল

বস্তুগুলিকে মডিউল দিয়ে মোড়ানোর জন্য যখন সেগুলি শুরু করা হয়, তখন আমাদের ক্লাসকে জানাতে হবে যে কোন মোড়কের মডেলগুলি ব্যবহার করতে হবে। চলুন শুরু করা যাক একটি সহজ Wrappable তৈরি করে মডিউল যা একটি wrap প্রদান করে পদ্ধতি যা প্রদত্ত মডিউলটিকে একটি ক্লাস অ্যাট্রিবিউট হিসাবে সংজ্ঞায়িত অ্যারেতে পুশ করে। উপরন্তু, আমরা new পুনরায় সংজ্ঞায়িত করি আগের পোস্টে আলোচনা করা পদ্ধতি।

module Wrappable
  @@wrappers = []
 
  def wrap(mod)
    @@wrappers << mod
  end
 
  def new(*arguments, &block)
    instance = allocate
    @@wrappers.each { |mod| instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &block)
    instance
  end
end

একটি ক্লাসে নতুন আচরণ যোগ করতে, আমরা extend ব্যবহার করি . extend পদ্ধতি ক্লাসে প্রদত্ত মডিউল যোগ করে। পদ্ধতিগুলি তখন শ্রেণি পদ্ধতিতে পরিণত হয়। এই ক্লাসের দৃষ্টান্তগুলি মোড়ানোর জন্য একটি মডিউল যোগ করতে, আমরা এখন wrap কল করতে পারি পদ্ধতি।

module Logging
  def make_noise
    puts "Started making noise"
    super
    puts "Finished making noise"
  end
end
 
class Bird
  extend Wrappable
 
  wrap Logging
 
  def make_noise
    puts "Chirp, chirp!"
  end
end

চলুন Bird-এর একটি নতুন উদাহরণ তৈরি করে এটি চেষ্টা করে দেখি এবং make_noise কল করছে পদ্ধতি।

bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

দারুণ! এটা প্রত্যাশিত হিসাবে কাজ করে. যাইহোক, যখন আমরা Wrappable দিয়ে দ্বিতীয় শ্রেণী প্রসারিত করি তখন জিনিসগুলি কিছুটা অদ্ভুত আচরণ করতে শুরু করে মডিউল।

module Powered
  def make_noise
    puts "Powering up"
    super
    puts "Shutting down"
  end
end
 
class Machine
  extend Wrappable
 
  wrap Powered
 
  def make_noise
    puts "Buzzzzzz"
  end
end
 
machine = Machine.new
machine.make_noise
# Powering up
# Started making noise
# Buzzzzzz
# Finished making noise
# Shutting down
 
bird = Bird.new
bird.make_noise
# Powering up
# Started making noise
# Chirp, chirp!
# Finished making noise
# Shutting down

যদিও Machine Logging দিয়ে মোড়ানো হয়নি মডিউল, এটি এখনও লগিং তথ্য আউটপুট করে। কি খারাপ - এমনকি পাখিটি এখন উপরে এবং নিচের দিকে শক্তি দিচ্ছে। এটা ঠিক হতে পারে না, তাই না?

এই সমস্যার মূল যেভাবে আমরা মডিউলগুলি সংরক্ষণ করছি তার মধ্যে রয়েছে। ক্লাস ভেরিয়েবল @@wrappables Wrappable-এ সংজ্ঞায়িত করা হয়েছে মডিউল এবং ব্যবহার করা হয় যখনই আমরা একটি নতুন মডিউল যোগ করি, wrap ক্লাস নির্বিশেষে ব্যবহার করা হয়।

Wrappable-এ সংজ্ঞায়িত ক্লাস ভেরিয়েবলের দিকে তাকালে এটি আরও স্পষ্ট হয় মডিউল এবং Bird এবং Machine ক্লাস যখন Wrappable একটি ক্লাস পদ্ধতি সংজ্ঞায়িত করা আছে, দুটি শ্রেণী নেই।

Wrappable.class_variables # => [:@@wrappers]
Bird.class_variables # => []
Machine.class_variables # => []

এটি ঠিক করার জন্য, আমাদের বাস্তবায়ন পরিবর্তন করতে হবে যাতে এটি ইনস্ট্যান্স ভেরিয়েবল ব্যবহার করে। যাইহোক, এগুলি Bird এর দৃষ্টান্তে ভেরিয়েবল নয় অথবা Machine , কিন্তু ইনস্ট্যান্স ভেরিয়েবল ক্লাসে নিজেরাই।

রুবিতে, ক্লাসগুলি কেবল বস্তু

এটি অবশ্যই প্রথমে কিছুটা মন খারাপ করে, তবে এখনও বোঝার জন্য একটি খুব গুরুত্বপূর্ণ ধারণা। ক্লাস হল Class এর উদাহরণ এবং class Bird; end Bird = Class.new লেখার সমতুল্য . জিনিসগুলিকে আরও বিভ্রান্তিকর করতে Class Module থেকে উত্তরাধিকারসূত্রে প্রাপ্ত যা Object থেকে উত্তরাধিকারসূত্রে পাওয়া যায় . ফলস্বরূপ, ক্লাস এবং মডিউলগুলির অন্য যে কোনও অবজেক্টের মতো একই পদ্ধতি রয়েছে। বেশিরভাগ পদ্ধতিই আমরা ক্লাসে ব্যবহার করি (যেমন attr_accessor ম্যাক্রো) আসলে Module এর উদাহরণ পদ্ধতি .

ক্লাসে ইনস্ট্যান্স ভেরিয়েবল ব্যবহার করা

চলুন Wrappable পরিবর্তন করি উদাহরণ ভেরিয়েবল ব্যবহার করার জন্য বাস্তবায়ন। জিনিসগুলিকে কিছুটা পরিষ্কার রাখতে, আমরা একটি wrappers প্রবর্তন করি পদ্ধতি যা হয় অ্যারে সেট আপ করে বা বিদ্যমান একটি রিটার্ন করে যখন ইনস্ট্যান্স ভেরিয়েবল ইতিমধ্যেই বিদ্যমান থাকে। এছাড়াও আমরা wrap পরিবর্তন করি এবং new পদ্ধতি যাতে তারা সেই নতুন পদ্ধতি ব্যবহার করে।

module Wrappable
  def wrap(mod)
    wrappers << mod
  end
 
  def wrappers
    @wrappers ||= []
  end
 
  def new(*arguments, &block)
    instance = allocate
    wrappers.each { |mod| instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &block)
    instance
  end
end

যখন আমরা মডিউলে এবং দুটি ক্লাসে ইনস্ট্যান্স ভেরিয়েবল পরীক্ষা করি, তখন আমরা দেখতে পাব যে উভয়ই Bird এবং Machine এখন তাদের নিজস্ব র‍্যাপিং মডিউলের সংগ্রহ বজায় রাখুন।

Wrappable.instance_variables #=> []
Bird.instance_variables #=> [:@wrappers]
Machine.instance_variables #=> [:@wrappers]

আশ্চর্যের বিষয় নয়, এটি আমরা আগে যে সমস্যাটি পর্যবেক্ষণ করেছি তাও সমাধান করে - এখন, উভয় শ্রেণীই তাদের নিজস্ব মডিউল দিয়ে মোড়ানো।

bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
 
machine = Machine.new
machine.make_noise
# Powering up
# Buzzzzzz
# Shutting down

সমর্থক উত্তরাধিকার

উত্তরাধিকার প্রবর্তিত না হওয়া পর্যন্ত এই সব মহান কাজ করে. আমরা আশা করব যে ক্লাসগুলি সুপারক্লাস থেকে র্যাপিং মডিউলগুলি উত্তরাধিকার সূত্রে পাবে। চলুন পরীক্ষা করে দেখা যাক তা হয় কিনা।

module Flying
  def make_noise
    super
    puts "Is flying away"
  end
end
 
class Pigeon < Bird
  wrap Flying
 
  def make_noise
    puts "Coo!"
  end
end
 
pigeon = Pigeon.new
pigeon.make_noise
# Coo!
# Is flying away

আপনি দেখতে পাচ্ছেন, এটি প্রত্যাশিতভাবে কাজ করে না, কারণ Pigeon র‌্যাপিং মডিউলের নিজস্ব সংগ্রহও বজায় রাখছে। যদিও এটা বোঝা যায় যে Pigeon-এর জন্য সংজ্ঞায়িত মোড়ক মডিউল Bird-এ সংজ্ঞায়িত করা হয় না , আমরা যা চাই তা ঠিক নয়। আসুন সমগ্র উত্তরাধিকার শৃঙ্খল থেকে সমস্ত মোড়ক পাওয়ার একটি উপায় বের করি।

আমাদের জন্য ভাগ্যবান, রুবি Module#ancestors প্রদান করে একটি ক্লাস (বা মডিউল) উত্তরাধিকার সূত্রে প্রাপ্ত সমস্ত ক্লাস এবং মডিউল তালিকাভুক্ত করার পদ্ধতি।

Pigeon.ancestors # => [Pigeon, Bird, Object, Kernel, BasicObject]

একটি grep যোগ করে কল করুন, আমরা সেইগুলিকে বেছে নিতে পারি যেগুলি আসলে Wrappable দিয়ে প্রসারিত . যেহেতু আমরা প্রথমে চেইনের উপরে থেকে র‍্যাপার দিয়ে দৃষ্টান্তগুলিকে মোড়ানো করতে চাই, আমরা .reverse কল করি অর্ডার ফ্লিপ করতে।

Pigeon.ancestors.grep(Wrappable).reverse # => [Bird, Pigeon]

রুবির #=== পদ্ধতি

রুবির কিছু জাদু #=== এ নেমে আসে (বা কেস সমতা ) পদ্ধতি। ডিফল্টরূপে, এটি ঠিক #== এর মত আচরণ করে (বা সমতা ) পদ্ধতি। যাইহোক, বেশ কিছু ক্লাস #=== ওভাররাইড করে case-এ ভিন্ন আচরণ প্রদানের পদ্ধতি বিবৃতি এইভাবে আপনি রেগুলার এক্সপ্রেশন ব্যবহার করতে পারেন (#=== #match? এর সমতুল্য ), অথবা ক্লাস (#=== #kind_of? এর সমতুল্য ) সেই বিবৃতিতে। Enumerable#grep এর মত পদ্ধতি , Enumerable#all? , অথবা Enumerable#any? এছাড়াও কেস সমতা পদ্ধতির উপর নির্ভর করে।

এখন আমরা flat_map(&:wrappers) কল করতে পারি একটি একক অ্যারে হিসাবে উত্তরাধিকার শৃঙ্খলে সংজ্ঞায়িত সমস্ত মোড়কের একটি তালিকা পেতে৷

Pigeon.ancestors.grep(Wrappable).reverse.flat_map(&:wrappers) # => [Logging]

যা বাকি আছে তা হল একটি inherited_wrappers-এ প্যাক করা মডিউল এবং নতুন পদ্ধতিটিকে সামান্য পরিবর্তন করা যাতে এটি wrappers এর পরিবর্তে এটি ব্যবহার করে পদ্ধতি।

module Wrappable
  def inherited_wrappers
    ancestors
      .grep(Wrappable)
      .reverse
      .flat_map(&:wrappers)
  end
 
  def new(*arguments, &block)
    instance = allocate
    inherited_wrappers.each { |mod|instance.singleton_class.include(mod) }
    instance.send(:initialize, *arguments, &block)
    instance
  end
end

একটি চূড়ান্ত পরীক্ষা রান নিশ্চিত করে যে সবকিছু এখন প্রত্যাশিত হিসাবে কাজ করছে। র্যাপিং মডিউলগুলি শুধুমাত্র ক্লাসে প্রয়োগ করা হয় (এবং এর সাবক্লাসগুলিতে) তারা প্রয়োগ করা হয়৷

bird = Bird.new
bird.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
 
machine = Machine.new
machine.make_noise
# Powering up
# Buzzzzz
# Shutting down
 
pigeon = Pigeon.new
pigeon.make_noise
# Started making noise
# Coo!
# Finished making noise
# Is flying away

এটা একটা মোড়ানো!

স্বীকার্য, এই কোলাহলপূর্ণ পাখিগুলি একটি তাত্ত্বিক উদাহরণ (টুইট, টুইট)। কিন্তু উত্তরাধিকারসূত্রে পাওয়া ক্লাস ইনস্ট্যান্স ভেরিয়েবলগুলি ক্লাসগুলি কীভাবে কাজ করে তা বোঝার জন্য কেবল দুর্দান্ত নয়। তারা একটি দুর্দান্ত উদাহরণ যে ক্লাসগুলি কেবল রুবিতে অবজেক্ট।

এবং আমরা স্বীকার করব যে উত্তরাধিকারসূত্রে পাওয়া ক্লাস ইনস্ট্যান্স ভেরিয়েবলগুলি বাস্তব জীবনেও বেশ কার্যকর হতে পারে। উদাহরণস্বরূপ, একটি মডেলের বৈশিষ্ট্য এবং সম্পর্কগুলিকে পরবর্তীতে আত্মদর্শন করার ক্ষমতার সাথে সংজ্ঞায়িত করার বিষয়ে চিন্তা করুন। আমাদের জন্য যাদু হল এটির সাথে খেলা করা এবং জিনিসগুলি কীভাবে কাজ করে তা আরও ভালভাবে বোঝা। এবং সমাধানের পরবর্তী স্তরের জন্য আপনার মন খুলুন। 🧙🏼‍♀️

বরাবরের মতো, আমরা এই বা অনুরূপ নিদর্শনগুলি ব্যবহার করে আপনি কী তৈরি করছেন তা শোনার অপেক্ষায় আছি। টুইটারে @AppSignal-এ শুধু কিচিরমিচির করুন।


  1. পাইথনে একটি উদাহরণের ক্লাসের নাম কীভাবে পাবেন?

  2. পাইথনে ক্লাস ভেরিয়েবল সংজ্ঞায়িত করার সঠিক উপায় কি?

  3. পরিবেশের ভেরিয়েবলের জন্য রুবিস্ট গাইড

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