2011-12-30 10 views
6

मैं Java, JavaScript और Scala में ओओ 'धाराप्रवाह इंटरफ़ेस' दृष्टिकोण के बारे में पढ़ रहा हूं और मुझे इसकी पसंद पसंद है, लेकिन यह देखने के लिए संघर्ष कर रहा है कि स्कैला में एक और प्रकार-आधारित/कार्यात्मक दृष्टिकोण के साथ इसे कैसे सुलझाना है ।स्कैला में एक कार्यात्मक शैली के साथ मैं धाराप्रवाह इंटरफेस को कैसे जोड़ सकता हूं?

मैं क्या मतलब है की एक बहुत विशिष्ट उदाहरण के लिए: के रूप में परिभाषित,

val response = MyTargetApi.get("orders", 24) 

get() से वापसी मान एक Tuple3 प्रकार RestfulResponse कहा जाता है: मैं एक API ग्राहक जो इस तरह लागू किया जा सकता है लिखा है में मेरी package object:

// 1. Return code 
// 2. Response headers 
// 2. Response body (Option) 
type RestfulResponse = (Int, List[String], Option[String]) 

यह ठीक काम करता है - और मैं नहीं है वास्तव में एक टपल वापसी मान के कार्यात्मक सादगी बलिदान करने के लिए चाहते हैं - लेकिन मैं विभिन्न 'च के साथ पुस्तकालय विस्तार करना चाहते हैं luent 'विधि कॉल, शायद कुछ इस तरह:

val response = MyTargetApi.get("customers", 55).throwIfError() 
// Or perhaps: 
MyTargetApi.get("orders", 24).debugPrint(verbose=true) 

मैं और अधिक जोड़ने की क्षमता के साथ एक टाइप टपल (या समान) लौटने get() के कार्यात्मक सादगी कैसे गठजोड़ कर सकते हैं' मेरे एपीआई को धाराप्रवाह 'क्षमताओं?

+0

यह "धाराप्रवाह इंटरफ़ेस" दृष्टिकोण ... स्थिर रूप से टाइप की गई भाषाओं के प्रति बहुत अनुकूल नहीं दिखता है। हालांकि मुझे यकीन है कि कुछ रैपिंग कोड और विरासत के साथ आप शायद इसे स्कैला के साथ कर सकते हैं, यह 'getOrders' और 'getCustomers 'को अलग-अलग प्राप्त करने के बजाय' get (" ऑर्डर ") के बजाय अलग-अलग प्रकार के रूप में सुरक्षित नहीं होगा। 'वही' प्राप्त विधि का उपयोग करके '(" ग्राहक ") प्राप्त करें। –

+0

धन्यवाद दान - लेकिन मैं 'get ("slug", id)' वाक्यविन्यास के बारे में बहुत ज्यादा चिंता नहीं करता - यह वास्तव में मेरा प्रश्न नहीं है। किसी भी मामले में लाइब्रेरी में एक और अधिक टाइपएफ़ मोड है जो 'MyTargetApi.orders.get (id)' –

+0

जैसा दिखता है व्यक्तिगत रूप से मुझे लगता है कि आपको कुछ धाराप्रवाह कोड का अधिक प्रतिनिधि उदाहरण प्रदान करना चाहिए और आप जो कुछ भी सोचते हैं वह कार्यात्मक नहीं है। फिलहाल, यह सिर्फ आपके प्रश्न से प्रकट होता है कि आप वास्तव में नहीं जानते कि किस धाराप्रवाह का अर्थ है –

उत्तर

7

