2012-06-28 12 views
31

मैं स्कैला में एक उचित कार्यात्मक कॉन्फ़िगर करने योग्य ऑब्जेक्ट कैसे बना सकता हूं? मैंने टोनी मॉरिस के वीडियो को Reader मोनैड पर देखा है और मैं अभी भी बिंदुओं को जोड़ने में असमर्थ हूं।स्कैला में कॉन्फ़िगरेशन डेटा - क्या मुझे रीडर मोनड का उपयोग करना चाहिए?

class Client(name : String, age : Int){ /* etc */} 

object Client{ 
    //Horrible! 
    val clients = List(Client("Bob", 20), Client("Cindy", 30)) 
} 

मैं चाहता हूँ Client.clients रनटाइम पर निर्धारित किया जा करने के लिए एक गुण से इसे पढ़ने के लिए फ़ाइल या किसी डेटाबेस से या तो के लचीलेपन के साथ,:

मैं Client वस्तुओं की एक हार्ड-कोडेड सूची है। जावा दुनिया में मैं एक इंटरफेस को परिभाषित करना चाहते हैं, स्रोत के दो प्रकार लागू है, और एक वर्ग चर आवंटित करने के लिए डि का उपयोग करें:

trait ConfigSource { 
    def clients : List[Client] 
} 

object ConfigFileSource extends ConfigSource { 
    override def clients = buildClientsFromProperties(Properties("clients.properties")) 
    //...etc, read properties files 
} 

object DatabaseSource extends ConfigSource { /* etc */ } 

object Client { 
    @Resource("configuration_source") 
    private var config : ConfigSource = _ //Inject it at runtime 

    val clients = config.clients 
} 

यह मेरे लिए एक बहुत साफ समाधान की तरह लगता है (नहीं कोड का एक बहुत, स्पष्ट मंशा), लेकिन यह var बाहर कूद करता है (OTOH, यह मेरे लिए वास्तव में परेशानी नहीं लगता है के बाद से मैं यह पता बार और केवल एक बार इंजेक्शन किया जाएगा)।

Reader मोनैड इस स्थिति में कैसा दिखता है और मुझे बताएं कि मैं 5 वर्ष का हूं, इसके फायदे क्या हैं?

+1

'val's * * प्रतिबिंब का उपयोग कर संशोधित किया जा सकता है, इसलिए यह है कि आपके निर्भरता इंजेक्शन पुस्तकालय – gerferra

+2

क्या वैल की बात प्रतिबिंब द्वारा संशोधित किया गया है @gerferra" एक वैल इंजेक्षन "कर सकता है, अगर हम वर है संभव है? –

+0

क्यों 'ग्राहक' को तर्क के साथ कक्षा नहीं बनाते हैं, इसलिए कॉन्फ़िगरेशन को 'क्लाइंट' के उदाहरणों में पारित किया जा सकता है? –

उत्तर

45

चलो अपने दृष्टिकोण और Reader दृष्टिकोण के बीच एक सरल, सतही अंतर के साथ शुरू करते हैं, जो कि आपको अब कहीं भी config पर लटकने की आवश्यकता नहीं है।

type Configured[A] = ConfigSource => A 

अब, अगर मैं कभी भी कुछ काम के लिए एक ConfigSource की जरूरत है, का कहना है कि एक समारोह है कि सूची में वें ग्राहक n 'हो जाता है, मुझे लगता है कि घोषणा कर सकते हैं: चलो आप निम्नलिखित थोड़ा चतुर प्रकार पर्याय परिभाषित मान लीजिए समारोह के रूप में "के लिए कॉन्फ़िगर":

def nthClient(n: Int): Configured[Client] = { 
    config => config.clients(n) 
} 

तो हम अनिवार्य रूप से हवा के बाहर एक config खींच रहे, किसी भी समय हम एक की जरूरत है! निर्भरता इंजेक्शन की तरह बदबू आ रही है, है ना? अब मान लीजिए कि हम सूची में प्रथम, द्वितीय और तृतीय ग्राहकों की उम्र चाहते हैं (यह मानते हुए वे मौजूद):

def ages: Configured[(Int, Int, Int)] = 
    for { 
    a0 <- nthClient(0) 
    a1 <- nthClient(1) 
    a2 <- nthClient(2) 
    } yield (a0.age, a1.age, a2.age) 

इस के लिए, निश्चित रूप से, आप map और flatMap के कुछ उचित परिभाषा की जरूरत है। मैं यहां उसमें नहीं पहुंचूंगा, लेकिन बस यह कहूंगा कि स्कालज़ (या Rúnar's awesome NEScala talk, या Tony's जो आपने पहले ही देखा है) आपको वह सब कुछ देता है जो आपको चाहिए।

यहां महत्वपूर्ण बात यह है कि ConfigSource निर्भरता और इसके तथाकथित इंजेक्शन अधिकतर छिपे हुए हैं। केवल "संकेत" जिसे हम यहां देख सकते हैं वह है कि ages(Int, Int, Int) की बजाय Configured[(Int, Int, Int)] प्रकार का है। हमें कहीं भी config स्पष्ट रूप से संदर्भित करने की आवश्यकता नहीं थी।

एक अलग रूप में के रूप में, इस तरह से मैं लगभग हमेशा monads के बारे में सोचना चाहते है: वे अपने प्रभाव तो यह आपके कोड के प्रवाह को प्रदूषित नहीं कर रहा है छिपाने , जबकि स्पष्ट प्रभाव की घोषणा प्रकार हस्ताक्षर में।दूसरे शब्दों में, आपको अपने आप को दोहराने की आवश्यकता नहीं है: आप कहते हैं, "हे, यह फ़ंक्शन प्रभाव X" फ़ंक्शन के रिटर्न प्रकार में है, और इसके साथ गड़बड़ न करें।

