2009-09-04 24 views
68

मैं समझता हूं कि स्थैतिक विधि विरासत सी # में समर्थित नहीं है। मैंने कई चर्चाएं भी पढ़ी हैं (यहां सहित) जिसमें डेवलपर्स इस कार्यक्षमता की आवश्यकता का दावा करते हैं, जिस पर सामान्य प्रतिक्रिया है "यदि आपको स्थिर सदस्य विरासत की आवश्यकता है, तो आपके डिजाइन में कोई दोष है"।स्थैतिक विधि विरासत का सही विकल्प क्या है?

ठीक है, यह देखते हुए कि OOP मुझे और भी स्थिर विरासत के बारे में सोचना नहीं चाहता है, मैं निष्कर्ष निकालना चाहिए कि इसके लिए मेरी स्पष्ट की जरूरत मेरी डिजाइन में एक त्रुटि को इंगित करता है। लेकिन, मैं अटक गया हूँ। मैं वास्तव में इसे हल करने में कुछ मदद की सराहना करता हूं। यहाँ चुनौती ...

मैं एक सार आधार वर्ग का निर्माण करना चाहते है कि कुछ जटिल प्रवर्तन कोड समाहित (यह एक फल कॉल)। यह कोड कन्स्ट्रक्टर में नहीं रखा जा सकता है, क्योंकि इसमें से कुछ आभासी विधि कॉल पर भरोसा करेंगे।

फल अन्य ठोस वर्ग (एप्पल, नारंगी), जिनमें से प्रत्येक एक मानक कारखाने विधि CreateInstance() बना सकते हैं और एक उदाहरण प्रारंभ करने में प्रदर्शित करना है द्वारा विरासत में मिला दिया जाएगा।

यदि स्थैतिक सदस्य विरासत व्यवहार्य था, तो मैं आधार वर्ग में फैक्ट्री विधि रखूंगा और व्युत्पन्न कक्षा में वर्चुअल विधि कॉल का उपयोग उस प्रकार से प्राप्त करने के लिए कर सकता हूं, जिससे एक ठोस उदाहरण प्रारंभ किया जाना चाहिए। ग्राहक कोड सरल रूप से प्रारंभिक ऐप्पल उदाहरण प्राप्त करने के लिए Apple.CreateInstance() को आमंत्रित करेगा।

लेकिन स्पष्ट रूप से यह संभव नहीं है, इसलिए किसी को बताएं कि किस तरह मेरी डिजाइन ही कार्यक्षमता को समायोजित करने के बदलने की जरूरत कृपया कर सकते हैं।

+1

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

उत्तर

52

एक विचार:

public abstract class Fruit<T> 
    where T : Fruit<T>, new() 
{ 
    public static T CreateInstance() 
    { 
     T newFruit = new T(); 
     newFruit.Initialize(); // Calls Apple.Initialize 
     return newFruit; 
    } 

    protected abstract void Initialize(); 
} 

public class Apple : Fruit<Apple> 
{ 
    protected override void Initialize() { ... } 
} 

और इसलिए की तरह फोन:

Apple myAppleVar = Fruit<Apple>.CreateInstance(); 

कोई अतिरिक्त कारखाने की जरूरत कक्षाएं।

+8

आईएमएचओ, सामान्य स्तर को इसे क्लास लेवल पर रखने के बजाय CreateInstance विधि में स्थानांतरित करना बेहतर है। (जैसे मैंने अपने जवाब में किया)। –

+1

आपकी वरीयता नोट किया गया है। आपकी वरीयता का एक संक्षिप्त विवरण अधिक सराहना की जाएगी। –

+6

ऐसा करके, आप बाकी वर्ग को 'प्रदूषित' नहीं करते हैं; प्रकार पैरामीटर केवल विधि बनाने में आवश्यक है (इस उदाहरण में कम से कम)। इसके आगे, मुझे लगता है कि: फल। (); तब अधिक पठनीय है: फल ।सर्जन करना(); –

0

मैं कहना चाहता हूँ तो सबसे अच्छा होगा फल वर्ग जो बुलाया जाना चाहिए पर एक आभासी/सार आरंभ विधि बनाने के लिए और फिर बनाने के लिए एक बाहरी 'फल कारखाना' वर्ग उदाहरण बना है:


