2009-02-04 10 views
93

मुझे पता है कि रूबी में अमूर्त वर्ग की कोई अवधारणा नहीं है। लेकिन अगर इसे लागू करने की जरूरत है, तो इसके बारे में कैसे जाना है? मैं कुछ की तरह कोशिश की ...रूबी में एक अमूर्त वर्ग को कैसे कार्यान्वित करें?

class A 
    def self.new 
    raise 'Doh! You are trying to write Java in Ruby!' 
    end 
end 

class B < A 
    ... 
    ... 
end 

लेकिन जब मैं बी का दृष्टांत करने की कोशिश, उसका आंतरिक A.new जो अपवाद को बढ़ाने के लिए जा रहा है कॉल करने के लिए जा रहा है।

इसके अलावा, मॉड्यूल को तत्काल नहीं किया जा सकता है लेकिन उन्हें विरासत में भी प्राप्त नहीं किया जा सकता है। नई विधि निजी बनाना भी काम नहीं करेगा। कोई संकेतक?

+1

मॉड्यूल को मिश्रित किया जा सकता है, लेकिन मुझे लगता है कि आपको किसी अन्य कारण से शास्त्रीय विरासत की आवश्यकता है? – Zach

+6

ऐसा नहीं है कि मैंने एक सार वर्ग को लागू करने के लिए _need_ किया है। मैं सोच रहा था कि इसे कैसे किया जाए, अगर किसी को इसे करने की ज़रूरत है। एक प्रोग्रामिंग समस्या। बस। – Chirantan

+103

'उठाओ" दोह! आप रूबी में जावा लिखने की कोशिश कर रहे हैं "। –

उत्तर

53

मुझे रूबी में अमूर्त कक्षाओं का उपयोग करना पसंद नहीं है (लगभग हमेशा एक बेहतर तरीका है)। यदि आप वास्तव में लगता है कि यह स्थिति के लिए सबसे अच्छी तकनीक है, हालांकि, आप निम्नलिखित स्निपेट का उपयोग कर सकते अधिक कथात्मक तरीकों के बारे में जो अमूर्त हैं होने के लिए:

module Abstract 
    def abstract_methods(*args) 
    args.each do |name| 
     class_eval(<<-END, __FILE__, __LINE__) 
     def #{name}(*args) 
      raise NotImplementedError.new("You must implement #{name}.") 
     end 
     END 
     # important that this END is capitalized, since it marks the end of <<-END 
    end 
    end 
end 

require 'rubygems' 
require 'rspec' 

describe "abstract methods" do 
    before(:each) do 
    @klass = Class.new do 
     extend Abstract 

     abstract_methods :foo, :bar 
    end 
    end 

    it "raises NoMethodError" do 
    proc { 
     @klass.new.foo 
    }.should raise_error(NoMethodError) 
    end 

    it "can be overridden" do 
    subclass = Class.new(@klass) do 
     def foo 
     :overridden 
     end 
    end 

    subclass.new.foo.should == :overridden 
    end 
end 

मूल रूप से, आप सिर्फ इतना है कि सार हैं तरीकों की सूची के साथ abstract_methods फोन , और जब उन्हें अमूर्त वर्ग के उदाहरण से बुलाया जाता है, तो NotImplementedError अपवाद उठाया जाएगा।

+0

यह वास्तव में एक इंटरफेस की तरह है लेकिन मुझे विचार मिलता है। धन्यवाद। – Chirantan

+4

