রুবিতে মডিউল / মিশ্রণগুলি থেকে শ্রেণি পদ্ধতির উত্তরাধিকারী


95

এটি জানা যায় যে রুবিতে শ্রেণিবদ্ধ পদ্ধতিগুলি উত্তরাধিকার সূত্রে প্রাপ্ত হয়:

class P
  def self.mm; puts 'abc' end
end
class Q < P; end
Q.mm # works

তবে এটি আমার কাছে অবাক করে দিয়েছিল যে এটি মিক্সিনগুলির সাথে কাজ করে না:

module M
  def self.mm; puts 'mixin' end
end
class N; include M end
M.mm # works
N.mm # does not work!

আমি জানি যে # এক্সটেন্ড পদ্ধতিটি এটি করতে পারে:

module X; def mm; puts 'extender' end end
Y = Class.new.extend X
X.mm # works

তবে আমি উভয় উদাহরণ পদ্ধতি এবং শ্রেণীর পদ্ধতি সমন্বিত একটি মিশ্রিন (বা বরং লিখতে চাই) লিখছি:

module Common
  def self.class_method; puts "class method here" end
  def instance_method; puts "instance method here" end
end

এখন আমি যা করতে চাই তা হ'ল:

class A; include Common
  # custom part for A
end
class B; include Common
  # custom part for B
end

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

এটিকে দুটি পৃথক মডিউলে বিভক্ত করা আমার পক্ষে অবাস্তব বলে মনে হচ্ছে, একটিতে অন্তর্ভুক্ত করা, অন্যটি প্রসারিত করা। আর একটি সম্ভাব্য সমাধান Commonহ'ল মডিউলটির পরিবর্তে একটি ক্লাস ব্যবহার করা । তবে এটি কেবল একটি কাজ। (যদি সাধারণ কার্যকারিতার দুটি সেট থাকে Common1এবং Common2এবং আমাদের সত্যিই মিশ্রণগুলি থাকা দরকার তবে কী হয়?) শ্রেণিক পদ্ধতির উত্তরাধিকার মিশ্রণগুলি থেকে কাজ না করার কোনও গভীর কারণ আছে?



4
পার্থক্যটির সাথে, যে এখানে, আমি জানি এটি সম্ভব - আমি এটি করার ন্যূনতম উপায় এবং কেন ভদ্রতা পছন্দ কাজ করে না তার জন্য জিজ্ঞাসা করছি।
বরিস স্ট্যাটনিকি

4
আরও অভিজ্ঞতার সাথে, আমি বুঝতে পেরেছিলাম যে রুবি প্রোগ্রামারের অভিপ্রায়টি অনুমান করে খুব বেশি দূরে চলে যাবে যদি কোনও মডিউলও অন্তর্ভুক্তকারীটির সিঙ্গলটন শ্রেণিতে মডিউল পদ্ধতি যুক্ত করে। এটি কারণ "মডিউল পদ্ধতিগুলি" আসলে সিঙ্গলটন পদ্ধতি ছাড়া কিছুই নয়। মডিউলগুলি সিঙ্গেলটন পদ্ধতি থাকার জন্য বিশেষ নয়, তারা নাম স্থানগুলির জন্য বিশেষ যেখানে পদ্ধতি এবং ধ্রুবকগুলি সংজ্ঞায়িত করা হয়। নেমস্পেসটি মডিউলটির সিঙ্গলটন পদ্ধতিগুলির সাথে সম্পূর্ণভাবে সম্পর্কিত নয়, সুতরাং প্রকৃতপক্ষে সিঙ্গেলটন পদ্ধতিগুলির বর্গ উত্তরাধিকার মডিউলগুলির অভাবের চেয়ে বেশি বিস্ময়কর।
বরিস স্ট্যাটনিকিকি

উত্তর:


171

একটি সাধারণ প্রতিমা includedহুক ব্যবহার এবং সেখান থেকে ক্লাস পদ্ধতি ইনজেক্ট করা হয়।

module Foo
  def self.included base
    base.send :include, InstanceMethods
    base.extend ClassMethods
  end

  module InstanceMethods
    def bar1
      'bar1'
    end
  end

  module ClassMethods
    def bar2
      'bar2'
    end
  end
end

class Test
  include Foo
end

Test.new.bar1 # => "bar1"
Test.bar2 # => "bar2"

