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

टेम्प्लेट का उपयोग जटिल इनिशियलाइज़ेशन प्रक्रिया को बदलने के लिए किया जाता है, जिसके द्वारा वांछित ऑब्जेक्ट स्टेप को अंतिम रूप देकर अंतिम विधि के साथ बनाया जाता है। चरण वैकल्पिक हो सकते हैं और इसमें सख्त कॉल अनुक्रम नहीं होना चाहिए।

फाउंडेशन का उदाहरण
उन मामलों में जहां वांछित "URL" तय नहीं किया गया है, लेकिन घटकों से निर्मित है (उदाहरण के लिए, संसाधन के लिए होस्ट पता और रिश्तेदार पथ), आपने संभवतः फाउंडेशन लाइब्रेरी से सुविधाजनक URLComponents
तंत्र का उपयोग किया था।
URLComponents
, अधिकांश भाग के लिए, बस एक वर्ग है जो कई चर को जोड़ता है जो विभिन्न URL घटकों के मूल्यों को संग्रहीत करता है, साथ ही साथ url
संपत्ति भी है, जो घटकों के वर्तमान सेट के लिए उपयुक्त URL देता है। उदाहरण के लिए:
var urlComponents = URLComponents() urlComponents.scheme = "https" urlComponents.user = "admin" urlComponents.password = "qwerty" urlComponents.host = "somehost.com" urlComponents.port = 80 urlComponents.path = "/some/path" urlComponents.queryItems = [URLQueryItem(name: "page", value: "0")] _ = urlComponents.url
वास्तव में, उपरोक्त उपयोग मामला बिल्डर पैटर्न का कार्यान्वयन है। इस मामले में, URLComponents
बिल्डर के रूप में कार्य करता है, इसके विभिन्न गुणों ( scheme
, host
, आदि) को मान असाइन करना चरणों में भविष्य की वस्तु का आरंभीकरण है, और url
संपत्ति को कॉल करना एक अंतिम पद्धति की तरह है।
टिप्पणियों में, गर्म लड़ाई "URL" और "URI" का वर्णन करने वाले "RFC" दस्तावेजों के बारे में सामने आई, इसलिए, अधिक सटीक होने के लिए, मैं उदाहरण के लिए यह सुझाव देता हूं कि हम केवल दूरस्थ संसाधनों के "URL" के बारे में बात कर रहे हैं, और इस तरह का ध्यान न रखें। "URL" योजनाएं, जैसे, "फ़ाइल" कहती हैं।
सब कुछ ठीक लगता है यदि आप इस कोड का उपयोग अनजाने में करते हैं और इसकी सभी बारीकियों को जानते हैं। लेकिन अगर आप कुछ भूल जाते हैं तो क्या होगा? उदाहरण के लिए, एक मेजबान पते की तरह कुछ भी महत्वपूर्ण है? आपको क्या लगता है कि निम्नलिखित कोड निष्पादित करने का परिणाम क्या होगा?
var urlComponents = URLComponents() urlComponents.scheme = "https" urlComponents.path = "/some/path" _ = urlComponents.url
हम गुणों के साथ काम करते हैं, तरीकों से नहीं, और कोई भी त्रुटि सुनिश्चित करने के लिए "बाहर फेंक" नहीं दी जाएगी। "अंतिम" url
संपत्ति एक वैकल्पिक मूल्य लौटाती है, तो शायद हम nil
? नहीं, हमें अर्थहीन मान के साथ प्रकार URL
की पूरी तरह से ऑब्जेक्ट प्राप्त होता है - "https: / some / path"। इसलिए, यह ऊपर वर्णित "एपीआई" के आधार पर मेरे अपने "बिल्डर" लिखने का अभ्यास करने के लिए हुआ।
(एक "इमोजी" "साइकिल" होनी चाहिए थी, लेकिन "हबर" इसे प्रदर्शित नहीं करती है)
उपरोक्त के बावजूद, मैं URLComponents
को घटकों से "URL" को इकट्ठा करने के लिए URLComponents
अच्छा और सुविधाजनक "API" मानता URLComponents
, और इसके विपरीत, प्रसिद्ध "URL" के घटकों को "पार्स" करता हूं। इसलिए, इसके आधार पर, अब हम अपने स्वयं के प्रकार लिखते हैं जो भागों से "URL" एकत्र करता है और इस समय हमारे पास "एपीआई" की आवश्यकता होती है (मान लीजिए)।
सबसे पहले, मैं सभी आवश्यक गुणों के लिए नए मूल्यों को असाइन करके असमान प्रारंभिककरण से छुटकारा चाहता हूं। इसके बजाय, हम बिल्डर की एक आवृत्ति बनाने और सभी गुणों को मान देने की संभावना को कार्यान्वित करते हैं, जो श्रृंखला द्वारा बताए गए तरीकों का उपयोग करते हैं। श्रृंखला एक अंतिम विधि के साथ समाप्त होती है, जिसके कॉल का परिणाम URL
का संगत उदाहरण होगा। शायद आपने अपनी जीवन यात्रा पर "जावा में StringBuilder
" जैसी किसी चीज़ का सामना किया है - हम अब ऐसे "एपीआई" के लिए प्रयास करने जा रहे हैं।
श्रृंखला के साथ तरीकों-चरणों को कॉल करने में सक्षम होने के लिए, उनमें से प्रत्येक को वर्तमान बिल्डर का एक उदाहरण वापस करना होगा, जिसके अंदर संबंधित परिवर्तन संग्रहीत किया जाएगा। इस कारण से, और वस्तुओं की एकाधिक प्रतिलिपि से छुटकारा पाने के लिए और mutating
विधियों के आसपास नृत्य करने से, विशेष रूप से बिना सोचे-समझे, हम अपने बिल्डर को एक वर्ग घोषित करेंगे:
final class URLBuilder { }
उपरोक्त आवश्यकताओं को ध्यान में रखते हुए, हम भविष्य में "URL" के मापदंडों को निर्दिष्ट करने वाले तरीकों की घोषणा करते हैं:
final class URLBuilder { private var scheme = "https" private var user: String? private var password: String? private var host: String? private var port: Int? private var path = "" private var queryItems: [String : String]? func with(scheme: String) -> URLBuilder { self.scheme = scheme return self } func with(user: String) -> URLBuilder { self.user = user return self } func with(password: String) -> URLBuilder { self.password = password return self } func with(host: String) -> URLBuilder { self.host = host return self } func with(port: Int) -> URLBuilder { self.port = port return self } func with(path: String) -> URLBuilder { self.path = path return self } func with(queryItems: [String : String]) -> URLBuilder { self.queryItems = queryItems return self } }
हम अंतिम विधि द्वारा भविष्य के उपयोग के लिए वर्ग के निजी गुणों में निर्दिष्ट मापदंडों को बचाते हैं।
"एपीआई" के लिए एक और श्रद्धांजलि, जिस पर हम अपनी कक्षा को आधार बनाते हैं, वह संपत्ति है, जो सभी पड़ोसी संपत्तियों के विपरीत, वैकल्पिक नहीं है, और यदि कोई सापेक्ष पथ नहीं है, तो यह एक रिक्त स्ट्रिंग को इसके मूल्य के रूप में संग्रहीत करता है।
इसे लिखने के लिए, वास्तव में, अंतिम रूप देने की विधि में, आपको कुछ और चीजों के बारे में सोचने की जरूरत है। सबसे पहले, "URL" में कुछ भाग होते हैं, जिसके बिना, जैसा कि शुरुआत में संकेत दिया गया था, समझ बनाने के लिए बंद हो गया - यह scheme
और host
। हमने डिफ़ॉल्ट मान के साथ पहला "सम्मानित" किया है, इसलिए, इसके बारे में भूल जाने के बाद, हम अभी भी, सबसे अधिक संभावना, अपेक्षित परिणाम प्राप्त करेंगे।
दूसरे के साथ, चीजें थोड़ी अधिक जटिल हैं: इसे कुछ डिफ़ॉल्ट मान नहीं सौंपा जा सकता है। इस मामले में, हमारे पास दो तरीके हैं: इस संपत्ति के लिए एक मूल्य की अनुपस्थिति में, या तो nil
लौटें या एक त्रुटि फेंक दें और ग्राहक कोड को खुद तय करने दें कि इसके साथ क्या करना है। दूसरा विकल्प अधिक जटिल है, लेकिन यह आपको एक विशिष्ट प्रोग्रामर त्रुटि को स्पष्ट रूप से इंगित करने की अनुमति देगा। शायद, उदाहरण के लिए, हम इस रास्ते पर जाएंगे।
एक और दिलचस्प बिंदु user
और password
गुणों से संबंधित है: वे केवल तभी समझ में आते हैं जब वे एक साथ उपयोग किए जाते हैं। लेकिन क्या होगा यदि कोई प्रोग्रामर इन दोनों में से किसी एक को मान लेना भूल जाए?
और, शायद, विचार करने वाली अंतिम बात यह है कि अंतिम विधि के परिणामस्वरूप हम URLComponents
की url
संपत्ति का URLComponents
, और इस मामले में, यह बहुत उपयोगी वैकल्पिक नहीं है। हालांकि, nil
गुणों के सेट मूल्यों के किसी भी संयोजन के लिए, हम इसे प्राप्त नहीं करेंगे। (केवल एक खाली, बस बनाया गया, URLComponents
उदाहरण का कोई मूल्य नहीं होगा।) इसे दूर करने के लिए, आप उपयोग कर सकते हैं !
- ऑपरेटर "जबरन अलिखित"। लेकिन सामान्य तौर पर, मैं इसके उपयोग को प्रोत्साहित नहीं करना चाहूंगा, इसलिए, हमारे उदाहरण में, हम संक्षेप में "फाउंडेशन" की सूक्ष्मताओं के ज्ञान से सारगर्भित हैं और चर्चा के तहत स्थिति को सिस्टम त्रुटि के रूप में मानते हैं, जिसकी घटना हमारे कोड पर निर्भर नहीं करती है।
तो:
extension URLBuilder { func build() throws -> URL { guard let host = host else { throw URLBuilderError.emptyHost } if user != nil { guard password != nil else { throw URLBuilderError.inconsistentCredentials } } if password != nil { guard user != nil else { throw URLBuilderError.inconsistentCredentials } } var urlComponents = URLComponents() urlComponents.scheme = scheme urlComponents.user = user urlComponents.password = password urlComponents.host = host urlComponents.port = port urlComponents.path = path urlComponents.queryItems = queryItems?.map { URLQueryItem(name: $0, value: $1) } guard let url = urlComponents.url else { throw URLBuilderError.systemError
बस इतना ही, शायद! अब उदाहरण में "URL" का एक विस्फोटित निर्माण शुरुआत में ऐसा लग सकता है:
_ = try URLBuilder() .with(user: "admin") .with(password: "Qwerty") .with(host: "somehost.com") .with(port: 80) .with(path: "/some/path") .with(queryItems: ["page": "0"]) .build()
बेशक, do
- catch
बाहर या ऑपरेटर के बिना try
का उपयोग do
?
यदि कोई त्रुटि होती है, तो यह प्रोग्राम क्रैश करने का कारण होगा। लेकिन हमने "क्लाइंट" को त्रुटियों को संभालने का अवसर प्रदान किया क्योंकि वह फिट दिखता है।
हां, और इस टेम्पलेट का उपयोग करके चरण-दर-चरण निर्माण की एक अन्य उपयोगी विशेषता कोड के विभिन्न हिस्सों में कदम रखने की क्षमता है। सबसे लगातार मामला नहीं है, लेकिन फिर भी। अनुस्मारक के लिए धन्यवाद!
निष्कर्ष
टेम्पलेट समझना बहुत आसान है, और सब कुछ सरल है, जैसा कि आप जानते हैं, सरल है। या इसके विपरीत? खैर, कोई बात नहीं। मुख्य बात यह है कि मैं, अपनी आत्मा की एक खाई के बिना, मैं कह सकता हूं कि यह (खाका) पहले से ही हुआ था, मुझे बड़ी और जटिल आरंभीकरण प्रक्रिया बनाने की समस्याओं को हल करने में मदद मिली। उदाहरण के लिए, लाइब्रेरी में सर्वर के साथ संचार का एक सत्र तैयार करने की प्रक्रिया, जिसे मैंने लगभग दो साल पहले एक सेवा के लिए लिखा था। वैसे, कोड "ओपन सोर्स" है और यदि वांछित है, तो इसके साथ खुद को परिचित करना काफी संभव है। (हालांकि, ज़ाहिर है, तब से बहुत पानी बह चुका है, और अन्य प्रोग्रामर ने इस कोड पर आवेदन किया है।)
डिजाइन पैटर्न पर मेरी अन्य पोस्ट:
और यह मेरा "ट्विटर" मेरी सार्वजनिक-व्यावसायिक गतिविधि में एक काल्पनिक रुचि को संतुष्ट करने के लिए है।