2015-09-05 5 views
8

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

type User = { UserID: UserID 
       Situations: SituationID list } 

type Situation = { SituationID: SituationID } 

और मुझे क्या करना चाहते हैं परिभाषित करने और इस तरह के रूप में कार्य करता फोन करने में सक्षम हो गया है:

do saveUser() 
let user = loadUser (UserID 57) 

वहाँ है इस परिभाषित करने के लिए किसी भी तरह से

मैं जैसे कुछ मॉडल, है स्पष्ट रूप से कार्यात्मक मुहावरे में, अधिमानतः म्यूटेबल राज्य से परहेज करते हुए (जो वैसे भी आवश्यक नहीं होना चाहिए)?

एक तरह से यह कुछ इस तरह लग सकता है ऐसा करने के लिए:

type IStorage = { 
    saveUser: User->unit; 
    loadUser: UserID->User } 

module Storage = 
    // initialize save/load functions to "not yet implemented" 
    let mutable storage = { 
     saveUser = failwith "nyi"; 
     loadUser = failwith "nyi" } 

// ....elsewhere: 
do Storage.storage = { a real implementation of IStorage } 
do Storage.storage.saveUser() 
let user = Storage.storage.loadUser (UserID 57) 

और वहाँ इस में बदलाव कर रहे हैं, लेकिन सभी लोगों को मैं के बारे में सोच सकते हैं अप्रारंभीकृत राज्य के कुछ प्रकार शामिल है। (Xamarin में, वहाँ भी DependencyService है, लेकिन वह अपने आप में एक निर्भरता मैं से बचने के लिए चाहते हैं।)

वहाँ किसी भी तरह से कोड एक भंडारण समारोह है, जो अभी तक लागू नहीं किया गया कॉल लिखते हैं, और उसके बाद लागू करने के लिए है यह, परिवर्तनीय राज्य का उपयोग किए बिना?

(नोट: इस सवाल का भंडारण खुद के बारे में नहीं है - कि सिर्फ उदाहरण मैं उपयोग कर रहा हूँ है यह कैसे अनावश्यक परिवर्तनशील राज्य का उपयोग किए बिना कार्यों सुई बारे में है।।)

उत्तर

15

अन्य जवाब यहां शायद आप को शिक्षित करेगी एफ # में आईओ मोनैड को कार्यान्वित करने के तरीके पर, जो निश्चित रूप से एक विकल्प है। एफ # में, हालांकि, मैं अक्सर अन्य कार्यों के साथ फ़ंक्शन लिखता हूं। ऐसा करने के लिए आपको 'इंटरफ़ेस' या किसी विशेष प्रकार को परिभाषित करने की आवश्यकता नहीं है।

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

डेटा स्टोर से पूछताछ की आवश्यकता है? loadUser तर्क में पास करें। उपयोगकर्ता को बचाने की जरूरत है? एक saveUser तर्क पास करें:

let myHighLevelFunction loadUser saveUser (userId) = 
    let user = loadUser (UserId userId) 
    match user with 
    | Some u -> 
     let u' = doSomethingInterestingWith u 
     saveUser u' 
    | None ->() 

loadUser तर्क प्रकार User -> User option का होना मान लिया जाता है, और saveUserUser -> unit के रूप में, क्योंकि doSomethingInterestingWith प्रकार User -> User के एक समारोह है।

अब आप निम्न स्तर की लाइब्रेरी में कॉल करने वाले कार्यों को लिखकर loadUser और saveUser लागू कर सकते हैं।

इस दृष्टिकोण पर मुझे प्राप्त होने वाली सामान्य प्रतिक्रिया है: मुझे मेरे फ़ंक्शन में बहुत से तर्कों को पारित करने की आवश्यकता होगी!

वास्तव में, यदि ऐसा होता है, तो विचार करें कि यह गंध नहीं है कि फ़ंक्शन बहुत अधिक करने का प्रयास कर रहा है।

चूंकि इस प्रश्न के शीर्षक में Dependency Inversion Principle का उल्लेख किया गया है, इसलिए मैं यह इंगित करना चाहता हूं कि SOLID principles उन सभी को सबसे अच्छा काम करता है यदि उनमें से सभी संगीत कार्यक्रम में लागू होते हैं। Interface Segregation Principle कहता है कि इंटरफेस जितना संभव हो उतना छोटा होना चाहिए, और जब आप प्रत्येक 'इंटरफ़ेस' एक ही फ़ंक्शन होता है तो आप उन्हें छोटे से नहीं प्राप्त करते हैं।

इस तकनीक का वर्णन करने वाले एक और विस्तृत लेख के लिए, आप मेरे Type-Driven Development article पढ़ सकते हैं।

1

आप इंटरफ़ेस आईएसटीओरेज के पीछे सार भंडारण कर सकते हैं। मुझे लगता है कि आपका इरादा था।

type IStorage = 
    abstract member LoadUser : UserID -> User 
    abstract member SaveUser : User -> unit 

module Storage = 
    let noStorage = 
     { new IStorage with 
      member x.LoadUser _ -> failwith "not implemented" 
      member x.SaveUser _ -> failwith "not implemented" 
     } 

आपके प्रोग्राम के किसी अन्य भाग में आपके पास एकाधिक संग्रहण कार्यान्वयन हो सकते हैं।

type MyStorage() = 
    interface IStorage with 
     member x.LoadUser uid -> ... 
     member x.SaveUser u -> ... 

और आपके सभी प्रकार के परिभाषित होने के बाद आप तय कर सकते हैं कि किस का उपयोग करना है।

let storageSystem = 
    if today.IsShinyDay 
    then MyStorage() :> IStorage 
    else Storage.noStorage 

let user = storageSystem.LoadUser userID 
संबंधित मुद्दे