"IOS" और "स्विफ्ट" ब्रह्मांडों में वास्तुशिल्प पैटर्न "आगंतुक"

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

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

मैं इस तथ्य पर भी ध्यान आकर्षित करना चाहूंगा कि डिजाइन तकनीक का अध्ययन करने के लिए उदाहरणों में कोड लिखा गया था। मैं इसकी (कोड) कमियों और वास्तविक परियोजनाओं में उपयोग के लिए इसे सुधारने की संभावनाओं से अवगत हूं।

उदाहरण


मान लें कि आपके पास UITableViewController का एक उपप्रकार है जो UITableViewController कई उपप्रकारों का उपयोग करता है:

 class FirstCell: UITableViewCell { /**/ } class SecondCell: UITableViewCell { /**/ } class ThirdCell: UITableViewCell { /**/ } class TableVC: UITableViewController { override func viewDidLoad() { super.viewDidLoad() tableView.register(FirstCell.self, forCellReuseIdentifier: "FirstCell") tableView.register(SecondCell.self, forCellReuseIdentifier: "SecondCell") tableView.register(ThirdCell.self, forCellReuseIdentifier: "ThirdCell") } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { /**/ return FirstCell() /**/ return SecondCell() /**/ return ThirdCell() } } 

मान लीजिए कि विभिन्न उपप्रकारों की कोशिकाओं में अलग-अलग ऊंचाइयां हैं।

बेशक, प्रत्येक प्रकार के सेल के कार्यान्वयन में ऊंचाई की गणना सीधे रखी जा सकती है। लेकिन क्या होगा अगर सेल की ऊंचाई न केवल अपने प्रकार पर निर्भर करती है, बल्कि किसी भी बाहरी परिस्थितियों पर भी? उदाहरण के लिए, एक सेल प्रकार का उपयोग विभिन्न तालिकाओं के साथ विभिन्न तालिकाओं में किया जा सकता है। इस मामले में, हम पूरी तरह से UITableViewCell उपवर्गों को उनके "सुपरवाइवे" या "व्यू कंट्रोलर" की जरूरतों से अवगत नहीं कराना चाहते हैं।

फिर ऊंचाई की गणना UITableViewController तरीकों से की जा सकती है: या तो UITableViewCell को ऊँचाई मान से आरंभ करें, या UITableViewCell उदाहरण को एक विशिष्ट उपप्रकार में UITableViewCell विधि tableView(_:heightForRowAt:) में विभिन्न मान tableView(_:heightForRowAt:) । लेकिन यह दृष्टिकोण भी अनम्य हो सकता है और "यदि" ऑपरेटरों या भारी "स्विच" निर्माण के लंबे अनुक्रम में बदल सकता है।

"विज़िटर" टेम्पलेट का उपयोग करके समस्या का समाधान करना


बेशक, न केवल "विज़िटर" टेम्पलेट इस समस्या को हल करने में सक्षम है, लेकिन वह इसे काफी शान से करने में सक्षम है।

ऐसा करने के लिए, सबसे पहले, हम एक प्रकार का निर्माण करेंगे, जो वास्तव में, सेल प्रकारों का "आगंतुक" होगा और एक ऐसी वस्तु जिसकी जिम्मेदारी केवल तालिका सेल की ऊंचाई की गणना करना है:

 struct HeightResultVisitor { func visit(_ ell: FirstCell) -> CGFloat { return 10.0 } func visit(_ ell: SecondCell) -> CGFloat { return 20.0 } func visit(_ ell: ThirdCell) -> CGFloat { return 30.0 } } 

प्रकार उपयोग किए गए प्रत्येक उपप्रकार से अवगत है और उनमें से प्रत्येक के लिए वांछित मान लौटाता है।

दूसरे, UITableViewCell प्रत्येक उपप्रकार को इस "आगंतुक" को "प्राप्त" करने में सक्षम होना चाहिए। ऐसा करने के लिए, हम इस तरह के एक "प्राप्त" विधि के साथ एक प्रोटोकॉल की घोषणा करेंगे, जिसे सभी उपयोग किए गए सेल प्रकारों द्वारा कार्यान्वित किया जाएगा:

 protocol HeightResultVisitable { func accept(_ visitor: HeightResultVisitor) -> CGFloat } extension FirstCell: HeightResultVisitable { func accept(_ visitor: HeightResultVisitor) -> CGFloat { return visitor.visit(self) } } extension SecondCell: HeightResultVisitable { func accept(_ visitor: HeightResultVisitor) -> CGFloat { return visitor.visit(self) } } extension ThirdCell: HeightResultVisitable { func accept(_ visitor: HeightResultVisitor) -> CGFloat { return visitor.visit(self) } } 

UITableViewController उपवर्ग के अंदर, कार्यक्षमता का उपयोग निम्नानुसार किया जा सकता है:

 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cell = tableView.cellForRow(at: indexPath) as! HeightResultVisitable return cell.accept(HeightResultVisitor()) } 

बेहतर हो सकता है!


सबसे अधिक संभावना है, हम ऐसा कोड नहीं चाहते हैं जो विशिष्ट कार्यक्षमता के साथ कठोरता से जुड़ा हो। शायद हम अपने सेल के सेट में नई कार्यक्षमता जोड़ने में सक्षम होना चाहते हैं, लेकिन न केवल उनकी ऊंचाई के बारे में, बल्कि, कहते हैं, पृष्ठभूमि का रंग, सेल के अंदर पाठ, आदि, और वापसी मूल्य के प्रकार से बंधा नहीं होना चाहिए। associatedtype प्रोटोकॉल ( "एसोसिएटेड प्रकार के साथ प्रोटोकॉल", "पैट" ) यहां मदद करेंगे:

 protocol CellVisitor { associatedtype T func visit(_ cell: FirstCell) -> T func visit(_ cell: SecondCell) -> T func visit(_ cell: ThirdCell) -> T } 