इस उदाहरण में, निश्चित रूप से प्रभाव कुछ निश्चित वातावरण से पढ़ना है। एक और मोनैडिक प्रभाव जिसमें आप परिचित हो सकते हैं, त्रुटि-हैंडलिंग शामिल है: हम कह सकते हैं कि Option आपकी विधि के प्रकार में स्पष्ट त्रुटियों की संभावना बनाते समय त्रुटि-हैंडलिंग तर्क छुपाता है। या, पढ़ने के विपरीत के विपरीत, Writer मोनैड उस चीज़ को छुपाता है जिसे हम लिख रहे हैं, जबकि इसकी उपस्थिति प्रकार प्रणाली में स्पष्ट है।

अब अंत में, बस के रूप में हम आम तौर पर (जैसे कि एक एक्सएमएल फ़ाइल के रूप में कहीं नियंत्रण की हमारी सामान्य प्रवाह के बाहर,) एक डि ढांचे bootstrap करने की जरूरत है, हम भी इस उत्सुक इकाई bootstrap की जरूरत है।

def run: Configured[Unit] = // ... 

यह बहुत सरल किया जा रहा समाप्त होता है: निश्चित रूप से हम इस तरह के रूप में, हमारे कोड के लिए कुछ तार्किक प्रवेश बिंदु होगा के बाद से Configured[A] समारोह ConfigSource => A के लिए सिर्फ एक प्रकार पर्याय है, हम बस करने के लिए समारोह लागू कर सकते हैं अपने "पर्यावरण":

run(ConfigFileSource) 
// or 
run(DatabaseSource) 

ता-दा! इसलिए, पारंपरिक जावा-शैली डी दृष्टिकोण के विपरीत, हमारे पास कोई "जादू" नहीं है। एकमात्र जादू, जैसा कि था, हमारे Configured प्रकार और जिस तरह से यह एक मोनड के रूप में व्यवहार करता है, की परिभाषा में encapsulated है। सबसे महत्वपूर्ण बात यह है कि टाइप सिस्टम हमें ईमानदार रखता है जिसके बारे में "वास्तविक" निर्भरता इंजेक्शन होता है: टाइप Configured[...] के साथ कुछ भी DI दुनिया में है, और इसके बिना कुछ भी नहीं है। हम इसे पुराने स्कूल डीआई में नहीं पाते हैं, जहां सब कुछ संभावित रूप से जादू द्वारा प्रबंधित किया जाता है, इसलिए आप वास्तव में नहीं जानते कि आपके कोड के कौन से भाग डीआई फ्रेमवर्क के बाहर पुन: उपयोग करने के लिए सुरक्षित हैं (उदाहरण के लिए, भीतर आपके यूनिट परीक्षण, या पूरी तरह से किसी अन्य परियोजना में)।


अद्यतन: मैं एक blog post जो अधिक विस्तार में Reader बताते ऊपर लिखा था।

+0

बस मेरे लिए हुआ, मुझे यह भी कहना चाहिए: ** "कॉन्फ़िगर करने योग्य ऑब्जेक्ट" बनाने के बारे में चिंता न करें। ** एक कॉन्फ़िगर करने योग्य ऑब्जेक्ट, वास्तव में, केवल कन्स्ट्रक्टर पैरामीटर के साथ कुछ है। वे पैरामीटर कहां से आते हैं? कन्स्ट्रक्टर का कॉलर, ज़ाहिर है, जो बदले में (यदि मैंने आपको इसे आज़माने के लिए आश्वस्त किया है) उन्हें पाठक से प्राप्त करें (इस मामले में, 'कॉन्फ़िगर किया गया [...] 'पर्यावरण)। यह आपके कार्यों की हिम्मत के बारे में नहीं, अन्य कार्यों को बुलाए कार्यों के बारे में है। – mergeconflict

+0

हम्म ...तो आखिरकार हमें अभी भी हमारे सभी एफएन हस्ताक्षरों को दोबारा बदलना होगा ताकि वे 'संरक्षित [PriorReturnedType]' को एफएन तक वापस कर सकें जहां हम 'रन (कॉन्फिगरफाइलसोर्स)' या 'रन (डाटाबेससोर्स)' चुनते हैं? 'कॉन्फसोर्स' को एक तर्क के रूप में पार करने के लिए बेहतर क्यों है? और मैं इस बात का पालन नहीं करता कि "हमारे पास कोई" जादू नहीं है "। हमें अभी भी कमांड लाइन तर्क या पर्यावरण चर या डी के "जादू" द्वारा 'रन (कॉन्फ़िगरफाइलसोर्स)' या 'रन (डाटाबेससोर्स)' चुनना है, है ना? –

+1

पुन: "तो आखिरकार हमें अभी भी हमारे सभी एफएन हस्ताक्षरों को दोबारा बदलना होगा ..." - उनमें से कुछ, सभी नहीं। ग्लोबल कॉन्फ़िगरेशन पर निर्भर करता है कि कुछ भी 'कॉन्फ़िगर [...] 'हस्ताक्षर की आवश्यकता होगी, लेकिन आपके शुद्ध कार्य नहीं करते हैं। दोबारा, उन दोनों दुनिया को स्पष्ट रूप से पहचानने योग्य रखना एक अच्छी बात है। – mergeconflict