সম্পাদনা : আমি এই উত্তরটি মূলত লিখেছিলাম 9 বছর হয়ে গেছে এবং এটি বর্তমান রাখতে কিছু প্রসাধনী শল্যচিকিত্সার দাবি রাখে।
আপনি এখানে সম্পাদনার আগে শেষ সংস্করণটি দেখতে পাবেন ।
আপনি নাম বা কীওয়ার্ড দ্বারা ওভাররাইট করা পদ্ধতিটি কল করতে পারবেন না । বাঁদর প্যাচিং এড়ানো উচিত এবং এর পরিবর্তে উত্তরাধিকারকে অগ্রাধিকার দেওয়া উচিত এর অনেকগুলি কারণ হ'ল স্পষ্টতই আপনি ওভাররাইড পদ্ধতিতে কল করতে পারেন ।
বানরের প্যাচিং এড়ানো
উত্তরাধিকার
সুতরাং, যদি সম্ভব হয় তবে আপনার এই জাতীয় কিছু পছন্দ করা উচিত:
class Foo
def bar
'Hello'
end
end
class ExtendedFoo < Foo
def bar
super + ' World'
end
end
ExtendedFoo.new.bar # => 'Hello World'
আপনি যদি Fooবস্তুগুলির সৃষ্টি নিয়ন্ত্রণ করেন তবে এটি কাজ করে। Fooপরিবর্তে একটি তৈরি করতে পারে এমন প্রতিটি স্থান পরিবর্তন করুন ExtendedFoo। আপনি যদি নির্ভরতা ইনজেকশন ডিজাইন প্যাটার্ন , কারখানার পদ্ধতি ডিজাইনের প্যাটার্ন , অ্যাবস্ট্রাক্ট ফ্যাক্টরি ডিজাইন প্যাটার্ন বা সেই লাইনগুলি বরাবর কিছু ব্যবহার করেন তবে এটি আরও ভাল কাজ করে, কারণ সেই ক্ষেত্রে আপনার পরিবর্তনের প্রয়োজন কেবলমাত্র একটি জায়গা।
প্রতিনিধি
আপনি যদি অবজেক্টগুলির সৃষ্টি নিয়ন্ত্রণ না করেনFoo , উদাহরণস্বরূপ কারণ সেগুলি এমন একটি ফ্রেমওয়ার্ক দ্বারা তৈরি করা হয়েছে যা আপনার নিয়ন্ত্রণের বাইরে (রুবি অন পাগলউদাহরণস্বরূপ), তারপরে আপনি মোড়ক ডিজাইনের প্যাটার্নটি ব্যবহার করতে পারেন :
require 'delegate'
class Foo
def bar
'Hello'
end
end
class WrappedFoo < DelegateClass(Foo)
def initialize(wrapped_foo)
super
end
def bar
super + ' World'
end
end
foo = Foo.new # this is not actually in your code, it comes from somewhere else
wrapped_foo = WrappedFoo.new(foo) # this is under your control
wrapped_foo.bar # => 'Hello World'
মূলত, সিস্টেমের সীমানায়, যেখানে Fooবস্তুটি আপনার কোডের মধ্যে আসে, আপনি এটিকে অন্য কোনও বস্তুতে মুড়িয়ে রাখুন এবং তারপরে আপনার কোডের অন্য যে কোনও জায়গায় আসলটির পরিবর্তে সেই বস্তুটি ব্যবহার করুন ।
এটি stdlib এর লাইব্রেরি Object#DelegateClassথেকে সহায়ক পদ্ধতি ব্যবহার করে delegate।
"পরিষ্কার" বানর প্যাচিং
বানরের প্যাচিং এড়ানোর জন্য উপরের দুটি পদ্ধতিতে সিস্টেম পরিবর্তন করা দরকার। এই বিভাগটি বানরের প্যাচিংয়ের পছন্দের এবং সর্বনিম্ন আক্রমণাত্মক পদ্ধতিটি দেখায়, সিস্টেমটি পরিবর্তন করা উচিত বিকল্প নয়।
Module#prependএই ব্যবহারের ক্ষেত্রে কমবেশি সমর্থন করার জন্য যুক্ত করা হয়েছিল। ক্লাসের নীচে সরাসরি মেশিনে মিশ্রিত করা বাদে Module#prependএকই কাজ করে:Module#include
class Foo
def bar
'Hello'
end
end
module FooExtensions
def bar
super + ' World'
end
end
class Foo
prepend FooExtensions
end
Foo.new.bar # => 'Hello World'
দ্রষ্টব্য: আমি Module#prependএই প্রশ্নেও কিছুটা লিখেছিলাম : রুবি মডিউল প্রিপেন্ড বনাম ডেরাইভেশন
মেশিন উত্তরাধিকার (ভাঙা)
আমি কিছু লোককে দেখেছি (এবং এটি স্ট্যাকওভারফ্লোতে কেন কাজ করে না সে সম্পর্কে জিজ্ঞাসা করে) এরকম কিছু, অর্থাত্ এটির includeপরিবর্তে মিক্সিন যুক্ত করা prepend:
class Foo
def bar
'Hello'
end
end
module FooExtensions
def bar
super + ' World'
end
end
class Foo
include FooExtensions
end
দুর্ভাগ্যক্রমে, এটি কাজ করবে না। এটি একটি ভাল ধারণা, কারণ এটি উত্তরাধিকার ব্যবহার করে যার অর্থ আপনি ব্যবহার করতে পারেন super। যাইহোক, Module#includemixin সন্নিবেশ উপরে উত্তরাধিকার শ্রেণীবিন্যাসে বর্গ, যার মানে FooExtensions#barকখনোই বলা হবে (এবং যদি এটা হয় বলা হয়, superআসলে না পড়ুন হবে Foo#barবরং করার Object#bar, যা বিদ্যমান নেই) যেহেতু Foo#barসবসময় প্রথম পাওয়া যাবে।
পদ্ধতি মোড়ানো
বড় প্রশ্ন হ'ল: আমরা প্রকৃতbar কোনও পদ্ধতি অবলম্বন না করে কীভাবে পদ্ধতিটি ধরে রাখতে পারি ? ফাংশনাল প্রোগ্রামিংয়ে যেমন উত্তরটি প্রায়শই ঘটে থাকে তেমনি lies আমরা প্রকৃত অবজেক্ট হিসাবে পদ্ধতিটি ধরে রাখি এবং আমরা এবং কেবলমাত্র আমরা সেই অবজেক্টটিকে ধরে রেখেছি তা নিশ্চিত করতে আমরা একটি ক্লোজার (অর্থাত্ একটি ব্লক) ব্যবহার করি :
class Foo
def bar
'Hello'
end
end
class Foo
old_bar = instance_method(:bar)
define_method(:bar) do
old_bar.bind(self).() + ' World'
end
end
Foo.new.bar # => 'Hello World'
এটি খুব পরিষ্কার: যেহেতু old_barকেবল একটি স্থানীয় পরিবর্তনশীল, তাই এটি শ্রেণিবদ্ধের শেষে শেষের বাইরে চলে যাবে এবং প্রতিচ্ছবি ব্যবহার করেও এটি যে কোনও জায়গা থেকে অ্যাক্সেস করা অসম্ভব ! এবং যেহেতু Module#define_methodএকটি ব্লক নেয় এবং তাদের আশেপাশের লেক্সিকাল পরিবেশের উপরে ব্লকগুলি বন্ধ হয়ে যায় (যার কারণে আমরা এখানে define_methodপরিবর্তে ব্যবহার defকরছি), এটি (এবং এটি কেবলমাত্র ) এর old_barবাইরেও অ্যাক্সেস পাবে , এমনকি এটি সুযোগ ছাড়িয়ে যাওয়ার পরেও।
সংক্ষিপ্ত ব্যাখ্যা:
old_bar = instance_method(:bar)
এখানে আমরা barপদ্ধতিটি কোনও UnboundMethodমেথড অবজেক্টে মোড়ানো এবং এটি স্থানীয় ভেরিয়েবলের জন্য বরাদ্দ করছি old_bar। এর অর্থ, barওভাররাইট হয়ে যাওয়ার পরেও আমাদের এখন ধরে রাখার উপায় রয়েছে।
old_bar.bind(self)
এটি কিছুটা জটিল। মূলত, রুবিতে (এবং প্রায় সমস্ত একক-প্রেরণা ভিত্তিক ওও ভাষাগুলিতে), একটি পদ্ধতি একটি নির্দিষ্ট রিসিভার অবজেক্টের সাথে আবদ্ধ, যার নাম selfরুবি। অন্য কথায়: একটি পদ্ধতি সর্বদা জানে যে কী ऑब्जेक्टটির জন্য ডাকা হয়েছিল, এটি এটি কী তা জানতে পারে self। তবে, আমরা একটি ক্লাস থেকে পদ্ধতিটি সরাসরি ধরলাম, কীভাবে এটি কীভাবে তা জানবে self?
ঠিক আছে, এটি হয় না, এ কারণেই bindআমাদের UnboundMethodপ্রথমে আমাদের কোনও বস্তুর কাছে যেতে হবে, যা কোনও Methodবস্তুকে আমরা কল করতে পারি তা ফিরিয়ে দেবে । ( UnboundMethodগুলি বলা যায় না, কারণ তাদের না জেনে কী করা উচিত তা তারা জানে না self))
এবং আমরা bindএটি কি ? আমরা কেবল bindএটা নিজেদের, যে যেভাবে এটা আচরণ করবে ঠিক মত মূল barআছে হবে!
শেষ অবধি, আমাদের Methodযেটি ফিরে এসেছে তা কল করতে হবে bind। রুবি ১.৯-তে, ( .()) এর জন্য কিছু নিফটি নতুন সিনট্যাক্স রয়েছে , তবে আপনি যদি 1.8 তে থাকেন তবে আপনি সহজ callপদ্ধতিটি ব্যবহার করতে পারেন ; যে কি .()যাই হোক না কেন অনুবাদ করা হয়।
এখানে আরও কয়েকটি প্রশ্ন দেওয়া হল, যেখানে এই ধারণাগুলির কয়েকটি ব্যাখ্যা করা হয়েছে:
"নোংরা" বানর প্যাচিং
আমাদের বানরের প্যাচিংয়ের সাথে আমাদের যে সমস্যা হচ্ছে তা হ'ল আমরা যখন পদ্ধতিটি ওভাররাইট করি তখন পদ্ধতিটি চলে যায়, তাই আমরা এটিকে আর কল করতে পারি না। সুতরাং, আসুন কেবল একটি ব্যাকআপ কপি করা যাক!
class Foo
def bar
'Hello'
end
end
class Foo
alias_method :old_bar, :bar
def bar
old_bar + ' World'
end
end
Foo.new.bar # => 'Hello World'
Foo.new.old_bar # => 'Hello'
এটির সাথে সমস্যাটি হ'ল আমরা এখন একটি অতিরিক্ত প্রয়োজনযুক্ত old_barপদ্ধতিতে নামস্থান দূষিত করেছি । এই পদ্ধতিটি আমাদের ডকুমেন্টেশনে প্রদর্শিত হবে, এটি আমাদের আইডিইগুলিতে কোড সমাপ্তিতে প্রদর্শিত হবে, এটি প্রতিবিম্বের সময় প্রদর্শিত হবে। এছাড়াও, এটি এখনও বলা যেতে পারে, তবে সম্ভবত আমরা এটি বানরকে ছুঁড়ে মারলাম, কারণ এটির আচরণটি আমরা প্রথমে পছন্দ করি না, তাই আমরা অন্য লোকেরা এটি কল করতে চাই না।
এটিতে কিছু অযাচিত বৈশিষ্ট্য রয়েছে তবুও দুর্ভাগ্যক্রমে এটি অ্যাসিভসপোর্টের মাধ্যমে জনপ্রিয় হয়ে উঠেছে Module#alias_method_chain।
আপনার কেবলমাত্র কয়েকটি নির্দিষ্ট জায়গায় ভিন্ন আচরণের প্রয়োজন এবং পুরো সিস্টেম জুড়ে নয়, আপনি বানর প্যাচকে একটি নির্দিষ্ট সুযোগে সীমাবদ্ধ করতে আপনি পরিশোধনগুলি ব্যবহার করতে পারেন। আমি Module#prependউপরের উদাহরণটি ব্যবহার করে এটি এখানে প্রদর্শন করতে যাচ্ছি :
class Foo
def bar
'Hello'
end
end
module ExtendedFoo
module FooExtensions
def bar
super + ' World'
end
end
refine Foo do
prepend FooExtensions
end
end
Foo.new.bar # => 'Hello'
# We haven’t activated our Refinement yet!
using ExtendedFoo
# Activate our Refinement
Foo.new.bar # => 'Hello World'
# There it is!
আপনি এই প্রশ্নে সংশোধনগুলি ব্যবহারের আরও পরিশীলিত উদাহরণ দেখতে পারেন: নির্দিষ্ট পদ্ধতির জন্য বানর প্যাচকে কীভাবে সক্ষম করবেন?
পরিত্যক্ত ধারণা
রুবি সম্প্রদায়টি বসতি স্থাপনের আগে Module#prepend, চারপাশে একাধিক বিভিন্ন ধারণা ভেসে উঠছিল যে আপনি মাঝে মাঝে পুরানো আলোচনায় রেফারেন্স দেখতে পাবেন। এই সমস্ত দ্বারা গ্রহন করা হয় Module#prepend।
পদ্ধতি সংযুক্তকারী
একটি ধারণা ছিল সিএলওএসের পদ্ধতিতে সংযুক্তকারীগুলির ধারণা। এটি মূলত অ্যাসপেক্ট-ওরিয়েন্টেড প্রোগ্রামিংয়ের একটি উপসেটের খুব হালকা ভার্সন।
মত সিনট্যাক্স ব্যবহার
class Foo
def bar:before
# will always run before bar, when bar is called
end
def bar:after
# will always run after bar, when bar is called
# may or may not be able to access and/or change bar’s return value
end
end
আপনি barপদ্ধতিটি কার্যকর করতে "প্রবেশ করতে" সক্ষম হবেন ।
তবে কীভাবে এবং কীভাবে আপনি barএর মধ্যে ফেরতের মান অ্যাক্সেস পান তা পুরোপুরি পরিষ্কার নয় bar:after। সম্ভবত আমরা (আব) superকীওয়ার্ডটি ব্যবহার করতে পারি?
class Foo
def bar
'Hello'
end
end
class Foo
def bar:after
super + ' World'
end
end
প্রতিস্থাপন
prependপূর্বের সংযোগকারীটি একটি ওভাররাইডিং পদ্ধতির সাথে মিক্সিন আইংয়ের সমতুল্য যা পদ্ধতির superএকেবারে শেষে কল করে । অনুরূপভাবে, সংযুক্তিটি prependএকটি ওভাররাইডিং পদ্ধতির সাথে মিক্সিন আইংয়ের সমতুল্য যা পদ্ধতির superএকেবারে প্রারম্ভে কল করে ।
কল করার আগে এবং পরে আপনি স্টাফও superকরতে পারেন, আপনি superএকাধিকবার কল করতে পারেন এবং পদ্ধতি সংযোজকগুলির চেয়ে আরও শক্তিশালী করে উভয়ই পুনরুদ্ধার superমানটি পুনরুদ্ধার ও হেরফের করতে পারেন prepend।
class Foo
def bar:before
# will always run before bar, when bar is called
end
end
# is the same as
module BarBefore
def bar
# will always run before bar, when bar is called
super
end
end
class Foo
prepend BarBefore
end
এবং
class Foo
def bar:after
# will always run after bar, when bar is called
# may or may not be able to access and/or change bar’s return value
end
end
# is the same as
class BarAfter
def bar
original_return_value = super
# will always run after bar, when bar is called
# has access to and can change bar’s return value
end
end
class Foo
prepend BarAfter
end
old শব্দ
এই ধারণাটির অনুরূপ একটি নতুন কীওয়ার্ড যুক্ত হয়েছে super, যা আপনাকে ওভাররাইট পদ্ধতিতে একইভাবে superকল করতে দেয় যাতে আপনি ওভাররাইড পদ্ধতিতে কল করতে পারেন :
class Foo
def bar
'Hello'
end
end
class Foo
def bar
old + ' World'
end
end
Foo.new.bar # => 'Hello World'
এর সাথে মুখ্য সমস্যাটি এটি পিছনের দিকে অসম্পূর্ণ: আপনার যদি পদ্ধতিটি কল করা হয় old, আপনি আর এটি কল করতে পারবেন না!
প্রতিস্থাপন
superprependএড মিক্সিনে একটি ওভাররাইড পদ্ধতিতে মূলত oldএই প্রস্তাবের মতোই ।
redef শব্দ
উপরের মতই, তবে ওভাররাইটন পদ্ধতিতে কল করার জন্য এবং defএকা রেখে নতুন কীওয়ার্ড যুক্ত করার পরিবর্তে আমরা পুনরায় সংজ্ঞায়িত পদ্ধতিগুলির জন্য একটি নতুন কীওয়ার্ড যুক্ত করি । এটি পিছনের দিকে সামঞ্জস্যপূর্ণ, কারণ সিনট্যাক্স বর্তমানে যাইহোক অবৈধ:
class Foo
def bar
'Hello'
end
end
class Foo
redef bar
old + ' World'
end
end
Foo.new.bar # => 'Hello World'
দুটি নতুন কীওয়ার্ড যুক্ত করার পরিবর্তে আমরা superঅভ্যন্তরের অর্থটি আবারও সংজ্ঞায়িত করতে পারি redef:
class Foo
def bar
'Hello'
end
end
class Foo
redef bar
super + ' World'
end
end
Foo.new.bar # => 'Hello World'
প্রতিস্থাপন
redefএকটি পদ্ধতি ইনিং একটি এড মিক্সিনে পদ্ধতিটিকে ওভাররাইড করার সমতুল্য prepend। superওভাররাইড পদ্ধতিতে superবা oldএই প্রস্তাব মতো আচরণ করে ।