26
includeউদাহরণ পদ্ধতি যুক্ত করে, extendক্লাসের পদ্ধতিগুলি যুক্ত করে। এটা এভাবে কাজ করে. আমি অসঙ্গতি দেখছি না, কেবল প্রত্যাশার
তুলনা করছি

4
আমি আস্তে আস্তে এই সত্যটি তুলে ধরছি যে, আপনার পরামর্শটি এই সমস্যার ব্যবহারিক সমাধান পাওয়ার মতোই মার্জিত। তবে আমি ক্লাসের সাথে কাজ করে এমন কিছু মডিউলগুলির সাথে কাজ করে না তার কারণটি জানার জন্য আমি কৃতজ্ঞ হব।
বরিস স্ট্যাটনিকি

6
@ বরিসস্টিটনিক এই উত্তরটি বিশ্বাস করুন। এটি রুবির একটি খুব সাধারণ প্রতিমা, যা আপনি জিজ্ঞাসা করেছেন এমন ব্যবহারের কেসটি সমাধান করে এবং আপনি যে কারণগুলি ভোগ করেছেন তার জন্য সঠিকভাবে সমাধান করছেন। এটি "অচল" মনে হতে পারে তবে এটি আপনার সেরা বাজি। (আপনি যদি প্রায়শই এটি করেন তবে আপনি includedপদ্ধতির সংজ্ঞাটিকে অন্য মডিউলে নিয়ে যেতে এবং এটি আপনার মূল মডিউলে অন্তর্ভুক্ত করতে পারেন;)
ফ্রোগজ

4
"কেন?" সম্পর্কে আরও অন্তর্দৃষ্টি জন্য এই থ্রেডটি পড়ুন ?
ফ্রেগজ

4
@ পূর্বশক্তি: একটি ডামি ক্লাসে মডিউলটি অন্তর্ভুক্ত করুন।
সার্জিও টুলেন্টেসেভ

49

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

একটি মডিউল অন্তর্ভুক্ত করা হয় যখন কি ঘটে?

ক্লাসে একটি মডিউল অন্তর্ভুক্ত করা ক্লাসের পূর্বপুরুষদের সাথে মডিউল যুক্ত করে । আপনি যে কোনও ক্লাস বা মডিউলটির পূর্বপুরুষদের ancestorsপদ্ধতিটি কল করে দেখতে পারেন :

module M
  def foo; "foo"; end
end

class C
  include M

  def bar; "bar"; end
end

C.ancestors
#=> [C, M, Object, Kernel, BasicObject]
#       ^ look, it's right here!

যখন আপনি কোনও উদাহরণটিতে কোনও কল করেন C, রুবি প্রদত্ত নামের সাথে একটি উদাহরণ পদ্ধতি সন্ধানের জন্য এই পূর্বপুরুষের তালিকার প্রতিটি আইটেমটি দেখবেন । যেহেতু আমরা অন্তর্ভুক্ত Mমধ্যে C, Mএখন পূর্বপুরুষ C, তাই যখন আমরা কলfoo এর একটি দৃষ্টান্ত উপর C, রুবি যে পদ্ধতি পাবেন M:

C.new.foo
#=> "foo"

নোট করুন যে অন্তর্ভুক্তি ক্লাসে কোনও উদাহরণ বা শ্রেণীর পদ্ধতি অনুলিপি করে না - এটি ক্লাসে কেবল একটি "নোট" যুক্ত করে যে এটি অন্তর্ভুক্ত মডিউলটিতে উদাহরণ পদ্ধতিগুলিও সন্ধান করা উচিত।

আমাদের মডিউল "শ্রেণি" পদ্ধতি সম্পর্কে কি?

কারণ অন্তর্ভুক্তি কেবলমাত্র ক্লাসে মডিউল সহ উদাহরণের পদ্ধতিগুলি প্রেরণের পদ্ধতি পরিবর্তিত করে কেবল তার ক্লাসে তার উদাহরণ পদ্ধতিগুলি উপলব্ধ করে। মডিউলটিতে "শ্রেণি" পদ্ধতি এবং অন্যান্য ঘোষণাগুলি স্বয়ংক্রিয়ভাবে শ্রেণিতে অনুলিপি করা হয় না:

module M
  def instance_method
    "foo"
  end

  def self.class_method
    "bar"
  end
end

class C
  include M
end

M.class_method
#=> "bar"

C.new.instance_method
#=> "foo"

C.class_method
#=> NoMethodError: undefined method `class_method' for C:Class