सेल ऊंचाई लौटने के लिए इसका कार्यान्वयन:

 struct HeightResultCellVisitor: CellVisitor { func visit(_ cell: FirstCell) -> CGFloat { return 10.0 } func visit(_ cell: SecondCell) -> CGFloat { return 20.0 } func visit(_ cell: ThirdCell) -> CGFloat { return 30.0 } } 

"होस्ट" पक्ष पर, इस प्रकार के किसी भी "आगंतुक" के लिए केवल एक सामान्य प्रोटोकॉल और इसके केवल कार्यान्वयन के लिए पर्याप्त है। केवल "आगंतुक" पार्टियां विभिन्न प्रकार के रिटर्न वैल्यू से अवगत होंगी।

"प्राप्त आगंतुक" (पुस्तक "GoF" इस पक्ष को "तत्व" कहा जाता है) के लिए प्रोटोकॉल फॉर्म ले जाएगा:

 protocol Visitableell where Self: UITableViewCell { func accept<V: CellVisitor>(_ visitor: V) -> VT } 

(लागू करने के प्रकार के लिए कोई प्रतिबंध नहीं हो सकता है। लेकिन इस उदाहरण में UITableViewCell उपवर्गों द्वारा इस प्रोटोकॉल को लागू करने का कोई मतलब नहीं है।)

और UITableViewCell उपप्रकारों में इसका कार्यान्वयन:

 extension FirstCell: Visitableell { func accept<V: CellVisitor>(_ visitor: V) -> VT { return visitor.visit(self) } } extension SecondCell: Visitableell { func accept<V: CellVisitor>(_ visitor: V) -> VT { return visitor.visit(self) } } extension ThirdCell: Visitableell { func accept<V: CellVisitor>(_ visitor: V) -> VT { return visitor.visit(self) } } 

और अंत में, उपयोग करें:

 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cell = tableView.cellForRow(at: indexPath) as! Visitableell return cell.accept(HeightResultCellVisitor()) } 
इस प्रकार, हम "आगंतुक" के विभिन्न कार्यान्वयनों का उपयोग करते हुए, सामान्य रूप से, लगभग कुछ भी बनाने में सक्षम होंगे, और नई कार्यक्षमता का समर्थन करने के लिए "प्राप्त करने वाले पक्ष" से कुछ भी आवश्यक नहीं होगा। इस पार्टी को यह भी पता नहीं होगा कि "अतिथि" ने वास्तव में क्या किया है।

एक और उदाहरण


आइए एक समान "विज़िटर" का उपयोग करके सेल की पृष्ठभूमि का रंग बदलने की कोशिश करें:

 struct ColorResultCellVisitor: CellVisitor { func visit(_ cell: FirstCell) -> UIColor { return .black } func visit(_ cell: SecondCell) -> UIColor { return .white } func visit(_ cell: ThirdCell) -> UIColor { return .red } } 

इस आगंतुक का उपयोग करने का एक उदाहरण:

 override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.contentView.backgroundColor = (cell as! Visitableell).accept(ColorResultCellVisitor()) } 

इस कोड में कुछ भ्रामक होना चाहिए ... शुरुआत में, यह कहा गया था कि "आगंतुक" बाहर से कक्षा में कार्यक्षमता जोड़ने में सक्षम है। तो क्या इसमें सेल के बैकग्राउंड कलर को बदलने की सभी कार्यक्षमता को "छिपाना" संभव है, और न केवल इससे मूल्य प्राप्त करना है? आप कर सकते हैं। फिर associatedtype मान Void (उर्फ () - एक खाली टुल्ल) लेगी:

 struct BackgroundColorSetter: CellVisitor{ func visit(_ cell: FirstCell) { cell.contentView.backgroundColor = .black } func visit(_ cell: SecondCell) { cell.contentView.backgroundColor = .white } func visit(_ cell: ThirdCell) { cell.contentView.backgroundColor = .red } } 

का उपयोग करें:

 override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { (cell as! Visitableell).accept(BackgroundColorSetter()) } 


एक निष्कर्ष के बजाय



आप पहली नज़र में लगभग पैटर्न को पसंद कर सकते हैं, हालाँकि, आपको इसका उपयोग सावधानी से करना चाहिए। कोड में इसकी उपस्थिति अक्सर वास्तुकला में अधिक सामान्य दोषों का संकेत हो सकती है। शायद आप उन चीजों को जोड़ने की कोशिश कर रहे हैं जो कनेक्ट नहीं होनी चाहिए। शायद अतिरिक्त कार्यक्षमता एक तरह से या किसी अन्य में अमूर्त के एक स्तर को डालने के लायक है।

एक तरीका या दूसरा, लगभग किसी भी पैटर्न के अपने फायदे और नुकसान हैं, और इसका उपयोग करने से पहले आपको हमेशा सोच समझकर निर्णय लेना चाहिए। पैटर्न, एक तरफ, कोड को आसानी से पढ़ने और चर्चा करने के लिए प्रोग्रामिंग तकनीकों को सामान्य बनाने का एक तरीका है। और दूसरे पर - एक समस्या को हल करने का एक तरीका (कभी-कभी कृत्रिम रूप से पेश किया गया)। और, ज़ाहिर है, किसी भी मामले में, अपने उपयोग के बहुत तथ्य के लिए सभी ज्ञात पैटर्न के लिए कोड को कट्टरता से न लाएं।


मुझे लगता है मैं कर रहा हूँ! सभी सुंदर कोड और कम "बग"!

डिजाइन पैटर्न पर मेरे अन्य लेख:

Source: https://habr.com/ru/post/hi432558/


All Articles