public class Fruit 
{ 
    //other members... 
    public abstract void Initialise(); 
} 

public class FruitFactory() 
{ 
    public Fruit CreateInstance() 
    { 
     Fruit f = //decide which fruit to create 
     f.Initialise(); 

     return f; 
    } 
} 
4

क्यों एक निर्माण विधि के साथ एक कारखाने वर्ग (templated) नहीं बनाते?

FruitFactory<Banana>.Create(); 
+0

आपको इसे मारो। ;) –

15

कारखाने की विधि को प्रकार से बाहर ले जाएं, और इसे अपनी फैक्टरी कक्षा में रखें।

public abstract class Fruit 
{ 
    protected Fruit() {} 

    public abstract string Define(); 

} 

public class Apple : Fruit 
{ 
    public Apple() {} 

    public override string Define() 
    { 
     return "Apple"; 
    } 
} 

public class Orange : Fruit 
{ 
    public Orange() {} 

    public override string Define() 
    { 
     return "Orange"; 
    } 
} 

public static class FruitFactory<T> 
{ 
    public static T CreateFruit<T>() where T : Fruit, new() 
    { 
     return new T(); 
    } 
} 

लेकिन, जैसा कि मैं इस पर देख रहा हूँ, वहाँ अपनी ही फैक्टरी वर्ग के लिए विधि बनाएं (हालांकि मुझे लगता है कि यह है concerns- की preferrable -separation) ले जाने के लिए कोई जरूरत नहीं है, आप इसे रख सकते हैं फल कक्षा में:

public abstract class Fruit 
{ 

    public abstract string Define(); 

    public static T CreateFruit<T>() where T : Fruit, new() 
    { 
     return new T(); 
    } 

} 

और, देखने के लिए अगर यह काम करता है:

class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine (Fruit.CreateFruit<Apple>().Define()); 
      Console.WriteLine (Fruit.CreateFruit<Orange>().Define()); 

      Console.ReadLine(); 
     }   
    } 
+1

आपका कोड संकलित नहीं होगा। आपको टी: नया() खंड की आवश्यकता है। हालांकि, यह मेरा एक सही डुप्लिकेट है। –

+1

इसे आज़माएं, यह संकलित करता है। –

+1

हालांकि कोई एक स्थिर फ्रूट फैक्ट्री क्लास बना सकता है, फिर भी मैं एक फर्मफैक्टरी इंटरफेस का समर्थन करता हूं, साथ ही एक तत्काल फलफैक्टरी वर्ग के साथ। आप केवल एक फ्रूट फैक्ट्री क्लास के साथ समाप्त हो सकते हैं, जिसका CreateFruit विधि इसके ऑब्जेक्ट इंस्टेंस का कोई संदर्भ नहीं देता है और इस प्रकार यह एक स्थिर विधि भी हो सकता है, लेकिन अगर फल बनाने के कई तरीकों की पेशकश करना आवश्यक हो, या फल निर्माण कभी भी राज्य को बनाए रखने की क्षमता की आवश्यकता होती है, उदाहरण के तरीकों का उपयोग करके उपयोगी साबित हो सकता है। – supercat

4

मैं इस

012 की तरह कुछ करना होगा
3

WebRequest वर्ग और .NET बीसीएल में अपनी व्युत्पन्न प्रकार कैसे डिजाइन इस तरह की अपेक्षाकृत अच्छी तरह से लागू किया जा सकता का एक अच्छा उदाहरण प्रस्तुत करती हैं।

WebRequest कक्षा में HttpWebRequest और FtpWebReuest समेत कई उप-वर्ग हैं। अब, यह WebRequest आधार वर्ग भी एक कारखाना प्रकार है, और एक स्थिर Create विधि (उदाहरण के लिए रचनाकारों को छुपाया गया है, कारखाने पैटर्न द्वारा आवश्यक) का खुलासा करता है।

public static WebRequest Create(string requestUriString) 
public static WebRequest Create(Uri requestUri) 