রুবি কীভাবে শ্রেণিবদ্ধ পদ্ধতি প্রয়োগ করে?

রুবিতে, ক্লাস এবং মডিউলগুলি সরল বস্তু - সেগুলি শ্রেণীর উদাহরণ Classএবং Module। এর অর্থ হ'ল আপনি গতিশীলভাবে নতুন ক্লাস তৈরি করতে পারবেন, ভেরিয়েবল ইত্যাদি নির্ধারণ করুন assign

klass = Class.new do
  def foo
    "foo"
  end
end
#=> #<Class:0x2b613d0>

klass.new.foo
#=> "foo"

এছাড়াও রুবিতে, আপনার কাছে বস্তুগুলিতে তথাকথিত সিঙ্গলটন পদ্ধতি সংজ্ঞায়নের সম্ভাবনা রয়েছে । এই পদ্ধতিগুলি বস্তুর বিশেষ, লুকানো সিঙ্গলটন শ্রেণিতে নতুন উদাহরণ পদ্ধতি হিসাবে যুক্ত হয় :

obj = Object.new

# define singleton method
def obj.foo
  "foo"
end

# here is our singleton method, on the singleton class of `obj`:
obj.singleton_class.instance_methods(false)
#=> [:foo]

তবে কি ক্লাস এবং মডিউলগুলি কেবল প্লেইন অবজেক্টগুলিও নয়? আসলে তারা! তার মানে কি তারা সিঙ্গলটন পদ্ধতিও থাকতে পারে? হ্যাঁ এটা করে! এবং এভাবেই শ্রেণিবদ্ধের জন্ম হয়:

class Abc
end

# define singleton method
def Abc.foo
  "foo"
end

Abc.singleton_class.instance_methods(false)
#=> [:foo]

বা, শ্রেণি পদ্ধতি সংজ্ঞায়নের আরও সাধারণ উপায় হচ্ছে selfশ্রেণি সংজ্ঞা ব্লকের মধ্যে ব্যবহার করা, যা শ্রেণি অবজেক্টটি তৈরি হচ্ছে তা বোঝায়:

class Abc
  def self.foo
    "foo"
  end
end

Abc.singleton_class.instance_methods(false)
#=> [:foo]

আমি একটি মডিউলে শ্রেণিক পদ্ধতিগুলি কীভাবে অন্তর্ভুক্ত করব?

যেমনটি আমরা প্রতিষ্ঠিত করেছি, শ্রেণি পদ্ধতিগুলি ক্লাস অবজেক্টের সিঙ্গলটন শ্রেণিতে সত্যই কেবল উদাহরণ পদ্ধতি instance এর অর্থ কি এই যে আমরা ক্লাস পদ্ধতিগুলির একগুচ্ছ যোগ করার জন্য কেবল একটি মডেলকে সিঙ্গলটন শ্রেণিতে অন্তর্ভুক্ত করতে পারি ? হ্যাঁ এটা করে!

module M
  def new_instance_method; "hi"; end

  module ClassMethods
    def new_class_method; "hello"; end
  end
end

class HostKlass
  include M
  self.singleton_class.include M::ClassMethods
end

HostKlass.new_class_method
#=> "hello"

এই self.singleton_class.include M::ClassMethodsলাইনটি খুব সুন্দর দেখাচ্ছে না, তাই রুবি যুক্ত করেছে Object#extend, যা একই কাজ করে - যেমন বস্তুর একক শ্রেণিতে একটি মডিউল অন্তর্ভুক্ত করে:

class HostKlass
  include M
  extend M::ClassMethods
end

HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
#    ^ there it is!

চলন্ত extendকলটি মডিউলে

এই পূর্ববর্তী উদাহরণটি দুটি কারণে সুসংগঠিত কোড নয়:

  1. আমাদের এখন এবং উভয়কে কল করতে হবেincludeextendHostClass সংজ্ঞা আমাদের মডিউল সঠিকভাবে অন্তর্ভুক্ত জন্য। যদি আপনাকে অনেক অনুরূপ মডিউল অন্তর্ভুক্ত করতে হয় তবে এটি খুব জটিল হয়ে উঠতে পারে।
  2. HostClassসরাসরি রেফারেন্স M::ClassMethods, যা একটি হল বাস্তবায়ন বিস্তারিত মডিউলের Mযে HostClassচেনেন বা যত্নের প্রয়োজন করা উচিত নয়।

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

