2012-05-17 8 views
5

निम्नलिखित कार्यक्रम को देखते हुए:अनियंत्रित (तृतीय पक्ष) की भिन्न गुणों को सेट करने गतिशील का उपयोग करते हुए सील प्रकार

using System; 
using System.Collections.Generic; 

namespace ConsoleApplication49 
{ 
    using FooSpace; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      IEnumerable<FooBase> foos = FooFactory.CreateFoos(); 

      foreach (var foo in foos) 
      {    
       HandleFoo(foo); 
      } 
     } 

     private static void HandleFoo(FooBase foo) 
     { 
      dynamic fooObject = foo; 
      ApplyFooDefaults(fooObject); 
     } 

     private static void ApplyFooDefaults(Foo1 foo1) 
     { 
      foo1.Name = "Foo 1"; 

      Console.WriteLine(foo1); 
     } 

     private static void ApplyFooDefaults(Foo2 foo2) 
     { 
      foo2.Name  = "Foo 2"; 
      foo2.Description = "SomeDefaultDescription"; 

      Console.WriteLine(foo2); 
     } 

     private static void ApplyFooDefaults(Foo3 foo3) 
     { 
      foo3.Name = "Foo 3"; 
      foo3.MaxSize = Int32.MaxValue; 

      Console.WriteLine(foo3); 
     } 

     private static void ApplyFooDefaults(Foo4 foo4) 
     { 
      foo4.Name  = "Foo 4"; 
      foo4.MaxSize  = 99999999; 
      foo4.EnableCache = true; 

      Console.WriteLine(foo4); 
     } 

     private static void ApplyFooDefaults(FooBase unhandledFoo) 
     { 
      unhandledFoo.Name = "Unhandled Foo"; 
      Console.WriteLine(unhandledFoo); 
     } 
    }  
} 

///////////////////////////////////////////////////////// 
// Assume this namespace comes from a different assembly 
namespace FooSpace 
{ 
    //////////////////////////////////////////////// 
    // these cannot be changed, assume these are 
    // from the .Net framework or some 3rd party 
    // vendor outside of your ability to alter, in 
    // another assembly with the only way to create 
    // the objects is via the FooFactory and you 
    // don't know which foos are going to be created 
    // due to configuration. 

    public static class FooFactory 
    { 
     public static IEnumerable<FooBase> CreateFoos() 
     { 
      List<FooBase> foos = new List<FooBase>(); 
      foos.Add(new Foo1()); 
      foos.Add(new Foo2()); 
      foos.Add(new Foo3()); 
      foos.Add(new Foo4()); 
      foos.Add(new Foo5()); 

      return foos; 
     } 
    } 

    public class FooBase 
    { 
     protected FooBase() { } 

     public string Name { get; set; } 

     public override string ToString() 
     { 
      return String.Format("Type = {0}, Name=\"{1}\"", this.GetType().FullName, this.Name); 
     } 
    } 

    public sealed class Foo1 : FooBase 
    { 
     internal Foo1() { } 
    } 

    public sealed class Foo2 : FooBase 
    { 
     internal Foo2() { } 

     public string Description { get; set; } 

     public override string ToString() 
     { 
      string baseString = base.ToString(); 
      return String.Format("{0}, Description=\"{1}\"", baseString, this.Description); 
     } 
    } 

    public sealed class Foo3 : FooBase 
    { 
     internal Foo3() { } 

     public int MaxSize { get; set; } 

     public override string ToString() 
     { 
      string baseString = base.ToString(); 
      return String.Format("{0}, MaxSize={1}", baseString, this.MaxSize); 
     } 
    } 

    public sealed class Foo4 : FooBase 
    { 
     internal Foo4() { } 

     public int MaxSize { get; set; } 
     public bool EnableCache { get; set; } 

     public override string ToString() 
     { 
      string baseString = base.ToString(); 
      return String.Format("{0}, MaxSize={1}, EnableCache={2}", baseString, 
                     this.MaxSize, 
                     this.EnableCache); 
     } 
    } 

    public sealed class Foo5 : FooBase 
    { 
     internal Foo5() { } 
    } 
    //////////////////////////////////////////////// 
} 

निम्नलिखित में से कौन उत्पादन का उत्पादन:

