पूर्णता तब प्राप्त नहीं होती है जब जोड़ने के लिए कुछ नहीं बचता हैऔर जब कुछ भी नहीं लिया जा सकता है।एंटोनी डी सेंट-एक्सपीरी, विंड, सैंड एंड स्टार्स, 1939
अक्सर आपको जावा अनुप्रयोगों के लिए I / O पैकेज डिजाइन और विकसित करने होते हैं। एक ओर java.io है, जो पर्याप्त से अधिक है। हालांकि, व्यवहार में, यह मानक कक्षाओं और इंटरफेस के एक सेट के साथ शायद ही कभी संभव है।
आलेख जावा प्लेटफ़ॉर्म पर I / O पैकेज को लागू करने के लिए एक विचार का एक व्यावहारिक उदाहरण प्रदान करता है।
समस्या का बयान
स्पष्टता के लिए, एक उदाहरण पर विचार करें। मान लीजिए कि आप मैट्रिक्स लाइब्रेरी के लिए I / O पैकेज विकसित करना चाहते हैं। इस मामले में, यह ध्यान में रखा जाना चाहिए कि:
- उदाहरण (प्रकार / वर्ग) अनगिनत हो सकते हैं, उदाहरण के लिए - घना, विरल;
- कई इनपुट और आउटपुट प्रारूप भी हो सकते हैं, उदाहरण के लिए, मैट्रिक्स मैट्रिक्स , एक्सएमएल;
मैट्रिक्स मैट्रिक्स प्रारूप
मैट्रिक्स मैट्रिक्स प्रारूप के लिए, निम्नलिखित मैट्रिक्स अभ्यावेदन होंगे:
0 2 3 0
घने मैट्रिक्स के लिए:
%%MatrixMarket matrix array real general 2 2 0 2 3 0
एक विरल मैट्रिक्स के लिए:
%%MatrixMarket matrix coordinate real general 2 2 2 0 1 2 1 0 3
कार्यान्वयन
इस प्रकार, I / O पैकेज का कार्यान्वयन इतना लचीला होना चाहिए कि सिस्टम का विस्तार करते समय (उदाहरण के लिए एक नए प्रकार के मैट्रिक्स को जोड़ना, उदाहरण के लिए ब्लॉक करना या एक नया प्रारूप जोड़ना, उदाहरण के लिए CSV), पैकेज को पूरी तरह से फिर से लिखना आवश्यक नहीं होगा - लेकिन यह केवल एक अतिरिक्त वर्ग को लागू करने के लिए पर्याप्त होगा - नए का वर्ग मैट्रिक्स या नया प्रारूप।
एक चौकस पाठक
पुल टेम्पलेट द्वारा हल की गई समस्या के साथ वर्णित समस्या की स्पष्ट समानता को नोटिस करेगा। यह सच है और लेख को I / O पैकेज के लिए ब्रिज टेम्पलेट के कार्यान्वयन के उदाहरण के रूप में लिया जा सकता है।
डिजाइन पैटर्न की मूल बातें पर लौटते हुए, हम
पुल पैटर्न को संक्षेप में अमूर्तन और कार्यान्वयन के अलगाव के रूप में वर्णित कर सकते हैं। हमारे मामले में, प्रारूप (XML, मैट्रिक्स मैट्रिक्स) से मैट्रिक्स (घने, ब्लॉक) के प्रकार को अलग करना। यह दो इंटरफेस - अमूर्त इंटरफेस और कार्यान्वयन इंटरफ़ेस के माध्यम से प्राप्त किया जाता है। अमूर्त इंटरफ़ेस को उच्च स्तर पर पैकेज के व्यवहार का वर्णन करना चाहिए, उदाहरण के लिए, रीडमैट्रिक्स (), राइटमैट्रिक्स () विधियों। उसी समय, कार्यान्वयन इंटरफ़ेस को निम्न-स्तरीय मुद्दों का वर्णन करना चाहिए, जैसे कि रीडमैट्रिक्स इलेमेंट (), राइटमैट्रिक्स मैट्रिक्स (), आदि। फिर सबसे सरल मामले में, I / O पैकेज के लिए वर्ग आरेख इस तरह दिखता है।