यह 'NotImplementedError' के वैध उपयोग मामले के रूप में नहीं लगता है जिसका अनिवार्य रूप से" प्लेटफ़ॉर्म-निर्भर, आपके पर उपलब्ध नहीं है "का अर्थ है। [दस्तावेज़ देखें] (http://ruby-doc.org/core-1.9.3/NotImplementedError.html)। – skalee

+3

आप मेटा-प्रोग्रामिंग के साथ एक लाइन विधि को प्रतिस्थापित कर रहे हैं, अब एक मिश्रण जोड़ने और एक विधि कॉल करने की आवश्यकता है। मुझे नहीं लगता कि यह बिल्कुल अधिक घोषणात्मक है। –

3

व्यक्तिगत रूप से मैं अमूर्त वर्गों के तरीकों में NotImplementedError को बढ़ाता हूं। लेकिन आप जिन कारणों का उल्लेख करते हैं, उनके लिए आप इसे 'नई' विधि से बाहर छोड़ना चाहेंगे।

+0

लेकिन फिर इसे तत्काल होने से कैसे रोकें? – Chirantan

+0

व्यक्तिगत रूप से मैं रूबी के साथ शुरुआत कर रहा हूं, लेकिन पायथन में, घोषित __init ___() विधियों के साथ उप-वर्ग स्वचालित रूप से अपने सुपरक्लास '__init __() विधियों को कॉल नहीं करते हैं। मुझे आशा है कि रुबी में एक समान अवधारणा होगी, लेकिन जैसा कि मैंने कहा था कि मैं अभी शुरू कर रहा हूं। – Zack

+0

रूबी माता-पिता की 'प्रारंभिक' विधियों में स्वचालित रूप से सुपर के साथ बुलाए जाने तक स्वचालित रूप से कॉल नहीं किया जाता है। – mk12

1

आपके दृष्टिकोण में कुछ भी गलत नहीं है। आरंभ में त्रुटि उत्पन्न करना ठीक लगता है, जब तक कि आपके सभी उप-वर्गों ने पाठ्यक्रम की शुरूआत को ओवरराइड किया हो। लेकिन आप स्वयं को परिभाषित नहीं करना चाहते हैं। नया ऐसा। मैं यही करूँगा।

class A 
    class AbstractClassInstiationError < RuntimeError; end 
    def initialize 
    raise AbstractClassInstiationError, "Cannot instantiate this class directly, etc..." 
    end 
end 

एक और दृष्टिकोण एक मॉड्यूल में उस कार्यक्षमता को रखा जाएगा, जैसा कि आपने उल्लेख किया है, कभी भी उत्तेजित नहीं किया जा सकता है। फिर किसी अन्य वर्ग से विरासत के बजाय अपनी कक्षाओं में मॉड्यूल शामिल करें। हालांकि, यह सुपर जैसी चीजों को तोड़ देगा।

तो यह इस बात पर निर्भर करता है कि आप इसे कैसे व्यवस्थित करना चाहते हैं।

class A 
    def initialize 
    raise 'Doh! You are trying to instantiate an abstract class!' 
    end 
end 

class B < A 
    def initialize 
    end 
end 
+0

मैं इसे नहीं करना चाहूंगा, या तो। बच्चे तब "सुपर" नहीं कह सकते हैं। –

36

इस प्रयास करें "मैं कुछ सामान है कि उपयोग करने के लिए अन्य वर्गों के लिए deigned है कैसे लिख सकता हूँ" कक्षा, अपने एनी विधि में, त्रुटि को फेंकने से पहले स्वयं == ए जांचें।

लेकिन वास्तव में, एक मॉड्यूल जो आप यहां चाहते हैं उतना ही लगता है - उदाहरण के लिए, संख्यात्मक चीज ऐसी चीज है जो अन्य भाषाओं में एक अमूर्त वर्ग हो सकती है। आप तकनीकी रूप से उन्हें उपclass नहीं कर सकते हैं, लेकिन include SomeModule पर कॉल करना लगभग एक ही लक्ष्य प्राप्त करता है। क्या कोई कारण है कि यह आपके लिए काम नहीं करेगा?

+1

यह ठीक +1 है –

+34

यदि आप बी के 'प्रारंभिक' में सुपर का उपयोग करने में सक्षम होना चाहते हैं, तो आप वास्तव में ए # प्रारंभ में 'जो भी self.class == ए' प्रारंभ कर सकते हैं। वास्तव में – mk12

4

आप एक uninstantiable के साथ जाना चाहते हैं: हालांकि मॉड्यूल की समस्या को हल करने के लिए एक क्लीनर समाधान की तरह लग

3

आप एक सार तत्व के साथ सेवा करने का क्या उद्देश्य कर रहे हैं? रूबी में ऐसा करने का शायद एक बेहतर तरीका है, लेकिन आपने कोई विवरण नहीं दिया।

मेरा सूचक यह है; mixin विरासत का उपयोग करें।

+0

। एक मॉड्यूल को मिलाकर एक सार क्लास का उपयोग करने के बराबर होगा: http://wiki.c2.com/?AbstractClass PS: चलो उन्हें मॉड्यूल कहते हैं, मिक्स्डिन नहीं, क्योंकि मॉड्यूल वे हैं जो उन्हें हैं और इन्हें मिलाकर आप उनके साथ क्या करते हैं। – Magne

3

एक अन्य जवाब:

module Abstract 
    def self.append_features(klass) 
    # access an object's copy of its class's methods & such 
    metaclass = lambda { |obj| class << obj; self ; end } 

    metaclass[klass].instance_eval do 
     old_new = instance_method(:new) 
     undef_method :new 

     define_method(:inherited) do |subklass| 
     metaclass[subklass].instance_eval do 
      define_method(:new, old_new) 
     end 
     end 
    end 
    end 
end 

यह सामान्य #method_missing पर निर्भर करता है लागू नहीं किया गया तरीकों रिपोर्ट करने के लिए, लेकिन (भले ही वे एक इनिशियलाइज़ विधि है)

class A 
    include Abstract 
end 
class B < A 
end 

B.new #=> #<B:0x24ea0> 
A.new # raises #<NoMethodError: undefined method `new' for A:Class> 

कार्यान्वित किया जा रहा से सार कक्षाएं रहता है अन्य पोस्टर्स की तरह कहा है, आप शायद एक अमूर्त वर्ग के बजाय, एक मिश्रण का उपयोग करना चाहिए।

10

प्रोग्रामिंग रूबी के पिछले 6 1/2 वर्षों में, मेरे पास एक सारणी कक्षा की आवश्यकता नहीं है।

यदि आप सोच रहे हैं कि आपको एक अमूर्त वर्ग की आवश्यकता है, तो आप ऐसी भाषा में बहुत अधिक सोच रहे हैं जो उन्हें रूबी में नहीं, प्रदान करता है।

जैसा कि अन्य ने सुझाव दिया है, एक मिश्रण उन चीजों के लिए अधिक उपयुक्त है जो इंटरफेस (जैसे जावा उन्हें परिभाषित करता है) माना जाता है, और आपके डिजाइन पर पुनर्विचार करना उन चीजों के लिए अधिक उपयुक्त है जिन्हें सी ++ जैसी अन्य भाषाओं से अमूर्त कक्षाओं की आवश्यकता होती है।

+18

आपको या तो जावा में एक सारणी वर्ग की आवश्यकता नहीं है। यह दस्तावेज करने का एक तरीका है कि यह एक बेस क्लास है और इसे आपकी कक्षा में विस्तार करने वाले लोगों के लिए तत्काल नहीं किया जाना चाहिए। – fijiaaron

+2

आईएमओ, कुछ भाषाओं को ऑब्जेक्ट उन्मुख प्रोग्रामिंग के अपने विचारों को सीमित करना चाहिए। किसी दिए गए परिस्थिति में क्या उचित है भाषा पर निर्भर नहीं होना चाहिए, जब तक कोई प्रदर्शन संबंधित कारण न हो (या कुछ अधिक आकर्षक)। – thekingoftruth

+10

@ फिजियारॉन: यदि आप ऐसा सोचते हैं, तो आप निश्चित रूप से समझ नहीं पाएंगे कि सार आधार वर्ग क्या हैं। वे "दस्तावेज" के लिए बहुत अधिक नहीं हैं कि एक वर्ग को तत्काल नहीं किया जाना चाहिए (यह इसके बजाय सार का प्रभाव है)। व्युत्पन्न कक्षाओं के समूह के लिए एक आम इंटरफ़ेस बताते हुए यह अधिक है, जो तब गारंटी देता है कि इसे कार्यान्वित किया जाएगा (यदि नहीं, व्युत्पन्न वर्ग भी अमूर्त रहेगा)। इसका लक्ष्य कक्षाओं के लिए लिस्कोव के प्रतिस्थापन सिद्धांत का समर्थन करना है जिसके लिए तत्कालता बहुत अधिक समझ में नहीं आती है। – SasQ

11
class A 
    private_class_method :new 
end 

class B < A 
    public_class_method :new 
end 
+5

इसके अतिरिक्त कोई भी उप-वर्गों में कन्स्ट्रक्टर विधि स्वचालित रूप से दिखाई देने के लिए अभिभावक वर्ग के विरासत हुक का उपयोग कर सकता है: def A.inherited (subclass); subclass.instance_eval {public_class_method: नया}; अंत – t6d

+1

बहुत अच्छा t6d। एक टिप्पणी के रूप में, बस यह सुनिश्चित करें कि यह दस्तावेज है, क्योंकि यह आश्चर्यजनक व्यवहार है (कम से कम आश्चर्य का उल्लंघन करता है)। – bluehavana

102

बस देर से यहाँ में झंकार के, मुझे लगता है कि अमूर्त वर्ग, especially because they can add methods to it on the fly instantiating से किसी को रोकने के लिए कोई कारण नहीं है कि।

रूबी की तरह बतख-टाइपिंग भाषा, रनटाइम पर उपस्थिति/अनुपस्थिति या विधियों के व्यवहार का उपयोग यह निर्धारित करने के लिए करें कि उन्हें कॉल किया जाना चाहिए या नहीं। इसलिए अपने प्रश्न, के रूप में यह एक सार विधि पर लागू होता है, भावना

def get_db_name 
    raise 'this method should be overriden and return the db name' 
end 

बनाता है और उस कहानी का अंत के बारे में होना चाहिए। जावा में अमूर्त वर्गों का उपयोग करने का एकमात्र कारण यह है कि कुछ विधियों को "भरने" मिलते हैं जबकि अन्यों के पास अमूर्त वर्ग में उनका व्यवहार होता है। एक बतख-टाइपिंग भाषा में, फोकस विधियों पर है, कक्षाओं/प्रकारों पर नहीं, इसलिए आपको अपनी चिंताओं को उस स्तर पर ले जाना चाहिए।

अपने प्रश्न में, आप मूल रूप से जावा से abstract कीवर्ड को फिर से बनाने की कोशिश कर रहे हैं, जो रूबी में जावा करने के लिए कोड-गंध है।

+2

सार कक्षाएं जावा में भी एक कोड गंध हैं। –

+3

@ क्रिस्टोफर पेरी: किसी भी कारण क्यों? – SasQ

+0

@SasQ क्योंकि यह आपके कोड में तंग युग्मन प्रस्तुत करता है। यह माता-पिता और भाई के बीच निर्भरता बनाता है। एक बेहतर दृष्टिकोण, जो युग्मन के बिना समान प्रभाव प्राप्त करता है वह विरासत के बजाय संरचना का उपयोग करना है। –

3

मैंने इसे इस तरह से किया, इसलिए यह गैर-वर्ग वर्ग पर नया खोजने के लिए बाल वर्ग पर नए को फिर से परिभाषित करता है। मुझे अभी भी रूबी में अमूर्त कक्षाओं का उपयोग करने में कोई व्यावहारिक नहीं दिख रहा है।

puts 'test inheritance' 
module Abstract 
    def new 
    throw 'abstract!' 
    end 
    def inherited(child) 
    @abstract = true 
    puts 'inherited' 
    non_abstract_parent = self.superclass; 
    while non_abstract_parent.instance_eval {@abstract} 
     non_abstract_parent = non_abstract_parent.superclass 
    end 
    puts "Non abstract superclass is #{non_abstract_parent}" 
    (class << child;self;end).instance_eval do 
     define_method :new, non_abstract_parent.method('new') 
     # # Or this can be done in this style: 
     # define_method :new do |*args,&block| 
     # non_abstract_parent.method('new').unbind.bind(self).call(*args,&block) 
     # end 
    end 
    end 
end 

class AbstractParent 
    extend Abstract 
    def initialize 
    puts 'parent initializer' 
    end 
end 

class Child < AbstractParent 
    def initialize 
    puts 'child initializer' 
    super 
    end 
end 

# AbstractParent.new 
puts Child.new 

class AbstractChild < AbstractParent 
    extend Abstract 
end 

class Child2 < AbstractChild 

end 
puts Child2.new 
11

मेरे 2 ¢: मैं एक सरल, हल्के डीएसएल mixin के लिए चुनते हैं:

module Abstract 
    extend ActiveSupport::Concern 

    included do 

    # Interface for declaratively indicating that one or more methods are to be 
    # treated as abstract methods, only to be implemented in child classes. 
    # 
    # Arguments: 
    # - methods (Symbol or Array) list of method names to be treated as 
    # abstract base methods 
    # 
    def self.abstract_methods(*methods) 
     methods.each do |method_name| 

     define_method method_name do 
      raise NotImplementedError, 'This is an abstract base method. Implement in your subclass.' 
     end 

     end 
    end 

    end 

end 

# Usage: 
class AbstractBaseWidget 
    include Abstract 
    abstract_methods :widgetify 
end 

class SpecialWidget < AbstractBaseWidget 
end 

SpecialWidget.new.widgetify # <= raises NotImplementedError 

और, ज़ाहिर है, बेस क्लास को शुरू करने के लिए एक और त्रुटि जोड़ना इस मामले में मामूली होगा।

+1

संपादित करें: अच्छी माप के लिए, चूंकि यह दृष्टिकोण define_method का उपयोग करता है, इसलिए कोई यह सुनिश्चित करना चाहता है कि बैकट्रैक बरकरार रहे, उदाहरण के लिए: 'err = NotImplementedError.new (message); err.set_backtrace कॉलर() 'YMMV –

+0

मुझे यह दृष्टिकोण काफी सुरुचिपूर्ण लगता है। इस प्रश्न में आपके योगदान के लिए धन्यवाद। –

4

यह छोटा abstract_type मणि भी है, जो एक अव्यवस्थित तरीके से अमूर्त वर्गों और मॉड्यूल घोषित करने की इजाजत देता है।

उदाहरण (README.md फ़ाइल से):

class Foo 
    include AbstractType 

    # Declare abstract instance method 
    abstract_method :bar 

    # Declare abstract singleton method 
    abstract_singleton_method :baz 
end 

Foo.new # raises NotImplementedError: Foo is an abstract type 
Foo.baz # raises NotImplementedError: Foo.baz is not implemented 

# Subclassing to allow instantiation 
class Baz < Foo; end 

object = Baz.new 
object.bar # raises NotImplementedError: Baz#bar is not implemented 
5

रेल दुनिया में किसी के लिए, एक ActiveRecord मॉडल को लागू के रूप में एक अमूर्त वर्ग मॉडल फ़ाइल में इस घोषणा के साथ किया जाता है:

self.abstract_class = true 
संबंधित मुद्दे