Type = ConsoleApplication49.Foo1, Name="Foo 1" 
Type = ConsoleApplication49.Foo2, Name="Foo 2", Description="SomeDefaultDescription" 
Type = ConsoleApplication49.Foo3, Name="Foo 3", MaxSize=2147483647 
Type = ConsoleApplication49.Foo4, Name="Foo 4", MaxSize=99999999, EnableCache=True 
Type = ConsoleApplication49.Foo5, Name="Unhandled Foo" 
Press any key to continue . . . 

मैं से बचने के लिए यहां गतिशील उपयोग करने के लिए चुना है निम्नलिखित:

  1. स्विच/अगर/अन्य कथन का उपयोग कर उदाहरण switch(foo.GetType().Name)
  2. स्पष्ट प्रकार की जांच कथन उदा। foo is Foo1
  3. स्पष्ट कास्टिंग स्टेटमेंट उदा। (Foo1)foo
dynamic रूपांतरण की वजह से

, सही ApplyFooDefaults विधि वस्तु HandleFoo(FooBase foo) में पारित कर दिया के प्रकार के आधार लागू हो जाता है। कोई भी ऑब्जेक्ट जिसमें उपयुक्त ApplyFooDefaults हैंडलर विधि नहीं है, "सभी को पकड़ें" विधि, ApplyFooDefaults(FooBase unhandledFoo) में आती है।

यहां एक महत्वपूर्ण हिस्सा यह है कि FooBase और व्युत्पन्न कक्षाएं हमारे नियंत्रण के बाहर के प्रकारों का प्रतिनिधित्व करती हैं और अतिरिक्त इंटरफेस जोड़ने के लिए नहीं ली जा सकती हैं।

क्या यह गतिशील के लिए "अच्छा" उपयोग है या क्या इस समस्या को बाधाओं के अतिरिक्त अतिरिक्त जटिलता जोड़ने के बिना ओओपी तरीके से हल किया जा सकता है और तथ्य यह है कि यह इन वस्तुओं पर डिफ़ॉल्ट संपत्ति मान निर्धारित करने के लिए है?

* UPDATED *

बॉब हॉर्न के जवाब के बाद, मुझे एहसास हुआ कि मेरी परिदृश्य पूरा नहीं हुआ था। अतिरिक्त बाधाएं:

  1. आप सीधे फूज़ नहीं बना सकते हैं, आपको FooFactory का उपयोग करना होगा।
  2. आप Foo प्रकार नहीं मान सकते हैं क्योंकि Foo प्रकार कॉन्फ़िगरेशन में निर्दिष्ट है और प्रतिबिंबित रूप से बनाया गया है।

+1

@ जोन स्कीट - मैंने सुना है कि आप सैंडुस्की, ओएच में कोड मैश 2012 में बात करते हैं। अधिकांश भाग के लिए ऐसा लगता है कि आप किसी भी स्थिति में गतिशील प्रकार के उपयोग की परवाह नहीं करते थे। मुझे इस सवाल पर आपके विचार मिलना अच्छा लगेगा। –

+1

मुझे लगता है कि आपका कोड बहुत है, * बहुत * अच्छा है। – Alex

+1

मुझे लगता है कि यदि आप गतिशील उपयोग करने पर सेट हैं तो आप जेनेरिक का उपयोग करने पर विचार करना चाहेंगे। इसी तरह: http://stackoverflow.com/q/10132760/1026459। नोट: जॉन स्कीट इस लिंक में जवाब प्रदान करता है। –

उत्तर

1

ठीक है, अलग-अलग वस्तु प्रारंभ प्रकार के निर्माताओं में होना चाहिए। यदि फैक्ट्री ऐसा करने में विफल रहता है और किसी ऑब्जेक्ट को मूल प्रकार के साथ आउटपुट करता है तो स्पष्ट रूप से यह प्रकार के आधार पर ऑब्जेक्ट्स को प्रारंभ करने के लिए ओओपी पैटर्न से परे है।

अलास, रनटाइम प्रकार का पता लगाने का तरीका है और गतिशील बस ऐसा करता है, इसलिए हाँ, आपका समाधान बहुत सुंदर है।(लेकिन तीसरा पक्ष lib नहीं है, क्योंकि यह आपको गतिशील प्रकारों का उपयोग करने के लिए मजबूर करता है)