उच्च-स्तरीय राइटमैट्रिक्स () विधि निम्न-स्तरीय कॉल का एक क्रम है:
- writeMatrixHeader () - मैट्रिक्स के प्रकार के बारे में जानकारी लिखें;
- writeMatrixMeta () - मैट्रिक्स के आयाम के बारे में जानकारी रिकॉर्ड करना;
- writeMatrixElement () - मैट्रिक्स तत्व के बारे में जानकारी लिखें।
यह पता चला है कि पुल टेम्पलेट पहले वर्णित समस्या को हल करता है, कार्यान्वयन और अमूर्तन के पृथक्करण के लिए धन्यवाद। लेकिन ज्यादातर मामलों में, जिन वस्तुओं के साथ I / O पैकेट काम करते हैं, वे पहले से ही क्रमिक तंत्र (श्रीबल करने योग्य, बाहरी) को लागू कर रहे हैं। हमारे मामले में, मैट्रिक्स इंटरफ़ेस पहले से ही एक्सरसाइज़ करने योग्य इंटरफ़ेस का विस्तार कर रहा है। क्यों वास्तव में एक्सरसाइज और सीरिज़ेलेबल को
इस या
इस (लेखक के काम) शोध में नहीं पढ़ा जा सकता है। संक्षेप में - JVM / परावर्तन में कॉल की संख्या कम होने के कारण एक्सटर्ज़ीबल कई गुना तेजी से काम करता है।
और इसलिए घने मैट्रिक्स के लिए readExternal / writeExternal तरीके इस प्रकार हैं:
public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(rows); out.writeInt(columns); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { out.writeDouble(self[i][j]); } } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { rows = in.readInt(); columns = in.readInt(); self = new double[rows][columns]; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { self[i][j] = in.readDouble(); } } }
चौकस पाठक कहेगा: "यह ब्रिज टेम्पलेट के समान है!" और बिल्कुल सही होगा। ObjectOutput और ObjectInput इंटरफेस टेम्पलेट के विचार को कार्यान्वयन इंटरफेस के रूप में लागू करते हैं। फिर सवाल उठता है - "क्यों मैट्रिक्सराइडर / मैरिक्सविटर जैसी अधिक कक्षाएं उत्पन्न होती हैं और उनमें डुप्लिकेट रीडएक्सटर्ना () एल / राइटएक्स्टर्नल () तरीके?" यह सही है - कोई कारण नहीं। इसके अलावा, DRY (खुद को दोहराएं नहीं) पद्धति हमें इसकी याद दिलाती है।
इस मामले में, हम पैकेज के प्रस्तावित कार्यान्वयन को संशोधित करने का प्रयास करेंगे, इस तथ्य को ध्यान में रखते हुए कि java.io में पहले से ही कार्यान्वयन इंटरफेस हैं - ObjectInput / ObjectOutput। यानी हमें केवल प्रारूप वर्गों को लागू करने की आवश्यकता है - MMOutoutStream / MMInputStream (MM = MatrixMarket), क्रमांकन के लिए मानक कक्षाओं के बजाय उनका उपयोग करने के लिए - ObjectInputStream / ObjectOutputStream। तब उपयोग बहुत पारदर्शी होगा:
उपरोक्त कोड आसानी से क्रमांकन कोड में बदल जाता है। ऐसा करने के लिए, बस MM * वर्गों को ऑब्जेक्ट * से बदलें। (MMOutputStream -> ObjectOutputStream)।
एक अनसुलझी समस्या है। फ़ाइल के तार्किक ब्लॉकों के पृथक्करण की समस्या। हमारे मामले में, फ़ाइल में विभाजित है:
- हेडर - एक हेडर जिसमें मैट्रिक्स का प्रकार होता है;
- मेटा - मेटा जानकारी जिसमें मैट्रिक्स का आयाम है;
- डेटा - डेटा।
पैकेज के पिछले आर्किटेक्चर में, अलग-अलग तरीके प्रस्तुत किए गए थे जो इस जानकारी को अलग से रिकॉर्ड करने की अनुमति देते थे। हालाँकि, ObjectOutput / ObjectInput इंटरफेस स्पष्ट रूप से इस तरह के तरीके शामिल नहीं हैं। यानी मानक कक्षाओं में विधियाँ निम्न स्तर की होती हैं।
इस समस्या को हल करने के लिए, लेखक विशेष मार्करों (बाइट्स) का उपयोग करने का सुझाव देता है जो प्रत्येक ब्लॉक की सीमाओं को इंगित करता है - हेडर (HEADER_MARKER), मेटा जानकारी (META_MARKER) और तत्व (ELEMENT_MARKER)।
तब लिखने वाला बाहरी () / readExternal () तरीके इस तरह दिखेगा:
@Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(rows); out.writeInt(columns); out.writeByte(META_MARKER);
एक ओर, कुछ अतिरिक्त बाइट्स लिखने से एप्लिकेशन प्रदर्शन या कोड पठनीयता प्रभावित नहीं होगी, दूसरी ओर, यह विभिन्न प्रारूपों के लिए समर्थन के साथ बाहरी धाराओं को लागू करने के लिए एक अतिरिक्त अवसर प्रदान करता है।
धारा के दृष्टिकोण से, हमारे मामले में MMInputStream / MMOutputStream, रिकॉर्ड इस तरह दिखेगा:
- लिखने के लिए किसी भी डेटा को लिखने के माध्यम से प्राप्त करें।
- एक मार्कर की प्राप्ति पर, मूल्य बफर के आधार पर इसके साथ जुड़े कार्यों को निष्पादित करें।
निम्नलिखित MMOutputSteam वर्ग के कार्यान्वयन का मुख्य भाग है:
public class MMOutputStream extends OutputStream implements ObjectOutput { @Override public void writeByte(int v) throws IOException { switch (v) { case HEADER_MARKER: writeHeader(); break; case META_MARKER: writeMeta(); break; case ELEMENT_MARKER: writeElement(); break; } } @Override public void writeInt(int v) throws IOException { put(String.valueOf(v)); } @Override public void writeDouble(double v) throws IOException { put(String.format(Locale.US, "%.12f", v)); } @Override public void writeObject(Object obj) throws IOException { if (matrix instanceof SparseMatrix) { put(SPARSE_HEADER); } else if (matrix instanceof DenseMatrix) { put(DENSE_HEADER); } writeHeader(); matrix.writeExternal(this); flush(); } private void writeHeader() throws IOException { out.write("%%MatrixMarket "); out.write(buffer[0] + " "); out.write(buffer[1] + " "); out.write("real general"); out.newLine(); } private void writeMeta() throws IOException { dumpBuffer(); out.newLine(); } private void writeElement() throws IOException { dumpBuffer(); out.newLine(); } private void put(String value) { buffer[length++] = value; } private void dumpBuffer() throws IOException { for (int i = 0; i < length; i++) { out.write(buffer[i] + " "); } } }
सारांश
जावा में I / O पैकेज का प्रस्तावित कार्यान्वयन जावा एपीआई में मौजूदा पदानुक्रमों के लिए ब्रिज टेम्पलेट का एक सफल अनुप्रयोग है। लेखक को उम्मीद है कि लेख में वर्णित विचार पाठकों के निपटान में एक और सुविधाजनक उपकरण बन जाएगा और उन्हें इस विषय पर अतिरिक्त चर्चा के लिए प्रेरित करेगा।
*
लेख में वर्णित उदाहरण रैखिक बीजगणित की समस्याओं को हल करने के लिए एक खुली लाइब्रेरी का हिस्सा है -
la4j । विचार के कार्यान्वयन को
la4j.io पैकेज में पाया जा सकता है। वर्तमान संस्करण में, केवल मैट्रिक्स मैट्रिक्स प्रारूप समर्थित है।
PS विषय लेखक = लेखक la4j