বিশেষ self.includedপদ্ধতিটির জন্য এটি ঠিক এটি । মডিউলটি অন্য শ্রেণিতে (বা মডিউল) অন্তর্ভুক্ত করার পরে রুবি স্বয়ংক্রিয়ভাবে এই পদ্ধতিটি কল করে এবং হোস্ট ক্লাসের অবজেক্টে প্রথম আর্গুমেন্ট হিসাবে পাস করে:

module M
  def new_instance_method; "hi"; end

  def self.included(base)  # `base` is `HostClass` in our case
    base.extend ClassMethods
  end

  module ClassMethods
    def new_class_method; "hello"; end
  end
end

class HostKlass
  include M

  def self.existing_class_method; "cool"; end
end

HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
#    ^ still there!

অবশ্যই, ক্লাসের পদ্ধতিগুলি যুক্ত করা কেবলমাত্র আমরাই করতে পারি না self.included। আমাদের ক্লাস অবজেক্ট রয়েছে, সুতরাং আমরা এটিতে অন্য কোনও (শ্রেণি) পদ্ধতি কল করতে পারি:

def self.included(base)  # `base` is `HostClass` in our case
  base.existing_class_method
  #=> "cool"
end

4
দুর্দান্ত উত্তর! এক দিন লড়াইয়ের পরে অবশেষে ধারণাটি বুঝতে সক্ষম হয়েছিল। ধন্যবাদ.
संकल्प

4
আমি মনে করি এটি সম্ভবত সবচেয়ে ভাল লিখিত উত্তর হতে পারে যা আমি এর আগে দেখেছি। অবিশ্বাস্য স্পষ্টতার জন্য এবং আমার রুবিকে অবিচ্ছিন্ন করার জন্য আপনাকে ধন্যবাদ। আমি যদি এটি উপহার দিতে পারি তবে 100 পিস বোনাস!
পিটার নিক্সি

7

সার্জিও যেমন মন্তব্যে উল্লেখ করেছেন, ইতিমধ্যে কারাগারে থাকা ছেলেদের জন্য (বা অ্যাক্টিভ সমর্থনের উপর নির্ভর করে কিছু মনে করবেন না ), Concernএখানে সহায়ক is

require 'active_support/concern'

module Common
  extend ActiveSupport::Concern

  def instance_method
    puts "instance method here"
  end

  class_methods do
    def class_method
      puts "class method here"
    end
  end
end

class A
  include Common
end

3

আপনি আপনার কেক রাখতে পারেন এবং এটি করে এটি খেয়েও পারেন:

module M
  def self.included(base)
    base.class_eval do # do anything you would do at class level
      def self.doit #class method
        @@fred = "Flintstone"
        "class method doit called"
      end # class method define
      def doit(str) #instance method
        @@common_var = "all instances"
        @instance_var = str
        "instance method doit called"
      end
      def get_them
        [@@common_var,@instance_var,@@fred]
      end
    end # class_eval
  end # included
end # module

class F; end
F.include M

F.doit  # >> "class method doit called"
a = F.new
b = F.new
a.doit("Yo") # "instance method doit called"
b.doit("Ho") # "instance method doit called"
a.get_them # >> ["all instances", "Yo", "Flintstone"]
b.get_them # >> ["all instances", "Ho", "Flintstone"]

আপনি যদি উদাহরণটি এবং ক্লাস ভেরিয়েবল যুক্ত করার ইচ্ছা রাখেন তবে আপনি আপনার চুলগুলি টেনে আনবেন কারণ আপনি যদি এইভাবে না করেন তবে ভাঙ্গা কোডের একগুচ্ছ হয়ে যাবেন।


কিছু অদ্ভুত জিনিস রয়েছে যা ক্লাস_ভাল ব্লকটি পাস করার সময় কার্যকর হয় না, যেমন ধ্রুবকগুলি সংজ্ঞায়িত করা, নেস্টেড ক্লাসগুলি সংজ্ঞায়িত করা এবং পদ্ধতির বাইরে ক্লাস ভেরিয়েবলগুলি ব্যবহার করা। এই জিনিসগুলিকে সমর্থন করার জন্য, আপনি ক্লাস_এভালকে ব্লকের পরিবর্তে একটি হেরডোক (স্ট্রিং) দিতে পারেন: বেস.class_eval << - 'সমাপ্তি'
পল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.