+0

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

+0

@ जिम सहमत हैं। असल में यह ऑब्जेक्ट प्रारंभ करने के लिए फैक्ट्री की ज़िम्मेदारी है और कॉलिंग कोड को बनाए गए ऑब्जेक्ट्स के प्रकार के बारे में चिंता नहीं करना चाहिए। यदि कारखाना ऐसा करने में विफल रहता है तो आप पैटर्न को तोड़ने और रन-टाइम प्रकार की जांच का उपयोग करने के लिए * मजबूर * होते हैं। तो मैं क्या कहने की कोशिश कर रहा था कि इस के आसपास कोई अच्छा रास्ता नहीं है क्योंकि तीसरा पक्ष कारखाना निष्पक्ष नहीं खेलता है। –

+0

मैं किसी भी अन्य ओओपी दृष्टिकोण के साथ चिंतन करने वाले किसी भी अन्य की अनुपस्थिति में आपका उत्तर स्वीकार करने जा रहा हूं। – Jim

0

ऐसा करने और गतिशील से बचने के लिए ApplyFooDefaults() विस्तार के तरीकों बनाने के लिए किया जाएगा एक संभव तरीका:

public static class FooExtensions 
{ 
    public static void ApplyFooDefaults(this Foo1 foo1) 
    { 
     foo1.Name = "Foo 1"; 

     Console.WriteLine(foo1); 
    } 

    public static void ApplyFooDefaults(this Foo2 foo2) 
    { 
     foo2.Name = "Foo 2"; 
     foo2.Description = "SomeDefaultDescription"; 

     Console.WriteLine(foo2); 
    } 

    public static void ApplyFooDefaults(this Foo3 foo3) 
    { 
     foo3.Name = "Foo 3"; 
     foo3.MaxSize = Int32.MaxValue; 

     Console.WriteLine(foo3); 
    } 

    public static void ApplyFooDefaults(this Foo4 foo4) 
    { 
     foo4.Name = "Foo 4"; 
     foo4.MaxSize = 99999999; 
     foo4.EnableCache = true; 

     Console.WriteLine(foo4); 
    } 

    public static void ApplyFooDefaults(this FooBase unhandledFoo) 
    { 
     unhandledFoo.Name = "Unhandled Foo"; 
     Console.WriteLine(unhandledFoo); 
    } 
} 

अपने कार्यक्रम में कुछ बिंदु पर, आप प्रत्येक foo बनाना होगा। जब आप ऐसा करेंगे, विस्तार के तरीकों तो फोन:

static void Main(string[] args) 
{ 
    List<FooBase> foos = new List<FooBase>(); 

    Foo1 foo1 = new Foo1(); 
    foo1.ApplyFooDefaults(); 
    foos.Add(foo1); 

    Foo2 foo2 = new Foo2(); 
    foo2.ApplyFooDefaults(); 
    foos.Add(foo2); 

    Foo3 foo3 = new Foo3(); 
    foo3.ApplyFooDefaults(); 
    foos.Add(foo3); 

    Foo4 foo4 = new Foo4(); 
    foo4.ApplyFooDefaults(); 
    foos.Add(foo4); 

    Foo5 foo5 = new Foo5(); 
    foo5.ApplyFooDefaults(); 
    foos.Add(foo5); 

    Console.WriteLine("Press any key to end."); 
    Console.ReadKey(); 
} 

enter image description here

+0

मैं समस्या का एक पहलू भूल गया, फूज़ हमारे नियंत्रण (कॉन्फ़िगरेशन से) से प्रतिबिंब के माध्यम से भी बनाए जाते हैं। मैं इसे प्रतिबिंबित करने के लिए उदाहरण अपडेट करूंगा। – Jim

+0

इसके अलावा, आप नहीं जानते कि कौन से फ़ूज़ बनाए गए हैं क्योंकि प्रकार कॉन्फ़िगरेशन में निर्दिष्ट है, hypothetically। – Jim

+0

एलओएल। आप केवल तब तक प्रतिबंध जोड़ना जारी रखेंगे जब तक कि केवल एक ही विकल्प शेष गतिशील न हो, है ना? :) –

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