আপনি কী মনে করেন যখন আপনি একটি মেথড কল করেন? রুবি কীভাবে সিদ্ধান্ত নেয় যে কোন মেথড কল করতে হবে যখন একই নামের আরেকটি মেথড থাকে? কি আপনি কখনো চিন্তা করেছেন যে মেথডটি কোথায় থাকে বা কোথায় থেকে এসেছে?
রুবি একটি নির্ধারিত "পথ" বা "প্যাটার্ন" ব্যবহার করে সঠিক মেথড কল করার এবং "নো মেথড এরর" রিটার্ন করার সঠিক সময় নির্ধারণ করতে, এবং আমরা এই "পথ"-কে রুবি মেথড লুকআপ পাথ বলতে পারি। এই টিউটোরিয়ালে আমরা রুবির মেথড লুকআপে গভীরে যাব। শেষে, আপনি বুঝতে পারবেন কীভাবে রুবি একটি অবজেক্টের হায়ারার্কি kroz যেতে সিদ্ধান্ত নেয় যে আপনি কোন মেথডকে রেফার করছেন।
আমরা যা শিখব তা পুরোপুরি বোঝার জন্য, আপনার রুবির বেসিক বোঝাপড়া থাকতে হবে। আমরা মডিউল এবং ক্লাসের কথা বলব, তবে এটি সেগুলোর গভীরে যাওয়া নয়। আমরা শুধু उत গভীরে যাব যতটুকু এই টিউটোরিয়ালের লক্ষ্যে পৌঁছাতে হয়: আপনি একটি অবজেক্টকে যে মেসেজ (মেথড) পাস করছেন সেটি রুবি কীভাবে নির্ধারণ করে।
অভিভাব
যখন আপনি একটি মেথড কল করেন, যেমন first_person.valid?, রুবি কিছু বিষয় নির্ধারণ করতে হয়:
.valid?মেথডটি কোথায় ডিফাইন করা হয়েছে।- কি একাধিক জায়গায়
.valid?মেথড ডিফাইন করা আছে? যদি হয়, তবে এই কন্টেক্সটে সঠিকটি কোনটি।
রুবি এটি বুঝতে যে প্রসেস (বা পথ) ফলো করে সেটিই আমরা মেথড লুকআপ বলি। রুবিকে খুঁজে বের করতে হয় যে মেথডটি কোথায় তৈরি হয়েছে যাতে সেটি কল করতে পারে। সঠিক মেথড কল করার নিশ্চিত করতে নিচের জায়গাগুলোতে সার্চ করতে হয়:
- সিঙ্গলটন মেথড: রুবি একটি অবজেক্টকে নিজস্ব মেথড ডিফাইন করার সুবিধা দেয়; এই মেথডগুলো শুধু সেই অবজেক্টের জন্য উপলব্ধ থাকে এবং সেই অবজেক্টের ইনস্ট্যান্স দ্বারা অ্যাক্সেস করা যায় না।
- মিক্সড-ইন মডিউলের মেথড: মডিউলগুলো
prepend,include, বাextendব্যবহার করে ক্লাসে মিক্স করা যায়। যখন এটি হয়, ক্লাসটি মডিউলে ডিফাইন করা মেথডগুলোর অ্যাক্সেস পায়, এবং রুবি মডিউলগুলোর ভেতরে কল করা মেথডটি খুঁজে বের করতে যায়। এটি জানা গুরুত্বপূর্ণ যে অন্য মডিউলও প্রাথমিক মডিউলগুলোর সাথে মিক্সড হতে পারে, এবং সার্চ সেগুলোতে ও প্রগতি করে। - ইনস্ট্যান্স মেথড: এগুলো ক্লাসে ডিফাইন করা মেথড যা সেই ক্লাসের ইনস্ট্যান্সগুলোর দ্বারা অ্যাক্সেসযোগ্য।
- পেরেন্ট ক্লাস মেথড বা মডিউল: যদি ক্লাসটি অন্য ক্লাসের চাইল্ড হয়, রুবি পেরেন্ট ক্লাসে সার্চ করে। সার্চ পেরেন্ট ক্লাসের সিঙ্গলটন মেথড, মিক্সড মডিউল, এবং এর পেরেন্ট ক্লাসে যায়।
- Object, Kernel, এবং BasicObject: এগুলো রুবি التىশেষে সার্চ করে। কারণ রুবির প্রতিটি অবজেক্টের অ্যানসেস্টর হিসেবে এগুলো থাকে।
ক্লাস এবং মডিউল
মেথডগুলো সাধারণত অবজেক্টের ওপর কল করা হয়। এই অবজেক্টগুলো নির্দিষ্ট ক্লাসের 의해 তৈরি হয়, যা রুবির বিল্ট-ইন ক্লাস বা ডেভেলপার তৈরি করা ক্লাস হতে পারে।
class Human
attr_reader :name
def initialize(name)
@name = name
end
def hello
put "Hello! #{name}"
end
end
তब আমরা উপরের Human ক্লাসের ইনস্ট্যান্সের ওপর hello মেথড কল করতে পারি; উদাহরণস্বরূপ,
john = Human.new("John")
john.hello # আউটপুট -> Hello John
hello মেথডটি একটি ইনস্ট্যান্স মেথড; এই কারণে আমরা Human ক্লাসের ইনস্ট্যান্সের ওপর এটি কল করতে পারি। এমন 케이স হতে পারে যেখানে আমরা মেথডটি ইনস্ট্যান্সের ওপর কল করতে চাই না। সেসব কেসে, আমরা ক্লাসের ওপর মেথড কল করতে চাই। এটি অর্জনের জন্য, আমাদের একটি ক্লাস মেথড তৈরি করতে হবে। উপরের ক্লাসের জন্য একটি ক্লাস মেথড ডিফাইন করলে এরকম দেখাবে:
def self.me
puts "I am a class method"
end
তब আমরা Human.me করলে কল করতে পারি। যেনে আমাদের অ্যাপ্লিকেশনের জটিলতা বৃদ্ধি পায় (কল্পনা করুন আমরা একটি নতুন স্টার্টআপ বিল্ড করছি), এমন সময় আসতে পারে যখন আমাদের দুটি বা অধিক ক্লাসে একই কাজ করে একাধিক মেথড থাকে। যদি এটা হয়, তবে আমাদের ড্রাই রাখতে হবে এবং নিশ্চিত করতে হবে যে আমরা নিজেদের রিপিট করছি না। সমস্যাটি হচ্ছে আমরা এই ফাংশনালিটি ক্লাসগুলোর মাঝপথে কীভাবে শেয়ার করব।
আপনি যদি আগে মডিউল ব্যবহার না করে থাকেন, তবে আপনি এই "শেয়ার করা" মেথডগুলোর জন্য স্ট্রিক্টলি একটি নতুন ক্লাস তৈরি করতে প্রলুব্ধ হয়তে পারেন। তবে, এতে নেতিবাচক ফলাফল আসতে পারে, বিশেষ করে যখন আপনাকে মাল্টিপল ইনহেরিটেন্স ব্যবহার করতে হবে, কিছু রুবি সাপোর্ট করে না। এই কেস হ্যান্ডল করার জন্য মডিউল সেরা উপায়। মডিউল ক্লাসের মত, কিন্তু কিছু পার্থক্য আছে। প্রথমে, একটি মডিউল কেমন দেখে সেটি দেখুন:
module Movement
def walk
puts "I can walk!"
end
end
- ডেফিনিশন
moduleকিওয়ার্ড দিয়ে শুরু হয়,classদিয়ে না। - মডিউলের ইনস্ট্যান্স থাকতে পারে না, তাই আপনি
Movement.newব্যবহার করতে পারবেন না।
মেথড
মেথডগুলোকে একটি নির্দিষ্ট অবজেক্ট tarafından করা অ্যাকশন হিসেবে দেখা যেতে পারে। যদি আমার কাছে [2, 3, 4] এরকম একটি অ্যারে থাকে যা numberList ভেরিয়েবলে অ্যাসাইন করা আছে, তবে .push মেথডটি অ্যারেটি যা মান পাবে সেটি অ্যারেতে দেয়া করার একটি অ্যাকশন। এই কোড স্নিপেটটি উদাহরণ:
john.walk
আপনি খুব সাধারণভাবে বলতে পারেন, "আমি অবজেক্টের মেথড কল করছি", যেখানে john একটি অবজেক্ট রেফার করে যা Human-এর ইনস্ট্যান্স, এবং walk হল মেথড। তবে, এটি সম্পূর্ণ সত্য নয় কারণ অনুমান করা মেথডটি অবজেক্টের ক্লাস, সুপারক্লাস, বা মিক্সড-ইন মডিউল থেকে আসে।
এটি যোগ করা গুরুত্বপূর্ণ যে একটি অবজেক্টের ওপর মেথড ডিফাইন করা সম্ভব, এমনকি john এর মত একটি অবজেক্টের ওপরও, কারণ রুবিতে সবকিছু অবজেক্ট, এমনকি অবজেক্ট তৈরিতে ব্যবহৃত ক্লাসও।
def john.drip
puts "My drip is eternal"
end
drip মেথডটি শুধু john এ অ্যাসাইন করা অবজেক্ট দ্বারা অ্যাক্সেসযোগ্য। drip হল একটি সিঙ্গলটন মেথড যা john অবজেক্টের জন্য উপলব্ধ থাকবে। জানা গুরুত্বপূর্ণ যে সিঙ্গলটন মেথড এবং ক্লাস মেথডের মধ্যে কোনো পার্থক্য নেই, আপনি এই Stack Overflow উত্তর থেকে দেখতে পারেন। আপনি যদি উপরের উদাহরণের মত একটি অবজেক্টের ওপর ডিফাইন করা মেথডকে রেফার না করেন, তবে বলা যে মেথডটি একটি নির্দিষ্ট অবজেক্টের সম্বन्धিত ভুল হবে। আমাদের উদাহরণে, walk মেথডটি Movement মডিউলের, আর hello মেথডটি Human ক্লাসের। এই বোঝাপড়ার সাথে, এটিকে এক ধাপ এগিয়ে নেওয়া সহজ হবে, অর্থাৎ একটি অবজেক্টের ওপর কল করা সঠিক মেথড নির্ধারণ করতে রুবিকে অবজেক্টের ক্লাস বা সুপার ক্লাস বা অবজেক্টের হায়ারার্কিতে মিক্সড মডিউল চেক করতে হয়।
মডিউল মিক্স করা
রুবি শুধু সিঙ্গেল ইনহেরিটেন্স সাপোর্ট করে; একটি ক্লাস শুধু একটি ক্লাস থেকে ইনহেরিট করতে পারে। এটি চাইল্ড ক্লাসকে অন্য ক্লাসের আচরণ (মেথড) ইনহেরিট করতে সক্ষম করে। কিন্তু কী হয় যখন আপনার কাছে ভিন্ন ক্লাসগুলোর মাঝপথে শেয়ার করার জন্য আচরণ থাকে? উদাহরণস্বরূপ, Human ক্লাসের ইনস্ট্যান্সগুলোর জন্য walk মেথড উপলব্ধ করতে, আমরা Movement মডিউল Human ক্লাসে মিক্স ইন করতে পারি। তাই include ব্যবহার করে Human ক্লাস রিরাইট করলে এরকম হবে:
require "movement" # অনুমান করছি মডিউলটি movement.rb ফাইলে আছে
class Human
include Movement
attr_reader :name
def initialize(name)
@name = name
end
def hello
put "Hello! #{name}"
end
end
এখন, আমরা ইনস্ট্যান্সের ওপর walk মেথড কল করতে পারি:
john = Human.new("John")
john.walk
Include
যখন আপনি include কিওয়ার্ড ব্যবহার করেন, যেমন আমরা উপরে করেছি, তখন ইনক্লুড করা মডিউল(গুলো)의 মেথডগুলো ক্লাসে ইনস্ট্যান্স মেথড হিসেবে যোগ হয়। এর কারণ হল ইনক্লুডেড মডিউলটি Human ক্লাসের অ্যানসেস্টরগুলোর মাঝপথে যোগ হয়, যাতে Movement মডিউলটি Human ক্লাসের পেরেন্ট হিসেবে দেখা যায়। উপরের উদাহরণে আমরা দেখেছি, Human ক্লাসের ইনস্ট্যান্সের ওপর walk মেথড কল করা হয়েছে।
Extend
include-এর পাশাপাশি, রুবি আমাদের extend কিওয়ার্ড দিয়েছে। এটি মডিউল(গুলো)의 মেথডগুলোকে ক্লাসে ক্লাস মেথড হিসেবে উপলব্ধ করاয়, যাকে সিঙ্গলটন মেথডও বলা হয়, যেমন আমরা পূর্বে শিখেছি। তাই, যদি আমাদের Feeding নামে একটি মডিউল থাকে যেটি এরকম দেখে:
module Feeding
def food
"I make my food :)"
end
end
তবেই আমরা এই আচরণ Human ক্লাসে শেয়ার করতে পারি এটিকে রিকвайয়ার করে এবং extend Feeding যোগ করে। তবে, এটি ব্যবহার করতে, ইনস্ট্যান্সের ওপর food মেথড কল করার বদলে, আমরা ক্লাসের ওপরই কল করব, ঠিক ক্লাস মেথড কল করার মত।
Human.food
Prepend
এটি include-এর মতো কিন্তু কিছু পার্থক্য আছে, যেমন এই পোস্টে বর্ণনা করা আছে;
এটি আসলে include-এর মত কাজ করে, এক্সেপ্ট যে মডিউলটি ক্লাস এবং এর সুপারক্লাসের মাঝপথে চেইনে ইনসার্ট করার বদলে, এটি চেইনের বটমে, এমনকি ক্লাসের আগেই ইনসার্ট করে।
এর মানে হল যখন একটি ক্লাস ইনস্ট্যান্সের ওপর একটি মেথড কল করা হয়, রুবি প্রথমে মডিউলের মেথডগুলোতে তাকাবে, তারপর ক্লাসে।
যদি আমাদের একটি মডিউল থাকে যেটি hello মেথড ডিফাইন করে এবং আমরা সেটি prepend ব্যবহার করে Human ক্লাসে মিক্স করি, তবে রুবি ক্লাসের মেথডের বদলে মডিউলের মেথডটি কল করবে।
রুবির prepend কীভাবে কাজ করে সেটি ভালোভাবে বুঝতে, আমি এই আর্টিকেলটি দেখার পরামর্শ দিচ্ছি।
মেথড লুকআপ পাথ
রুবি ইন্টারপ্রেটার মেথড কল করার চেষ্টা করার সময় সবচেয়ে প্রথমে সিঙ্গলটন মেথডগুলোতে তাকায়। আমি এই repl তৈরি করেছি, যা আপনি ব্যবহার করে সম্ভাব্য ফলাফল দেখতে পারেন।
মান লিন, আমাদের কাছে নিচের মত কিছু মডিউল এবং ক্লাস আছে:
module One
def another
puts "From one module"
end
end
module Two
def another
puts "From two module"
end
end
module Three
def another
puts "From three module"
end
end
class Creature
def another
puts "From creature class"
end
end
আগে যাই এবং এইগুলোকে Human ক্লাসে মিক্স করি।
class Human < Creature
prepend Three
extend Two
include One
def another
puts "Instance method"
end
def self.another
puts "From Human class singleton"
end
end
মডিউল মিক্স করার পাশাপাশি, আমাদের একটি ইনস্ট্যান্স এবং ক্লাস মেথড আছে। আপনি দেখতে পাচ্ছেন Human ক্লাস Creature ক্লাসের সাবক্লাস।
প্রথম লুকআপ - সিঙ্গলটন মেথড
যখন আমরা Human.another রান করি, তখন প্রিন্ট হয় From Human class singleton, যা আমাদের ক্লাস মেথডে আছে। যদি আমরা ক্লাস মেথডটি কমেন্ট আউট করে আবার রান করি, তবে কনসোলে From two module প্রিন্ট হবে। এটি extend ব্যবহার করে মিক্স করা মডিউল থেকে আসে। এটি প্রমাণ করে যে লুকআপ সিঙ্গলটন মেথডগুলোর মাঝপথে শুরু হয়। যদি আমরা extend Two রিমুভ (বা কমেন্ট আউট) করে আবার কমান্ড রান করি, তবে এটি মেথড মিসিং এরর ফেলবে। আমরা এই এরর পাই কারণ রুবি সিঙ্গলটন মেথডগুলোর মাঝপথে another মেথড খুঁজে পায়নি।
আগে যাই এবং ক্লাস ইনস্ট্যান্স ব্যবহার করি একটি ইনস্ট্যান্স তৈরি করে:
n = Human.new
আমরা ইনস্ট্যান্সের জন্য একটি সিঙ্গলটন মেথডও তৈরি করব:
def n.another
puts "From n object"
end
এখন, যখন আমরা n.another রান করি, কল করা ভার্সনটি হল n অবজেক্টে ডিফাইন করা সিঙ্গলটন মেথড। রুবি এই কেসে extend দিয়ে মিক্স করা মডিউলে তাকাবে না কারণ আমরা ক্লাসের ইনস্ট্যান্সের ওপর মেথড কল করছি। জানা গুরুত্বপূর্ণ যে সিঙ্গলটন মেথডের প্রাসঙ্গিকতা extend দিয়ে মিক্স করা মডিউলের মেথডগুলোর চেয়ে বেশি।
দ্বিতীয় লুকআপ - prepend ব্যবহার করে মিক্স করা মডিউল
যদি আমরা n অবজেক্টের সিঙ্গলটন মেথড কমেন্ট আউট করে কমান্ড রান করি, তবে কল করা মেথডের ভার্সনটি হবে prepend ব্যবহার করে মিক্স করা মডিউল। এর কারণ হল prepend ব্যবহার করলে মডিউলটি ক্লাসের আগেই ইনসার্ট হয়।
তৃতীয় লুকআপ - ক্লাস
যদি আমরা মডিউল Three কমেন্ট আউট করি, তবে কল করা another মেথডের ভার্সনটি হবে ক্লাসে ডিফাইন করা ইনস্ট্যান্স মেথড।
চতুর্থ লুকআপ - include ব্যবহার করে মিক্স করা মডিউল
রুবি মেথড খুঁজার জন্য পরবর্তী জায়গা হল include ব্যবহার করে মিক্স করা মডিউল। তাই, যখন আমরা ইনস্ট্যান্স মেথড কমেন্ট আউট করি, তবে আমরা মডিউল One-এর ভার্সন পাই।
পঞ্চম লুকআপ - পেরেন্ট ক্লাস
যদি ক্লাসের একটি পেরেন্ট ক্লাস থাকে, রুবি ক্লাসে সার্চ করে। সার্চে পেরেন্ট ক্লাসে মিক্স করা মডিউলগুলোর ভেতরে যাওয়া অন্তর্ভুক্ত; যদি আমাদের মেথডটি পেরেন্ট ক্লাসে মিক্স করা একটি মডিউলে ডিফাইন করা থাকত, তবে মেথডটি কল হত।
মেথড সার্চের শেষ
আমরা মেথড সার্চ কোথায় শেষ হয় সেটি জানতে পারি এর অ্যানসেস্টর চেক করে: ক্লাসের ওপর .ancestors কল করে। Human ক্লাসের জন্য এটি করলে রিটার্ন হবে [Three, Human, One, Creature, Object, Kernel, BasicObject]। একটি মেথডের সার্চ BasicObject ক্লাসে শেষ হয়, যা রুবির রুট ক্লাস। যেকোনো অবজেক্ট যা কোনো ক্লাসের ইনস্ট্যান্স, সেটি BasicObject ক্লাস থেকে উদ্ভূত।
ডেভেলপার-ডিফাইনড পেরেন্ট ক্লাস পাস করার পর সার্চ নিচেরগুলোর ওপর যায়:
Objectক্লাসKernelমডিউলBasicObjectক্লাস
method_missing মেথড
আপনি যদি কিছুক্ষণ রুবি ব্যবহার করে থাকেন, তবে আপনি সম্ভবত NoMethodError-এর সামনা করেছেন, যা হয় যখন আপনি একটি অজানা মেথড একটি অবজেক্টের ওপর কল করার চেষ্টা করেন। এটি হয় রুবি অবজেক্টের অ্যানসেস্টরগুলোর মধ্য দিয়ে যাওয়ার পর এবং কল করা মেথডটি খুঁজে না পাওয়ার পর। আপনি যে এরর মেসেজ পান সেটি BasicObject ক্লাসে ডিফাইন করা method_missing মেথড দ্বারা হ্যান্ডেল করা হয়। আপনি যে অবজেক্টের ওপর মেথড কল করছেন সেটির জন্য মেথডটি ওভাররাইড করা সম্ভব, যা আপনি এটি চেক করে শিখতে পারেন।
উপসংহার
এখন আপনি জানেন রুবি একটি অবজেক্টের ওপর কল করা মেথড বুঝতে যে পথ ফলো করে। এই বোঝাপড়ার সাথে, আপনাকে একটি অবজেক্টের ওপর অজানা মেথড কল করার ফলে উদ্ভূত এরর সহজে ফিক্স করতে পারবেন।