यह Create विधि WebRequest वर्ग के एक विशिष्ट कार्यान्वयन देता है, और वस्तु के प्रकार बना सकते हैं और वापस जाने के लिए निर्धारित करने के लिए यूआरआई (या URI स्ट्रिंग) का उपयोग करता है।

यह निम्न उपयोग पैटर्न के अंतिम परिणाम है:

var httpRequest = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com/"); 
// or equivalently 
var httpRequest = (HttpWebRequest)HttpWebWebRequest.Create("http://stackoverflow.com/"); 

var ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://stackoverflow.com/"); 
// or equivalently 
var ftpRequest = (FtpWebRequest)FtpWebWebRequest.Create("ftp://stackoverflow.com/"); 

मैं व्यक्तिगत रूप से लगता है कि इस मुद्दे दृष्टिकोण का एक अच्छा तरीका है, और यह वास्तव में .NET फ्रेमवर्क के preffered तरीका हो प्रतीत होता है रचनाकारों।

3

सबसे पहले, स्थिर प्रारंभकर्ता नहीं होने वाले आभासी हो सकते हैं इसका मतलब यह नहीं है कि आपके पास "मानक" सदस्य विधियां नहीं हो सकती हैं, जिन्हें अधिभारित किया जा सकता है। सबसे दूसरा, आप अपने आभासी तरीकों को कन्स्ट्रक्टर से कॉल कर सकते हैं, और वे उम्मीद के अनुसार काम करेंगे, इसलिए यहां कोई समस्या नहीं है। तीसरा, आप टाइप-सेफ फैक्ट्री रखने के लिए जेनरिक का उपयोग कर सकते हैं।
यहाँ कुछ कोड, कारखाने + सदस्य Initialize() विधि है कि निर्माता द्वारा कहा जाता है का उपयोग करता है है (और यह संरक्षित है, ताकि आप चिंता करने की ज़रूरत नहीं है, कि किसी को इसे फिर से एक वस्तु बनाने के बाद कॉल करेंगे):


abstract class Fruit 
{ 
    public Fruit() 
    { 
     Initialize(); 
    } 

    protected virtual void Initialize() 
    { 
     Console.WriteLine("Fruit.Initialize"); 
    } 
} 

class Apple : Fruit 
{ 
    public Apple() 
     : base() 
    { } 

    protected override void Initialize() 
    { 
     base.Initialize(); 
     Console.WriteLine("Apple.Initialize"); 
    } 

    public override string ToString() 
    { 
     return "Apple"; 
    } 
} 

class Orange : Fruit 
{ 
    public Orange() 
     : base() 
    { } 

    protected override void Initialize() 
    { 
     base.Initialize(); 
     Console.WriteLine("Orange.Initialize"); 
    } 

    public override string ToString() 
    { 
     return "Orange"; 
    } 
} 

class FruitFactory 
{ 
    public static T CreateFruit<T>() where T : Fruit, new() 
    { 
     return new T(); 
    } 
} 

public class Program 
{ 

    static void Main() 
    { 
     Apple apple = FruitFactory.CreateFruit<Apple>(); 
     Console.WriteLine(apple.ToString()); 

     Orange orange = new Orange(); 
     Console.WriteLine(orange.ToString()); 

     Fruit appleFruit = FruitFactory.CreateFruit<Apple>(); 
     Console.WriteLine(appleFruit.ToString()); 
    } 
} 
+1

आम तौर पर कन्स्ट्रक्टर से आभासी तरीकों को कॉल करने से बचने के लिए सबसे अच्छा है। देखें http://blogs.msdn.com/abhinaba/archive/2006/02/28/540357.aspx – TrueWill

+0

यह सच है, यह मुश्किल हो सकता है, हालांकि, यह संभव है और हमेशा काम करेगा जैसा कि करना चाहिए, समस्या है, समझने के लिए क्या मतलब है 'इसे चाहिए'। आम तौर पर, मैं किसी भी जटिल तरीके (यहां तक ​​कि गैर-आभासी, यानी क्योंकि उन्हें अक्सर एक तरह से बनाया जाता है, जिससे उन्हें अपवाद फेंकने की अनुमति मिलती है, और मुझे कुछ भी फेंकने की इच्छा नहीं है) , लेकिन यह डेवलपर का निर्णय है। –

संबंधित मुद्दे