রুবি কে মহান 만드는 জিনিসগুলোতে একটা হলো আমরা প্রায় সব কাস্টোমাইজ করতে পারি আমাদের প্রয়োজন অনুযায়ী। এটি দুভাবে উপকারী এবং বিপদজনক। পায়ে আত্মঘাত করা সহজ, কিন্তু যত্নপূর্বক ব্যবহার করলে, এটি খুব শক্তিশালী সমাধান ফলাফল দিতে পারে।
Ruby Magic-এ, আমরা মনে করি উপকারী এবং বিপদজনক একটি চমৎকার কম্বিনেশন। চলুন দেখি রুবি কীভাবে অবজেক্ট তৈরি এবং ইনিশিয়ালাইজ করে এবং কীভাবে আমরা ডিফল্ট আচরণ পরিবর্তন করতে পারি।
ক্লাস থেকে নতুন অবজেক্ট তৈরি করার বেসিকস
শুরু করতে, দেখি রুবিতে অবজেক্ট কীভাবে তৈরি করা হয়। একটি নতুন অবজেক্ট (বা ইনস্ট্যান্স) তৈরি করতে, আমরা ক্লাসে new কল করি। অন্যান্য ল্যাঙ্গুয়েজের বিপরীতে, new ল্যাঙ্গুয়েজের নিজস্ব একটি কীওয়ার্ড নয়, বরং একটি মেথড যা অন্য যেকোনো মেথডের মতই কল করা হয়।
class Dog
end
object = Dog.new
নতুন তৈরি অবজেক্ট কাস্টোমাইজ করতে, new মেথডে আর্গুমেন্ট পাস করা সম্ভব। যা যেকোনো আর্গুমেন্ট হিসেবে পাস করা হবে, সেটি ইনিশিয়ালাইজারকে পাস করা হবে।
class Dog
def initialize(name)
@name = name
end
end
object = Dog.new('Good boy')
আবার, অন্যান্য ল্যাঙ্গুয়েজের বিপরীতে, রুবিতে ইনিশিয়ালাইজারও শুধু একটি মেথড, কোনো স্পেশাল সিনট্যাক্স বা কীওয়ার্ড নয়।
এটি ধরে রেখে, কি এগুলো মেথডের সাথে لعب করা সম্ভব নয়, যেন অন্য যেকোনো রুবি মেথডের সাথে করা যায়? অবশ্যই সম্ভব!
একটি সিঙ্গল অবজেক্টের আচরণ পরিবর্তন করা
মান লিন, আমরা চাই একটি নির্দিষ্ট ক্লাসের সব অবজেক্ট সবসময় লগ স্টেটমেন্ট প্রিন্ট করবে, এমনকি যদি মেথডটি সাবক্লাসে ওভাররাইড করা থাকে। এক উপায় হলো অবজেক্টের সিঙ্গলটন ক্লাসে একটি মডিউল যোগ করা।
module Logging
def make_noise
puts "Started making noise"
super
puts "Finished making noise"
end
end
class Bird
def make_noise
puts "Chirp, chirp!"
end
end
object = Bird.new
object.singleton_class.include(Logging)
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
এই উদাহরণে, একটি Bird অবজেক্ট Bird.new ব্যবহার করে তৈরি করা হয়, এবং Logging মডিউলটি ফলাফল অবজেক্টে এর সিঙ্গলটন ক্লাস ব্যবহার করে ইনক্লуд করা হয়।
সিঙ্গলটন ক্লাস কী?
রুবি একটি একক অবজেক্টের জন্য উникаল মেথড অনুমতি দেয়। এটিকে সমর্থন করতে, রুবি অবজেক্ট এবং এর প্রকৃত ক্লাসের মধ্যে একটি অনামী ক্লাস যোগ করে। যখন মেথড কল করা হয়, সিঙ্গলটন ক্লাসে ডিফাইন করা মেথডগুলো প্রকৃত ক্লাসের মেথডগুলোর ওপর প্রেসিডেন্স পায়। এই সিঙ্গলটন ক্লাসগুলো প্রতিটি অবজেক্টের জন্য উникаল, তাই সেগুলোতে মেথড যোগ করলে প্রকৃত ক্লাসের অন্য কোনো অবজেক্টে প্রভাব ফেলে না। ক্লাস এবং অবজেক্ট সম্পর্কে আরও জানুন Programming Ruby গাইডে।
প্রতিবার অবজেক্ট তৈরি হলে এর সিঙ্গলটন ক্লাস মডিফাই করা একটু কষ্টসাধ্য। তাই চলুন Logging ক্লাস ইনক্লুশনটি ইনিশিয়ালাইজারে সরিয়ে দিই যাতে প্রতিবার অবজেক্ট তৈরি হলে সেটি যোগ হয়।
module Logging
def make_noise
puts "Started making noise"
super
puts "Finished making noise"
end
end
class Bird
def initialize
singleton_class.include(Logging)
end
def make_noise
puts "Chirp, chirp!"
end
end
object = Bird.new
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
যদিও এটি ভালো কাজ করে, যদি আমরা Bird-এর একটি সাবক্লাস তৈরি করি, যেন Duck, এর ইনিশিয়ালাইজারকে super কল করতে হবে Logging আচরণ রিটেইন করার জন্য। যতোই কেউ বলতে পারে যে একটি মেথড ওভাররাইড করার সময় সবসময় সঠিকভাবে super কল করা একটি ভালো অভ্যাস, চলুন একটি উপায় খুঁজে বের করি যেটি এটিকে আবশ্যক করে না।
যদি আমরা সাবক্লাস থেকে super কল না করি, আমরা Logging ইনক্লুশন হারিয়ে যাই:
class Duck < Bird
def initialize(name)
@name = name
end
def make_noise
puts "#{@name}: Quack, quack!"
end
end
object = Duck.new('Felix')
object.make_noise
# Felix: Quack, quack!
বদলে, চলুন Bird.new ওভাররাইড করি। যেমন আগে বলা হয়েছে, new ক্লাসে ইমপ্লিমেন্টেড শুধু একটি মেথড। তাই আমরা এটিকে ওভাররাইড করতে পারি, super কল করতে পারি, এবং নতুন তৈরি অবজেক্টকে আমাদের প্রয়োজন অনুযায়ী মডিফাই করতে পারি।
class Bird
def self.new(*arguments, &block)
instance = super
instance.singleton_class.include(Logging)
instance
end
end
object = Duck.new('Felix')
object.make_noise
# Started making noise
# Felix: Quack, quack!
# Finished making noise
কিন্তু, যদি আমরা ইনিশিয়ালাইজারে make_noise কল করি তখন কী হবে? দুর্ভাগ্যক্রমে, যেহেতু সিঙ্গলটন ক্লাস এখনও Logging মডিউল ইনক্লуд করেনি, আমরা প্রত্যাশিত আউটপুট পাবো না।
শুভভাবে, একটি সমাধান আছে: ডিফল্ট .new আচরণটি স্ক্র্যাচ থেকে তৈরি করা সম্ভব allocate কল করে।
class Bird
def self.new(*arguments, &block)
instance = allocate
instance.singleton_class.include(Logging)
instance.send(:initialize, *arguments, &block)
instance
end
end
allocate কল করলে ক্লাসের একটি নতুন, আনইনিশিয়ালাইজড অবজেক্ট রিটার্ন করে। তাই এরপর, আমরা অতিরিক্ত আচরণ ইনক্লуд করতে পারি এবং কেবল তারপর, সেই অবজেক্টে initialize মেথড কল করতে পারি। (কারণ initialize ডিফল্টভাবে প্রাইভেট, আমাদের send ব্যবহার করতে বাধ্য হয় এই কাজের জন্য)।
Class#allocate সম্পর্কে সত্য
অন্যান্য মেথডের বিপরীতে, allocate ওভাররাইড করা সম্ভব নয়। রুবি allocate-র জন্য কনভেনশনাল মেথড ডিস্প্যাচিং ব্যবহার করে না ইন্টারন্যালি। ফলস্বরূপ, শুধু allocate ওভাররাইড করে new ওভাররাইড ছাড়া কাজ করে না। য покși, যদি আমরা সরাসরি allocate কল করি, রুবি রিডিফাইন করা মেথড কল করবে। Class#new এবং Class#allocate সম্পর্কে আরও জানুন রুবির ডকুমেন্টেশনে।
আমরা কেন এই করব?
অনেক জিনিসের মত, রুবি অবজেক্ট ক্লাস থেকে তৈরি করার পদ্ধতি পরিবর্তন করা বিপদজনক এবং অপ্রত্যাশিত ভাবে জিনিসগুলো ভাঙতে পারে।
তবুও, অবজেক্ট ক্রিয়েশন পরিবর্তন করার বৈধ ইউজ কেস আছে। উদাহরণস্বরূপ, ActiveRecord allocate ব্যবহার করে একটি ভিন্ন init_from_db মেথড সহ ইনিশিয়ালাইজেশন প্রক্রিয়া পরিবর্তন করে ডেটাবেস থেকে অবজেক্ট তৈরি করার সময় বনাম আনসেভড অবজেক্ট বিল্ড করার সময়। এটি allocate ব্যবহার করে রেকর্ড কনভার্ট করতেও একসাথে সিঙ্গল-টেবিল ইনহেরিটেন্স টাইপের মধ্যে becomes দিয়ে।
সবচেয়ে গুরুত্বপূর্ণ, অবজেক্ট ক্রিয়েশনের সাথে খেলা দিয়ে, আপনি রুবিতে কীভাবে কাজ করে সেটা সম্পর্কে গভীর অন্তর্দৃষ্টি পান এবং আপনার মন বিভিন্ন সমাধানের জন্য খুলে যাওয়া। আমরা আশা করি আপনি আর্টিকেলটি উপভোগ করেছেন।
আমরা রুবির ডিফল্ট অবজেক্ট ক্রিয়েশন পদ্ধতি পরিবর্তন করে আপনি যে জিনিসগুলো ইমপ্লিমেন্ট করেছেন সেগুলো শুনতে চাই। দয়া করে @AppSignal-এ আপনার চিন্তা টুইট করতে সংকোচ করবেন না।