ऐसा लगता है कि आप एक आराम शैली संचार के क्लाइंट साइड एपीआई से निपट रहे हैं। आपकी get विधि वास्तविक अनुरोध/प्रतिक्रिया चक्र को ट्रिगर करती है। ऐसा लगता है कि आप इस से निपटने के लिए होगा की तरह: इनपुट के लिए परिवहन का

  • गुण (साख की तरह, डिबग स्तर, त्रुटि हैंडलिंग)
  • उपलब्ध कराने के डेटा (अपने आईडी की और प्रकार रिकॉर्ड (आदेश या ग्राहक)
  • परिणाम

मैं परिवहन के गुणों के लिए लगता है, आप MyTargetApi obj के निर्माता में इसके बारे में कुछ रख सकते हैं के साथ कुछ कर रही ect, लेकिन आप भी एक क्वेरी उद्देश्य यह है कि किसी एक क्वेरी के लिए उन संग्रहीत करेगा और एक query() विधि का उपयोग कर एक धाराप्रवाह तरह से सेट किया जा सकता बना सकते हैं:

MyTargetApi.query().debugPrint(verbose=true).throwIfError() 

यह कुछ स्टेटफुल Query वस्तु वापसी होगी कि लॉग स्तर, त्रुटि हैंडलिंग के लिए मान स्टोर करता है।इनपुट के लिए डेटा उपलब्ध कराने के लिए, आप भी क्वेरी ऑब्जेक्ट का उपयोग उन मूल्यों को स्थापित करने के लिए कर सकते हैं, लेकिन इसके बजाय लौटने की अपनी प्रतिक्रिया वापस एक QueryResult:

class Query { 
    def debugPrint(verbose: Boolean): this.type = { _verbose = verbose; this } 
    def throwIfError(): this.type = { ... } 
    def get(tpe: String, id: Int): QueryResult[RestfulResponse] = 
    new QueryResult[RestfulResponse] { 
     def run(): RestfulResponse = // code to make rest call goes here 
    } 
} 

trait QueryResult[A] { self => 
    def map[B](f: (A) => B): QueryResult[B] = new QueryResult[B] { 
    def run(): B = f(self.run()) 
    } 
    def flatMap[B](f: (A) => QueryResult[B]) = new QueryResult[B] { 
    def run(): B = f(self.run()).run() 
    } 
    def run(): A 
} 

फिर अंत में परिणाम आप run फोन मिलता है। तो दिन के अंत में आप इसे इस तरह कॉल कर सकते हैं:

MyTargetApi.query() 
    .debugPrint(verbose=true) 
    .throwIfError() 
    .get("customers", 22) 
    .map(resp => resp._3.map(_.length)) // body 
    .run() 

कौन सा एक वर्बोज़ अनुरोध है कि इस मुद्दे पर बाहर त्रुटि होगा, आईडी 22 के साथ ग्राहकों को पुनः प्राप्त करने, शरीर रखने के लिए और एक के रूप में इसकी लंबाई प्राप्त होना चाहिए Option[Int]

विचार यह है कि आप परिणामस्वरूप गणनाओं को परिभाषित करने के लिए map का उपयोग कर सकते हैं जो आपके पास अभी तक नहीं है। यदि हम flatMap इसमें जोड़ते हैं, तो आप दो अलग-अलग प्रश्नों से दो गणनाओं को भी जोड़ सकते हैं।

+0

वाह - विशाल धन्यवाद ** huynhjl ** मेरे प्रश्न के खराब वाक्यांश के माध्यम से wading और इस जवाब को एक साथ रखने के लिए। यह बेहद सहायक है - यह दिखाता है कि एक धाराप्रवाह इंटरफ़ेस को परिभाषित करने के लिए जो मेरे 'RestfulResponse' प्रकार या 'map' के माध्यम से वापस कर सकता है, आगे गणना कर सकता है और उसे वापस कर सकता है। क्या मैं पुष्टि कर सकता हूं कि मूल 'क्वेरी()' विधि जिसका आप उल्लेख करते हैं वह 'MyTargetApi' का हिस्सा है और बस' नई क्वेरी() 'ऑब्जेक्ट देता है? 'FlatMap' परिभाषा को देखने के लिए भी बहुत कुछ होगा? एक बार फिर धन्यवाद! –

+1

@AlexDean, मैंने 'flatMap' जोड़ा। हां 'क्वेरी()' आपके मूल 'MyTargetApi' का हिस्सा होगा और एक नई' क्वेरी 'ऑब्जेक्ट वापस कर देगा। कृपया, केवल कुछ विचारों के लिए मेरे उत्तर का उपयोग करें। मैं आपको http://engineering.foursquare.com/2011/01/21/rogue-a-type-safe-scala-dsl-for-querying-mongodb/ और सामान्य रूप से किसी भी ओआरएम और नोस्कल इंटरफ़ेस को देखने के लिए आमंत्रित करता हूं या अधिक प्रेरणा के लिए स्कैला के लिए लिखा रैपर। – huynhjl

+0

बहुत धन्यवाद ** huynhjl **। मैं प्रेरणा के लिए [स्क्वायरल स्रोत] (https://github.com/max-l/Squeryl/tree/master/src/main/scala/org/squeryl) का उपयोग कर रहा हूं, लेकिन मैं निश्चित रूप से दुष्ट की जांच करूँगा और कुछ अन्य ORMs/NoSQL उपकरण भी ... –

3

ईमानदार होने के लिए, मुझे लगता है कि ऐसा लगता है कि आपको थोड़ा और अधिक तरीके से महसूस करने की आवश्यकता है क्योंकि उदाहरण स्पष्ट रूप से कार्यात्मक नहीं है, न ही विशेष रूप से धाराप्रवाह है। ऐसा लगता है कि आप गैर-idempotent के साथ प्रवाह को मिश्रित कर सकते हैं, इस अर्थ में कि आपकी debugPrint विधि संभवतः प्रदर्शन कर रही है I/O और throwIfError अपवाद फेंक रहा है। क्या यही मतलब है तुम्हारा?

यदि आप स्टेटफुलर बिल्डर कार्यात्मक हैं, तो इसका जवाब है कि उत्तर "शुद्ध अर्थ में नहीं है"। हालांकि, ध्यान दें कि एक निर्माता को राज्यिक होना जरूरी नहीं है।

case class Person(name: String, age: Int) 

सबसे पहले;

Person(name="Oxbow", age=36) 

या, एक राज्यविहीन बिल्डर:

object Person { 
    def withName(name: String) 
    = new { def andAge(age: Int) = new Person(name, age) } 
} 

अरे Presto: इस नाम वाले पैरामीटर का उपयोग कर बनाया जा सकता है

scala> Person withName "Oxbow" andAge 36 

untyped तार के आपके उपयोग क्वेरी परिभाषित करने के लिए के रूप में आप बना रहे है; यह एक स्थाई रूप से टाइप की गई भाषा में खराब रूप है।

sealed trait Query 
case object orders extends Query 

def get(query: Query): Result 

अरे Presto:: क्या अधिक है, वहाँ कोई जरूरत नहीं है

api get orders 

हालांकि, मुझे लगता है कि यह एक बुरा विचार है - आप एक ही तरीका है जिसके तुम वापस कल्पना की दृष्टि से दे सकते हैं नहीं होना चाहिए परिणाम


की पूरी तरह से अलग अलग प्रकार के निष्कर्ष करने के लिए: मैं व्यक्तिगत रूप से लगता है कि वहाँ कि प्रवाह और कार्यात्मक जो भी कोई कारण नहीं मिश्रण कर सकते हैं, के बाद से कार्यात्मक सिर्फ अस्थायी राज्य एक की कमी का संकेत करता है nd idempotent कार्यों के लिए मजबूत वरीयता में अपने तर्क प्रदर्शन करने के लिए

यहाँ आप के लिए एक है:।

args.map(_.toInt) 

args map toInt 

मैं तर्क था कि दूसरा अधिक धाराप्रवाह है। यदि आप परिभाषित करते हैं तो यह संभव है:

val toInt = (_ : String).toInt 

यही है; यदि आप एक समारोह को परिभाषित करते हैं। मुझे स्कैला में फ़ंक्शन और फ्लेंसी मिश्रण बहुत अच्छी तरह मिलते हैं।

+0

हाय ** oxbow_lakes ** - इस उत्तर को एक साथ रखने के लिए समय निकालने के लिए बहुत सारे धन्यवाद। आप सही थे - मेरे प्रश्न का वाक्यांश बहुत खराब था, सभी भ्रम के लिए क्षमा चाहते थे। मैं मानता हूं कि कार्यात्मक और धाराप्रवाह पूरी तरह से संगत हैं - और राज्यव्यापी और स्टेटलेस बिल्डरों के उदाहरणों के लिए धन्यवाद। HTTP संसाधनों को प्राप्त करने के लिए अनियमित तारों पर - मैं सहमत हूं, यह एक बुरा विचार था, मैं एपीआई से उस क्षमता को हटाने जा रहा हूं (या कम से कम इसे 'असुरक्षित', हास्केल-शैली चिह्नित करें)। –

0

आप होने get() एक आवरण उद्देश्य यह है कि इस

type RestfulResponse = (Int, List[String], Option[String]) 

class ResponseWrapper(private rr: RestfulResponse /* and maybe some flags as additional arguments, or something? */) { 

    def get : RestfulResponse = rr 

    def throwIfError : RestfulResponse = { 
     // Throw your exception if you detect an error 
     rr // And return the response if you didn't detect an error 
    } 

    def debugPrint(verbose: Boolean, /* whatever other parameters you had in mind */) { 
     // All of your debugging printing logic 
    } 

    // Any and all other methods that you want this API response to be able to execute 

} 

मूल रूप से की तरह कुछ लग सकता है लौटने की कोशिश कर सकते, यह आप अपनी प्रतिक्रिया डाल करने में एक शामिल है कि इन अच्छा तरीकों कि आप चाहते हैं के सभी नहीं है की अनुमति देता है , और, यदि आप बस लपेटा प्रतिक्रिया प्राप्त करना चाहते हैं, तो आप केवल रैपर की get() विधि को कॉल कर सकते हैं।

बेशक

, इस का नकारात्मक पहलू है कि आप, अगर है कि सब पर आप के लिए चिंताजनक है अपने एपीआई थोड़ा परिवर्तन करने की आवश्यकता नहीं है। खैर ... आप शायद अपने एपीआई को बदलने की जरूरत से बच सकते हैं, असल में, अगर आपने इसके बजाय, RestfulResponse से ResponseWrapper और इसके विपरीत एक अंतर्निहित रूपांतरण बनाया है। यह विचार करने लायक कुछ है।

+0

और यह किस तरह से "कार्यात्मक शैली" है जिसे ओपी पूछ रहा था? आप त्रुटियों को फेंक रहे हैं और I/O –

+1

@oxbox_lakes प्रदर्शन कर रहे हैं आप सही हैं; यह कार्यात्मक नहीं है, लेकिन अधिकांश व्यावहारिक प्रोग्रामिंग को कार्यप्रणाली के रूप में समझौता करना पड़ता है। मुझे लगता है कि वह जो करने के बारे में पूछ रहा है उसके लिए यह सबसे अच्छा समाधान है। मैं व्यक्तिगत रूप से एपीआई को बदलने की सिफारिश करता हूं, लेकिन यह मेरी कॉल नहीं है। अगर वह त्रुटियों को फेंकना और आई/ओ करना चाहता है, तो मैं कहूंगा कि उसे नहीं करना चाहिए? – Destin

+0

लेकिन यह विशेष रूप से उनका प्रश्न था: "धाराप्रवाह और कार्यात्मक मिश्रण कैसे होता है?"। जो आपने किसी भी तरह उत्तर नहीं दिया है। –

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