मैं C ++ एप्लिकेशन लिख रहा हूं। अधिकांश एप्लिकेशन डेटा उद्धरण को पढ़ना और लिखना आवश्यक है और यह कोई अपवाद नहीं है। मैंने डेटा मॉडल और क्रमांकन तर्क के लिए एक उच्च स्तरीय डिज़ाइन बनाया। यह प्रश्न इन विशिष्ट लक्ष्यों को ध्यान में रखते हुए मेरे डिजाइन की समीक्षा का अनुरोध कर रहा है:
कच्चे बाइनरी, XML, JSON, एट: मनमाने प्रारूपों में डेटा मॉडल पढ़ने और लिखने का एक आसान और लचीला तरीका है। अल। डेटा का प्रारूप डेटा से और साथ ही उस कोड से भी अलग होना चाहिए जो क्रमबद्धता का अनुरोध कर रहा है।
यह सुनिश्चित करने के लिए कि क्रमबद्धता यथोचित रूप से त्रुटि-मुक्त है। I / O विभिन्न कारणों से स्वाभाविक रूप से जोखिम भरा है: क्या मेरा डिज़ाइन इसे विफल करने के लिए अधिक तरीके पेश करता है? यदि हां, तो मैं उन जोखिमों को कम करने के लिए डिजाइन को कैसे परिष्कृत कर सकता हूं?
यह प्रोजेक्ट C ++ का उपयोग करता है। आप इसे प्यार करते हैं या नफरत करते हैं, भाषा का काम करने का अपना तरीका है और डिजाइन का उद्देश्य भाषा के साथ काम करना है, इसके खिलाफ नहीं ।
अंत में, इस परियोजना को wxWidgets के शीर्ष पर बनाया गया है । जब मैं एक अधिक सामान्य मामले के लिए लागू समाधान की तलाश कर रहा हूं, तो इस विशिष्ट कार्यान्वयन को उस टूलकिट के साथ अच्छी तरह से काम करना चाहिए।
इस प्रकार C ++ में लिखी जाने वाली कक्षाओं का एक बहुत ही सरल सेट है जो डिज़ाइन का वर्णन करता है। ये वास्तविक कक्षाएं नहीं हैं जिन्हें मैंने आंशिक रूप से अब तक लिखा है, यह कोड केवल उस डिज़ाइन को दिखाता है जिसका मैं उपयोग कर रहा हूं।
सबसे पहले, कुछ नमूना DAO:
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
// One widget represents one record in the application.
class Widget {
public:
using id_type = int;
private:
id_type id;
};
// Container for widgets. Much more than a dumb container,
// it will also have indexes and other metadata. This represents
// one data file the user may open in the application.
class WidgetDatabase {
::std::map<Widget::id_type, ::std::shared_ptr<Widget>> widgets;
};
अगला, मैं DAO पढ़ने और लिखने के लिए शुद्ध आभासी कक्षाएं (इंटरफेस) को परिभाषित करता हूं। विचार डेटा ( SRP ) से डेटा के क्रमांकन को अमूर्त करना है ।
class WidgetReader {
public:
virtual Widget read(::std::istream &in) const abstract;
};
class WidgetWriter {
public:
virtual void write(::std::ostream &out, const Widget &widget) const abstract;
};
class WidgetDatabaseReader {
public:
virtual WidgetDatabase read(::std::istream &in) const abstract;
};
class WidgetDatabaseWriter {
public:
virtual void write(::std::ostream &out, const WidgetDatabase &widgetDb) const abstract;
};
अंत में, यहां वह कोड है जो वांछित I / O प्रकार के लिए उचित रीडर / लेखक प्राप्त करता है। पाठकों / लेखकों के उपवर्ग भी परिभाषित होंगे, लेकिन ये डिज़ाइन समीक्षा में कुछ नहीं जोड़ते हैं:
enum class WidgetIoType {
BINARY,
JSON,
XML
// Other types TBD.
};
WidgetIoType forFilename(::std::string &name) { return ...; }
class WidgetIoFactory {
public:
static ::std::unique_ptr<WidgetReader> getWidgetReader(const WidgetIoType &type) {
return ::std::unique_ptr<WidgetReader>(/* TODO */);
}
static ::std::unique_ptr<WidgetWriter> getWidgetWriter(const WidgetIoType &type) {
return ::std::unique_ptr<WidgetWriter>(/* TODO */);
}
static ::std::unique_ptr<WidgetDatabaseReader> getWidgetDatabaseReader(const WidgetIoType &type) {
return ::std::unique_ptr<WidgetDatabaseReader>(/* TODO */);
}
static ::std::unique_ptr<WidgetDatabaseWriter> getWidgetDatabaseWriter(const WidgetIoType &type) {
return ::std::unique_ptr<WidgetDatabaseWriter>(/* TODO */);
}
};
मेरे डिजाइन के निर्धारित लक्ष्यों के अनुसार, मुझे एक विशिष्ट चिंता है। C ++ स्ट्रीम को टेक्स्ट या बाइनरी मोड में खोला जा सकता है, लेकिन पहले से ही ओपन स्ट्रीम को चेक करने का कोई तरीका नहीं है। यह एक XML या JSON रीडर / लेखक को बाइनरी स्ट्रीम प्रदान करने के लिए प्रोग्रामर त्रुटि के माध्यम से संभव हो सकता है। यह सूक्ष्म (या इतनी सूक्ष्म नहीं) त्रुटियों का कारण बन सकता है। मैं कोड को तेजी से विफल करना पसंद करूंगा, लेकिन मुझे यकीन नहीं है कि यह डिजाइन ऐसा करेगा।
इसका एक तरीका पाठक या लेखक को स्ट्रीम खोलने की जिम्मेदारी को उतारना हो सकता है, लेकिन मेरा मानना है कि SRP का उल्लंघन करता है और कोड को अधिक जटिल बना देगा। DAO लिखते समय, लेखक को इस बात की परवाह नहीं करनी चाहिए कि धारा कहाँ जा रही है: यह एक फ़ाइल, मानक आउट, एक HTTP प्रतिक्रिया, एक सॉकेट, कुछ भी हो सकता है। एक बार जब यह चिंता क्रमिकता के तर्क में ढल जाती है तो यह और अधिक जटिल हो जाती है: यह विशिष्ट प्रकार की धारा को जानना चाहिए और किस निर्माता को कॉल करना चाहिए।
उस विकल्प के अलावा, मुझे यकीन नहीं है कि इन वस्तुओं को मॉडल करने का एक बेहतर तरीका क्या होगा जो सरल, लचीला है और इसका उपयोग करने वाले कोड में तर्क त्रुटियों को रोकने में मदद करता है।
उपयोग मामला जिसके साथ समाधान को एकीकृत किया जाना चाहिए, एक साधारण फ़ाइल चयन संवाद बॉक्स है । उपयोगकर्ता फ़ाइल मेनू से "Open ..." या "इस रूप में सहेजें ..." का चयन करता है, और प्रोग्राम WidgetDatabase खोलता या सहेजता है। व्यक्तिगत विजेट के लिए "आयात ..." और "निर्यात ..." विकल्प भी होंगे।
जब उपयोगकर्ता फ़ाइल खोलने या सहेजने के लिए चयन करता है, तो wxWidgets एक फ़ाइल नाम लौटाएगा। उस घटना पर प्रतिक्रिया देने वाला हैंडलर सामान्य प्रयोजन कोड होना चाहिए जो फ़ाइल का नाम लेता है, एक धारावाहिक को प्राप्त करता है, और भारी उठाने के लिए एक फ़ंक्शन को कॉल करता है। आदर्श रूप से यह डिज़ाइन भी काम करेगा यदि कोई अन्य कोड नॉन-फ़ाइल I / O का प्रदर्शन कर रहा हो, जैसे कि सॉकेट के ऊपर एक मोबाइल डिवाइस पर एक WidgetDatabase भेजना।
क्या एक विजेट अपने स्वयं के प्रारूप को बचाता है? क्या यह मौजूदा प्रारूपों के साथ हस्तक्षेप करता है? हाँ! ऊपर के सभी। फ़ाइल संवाद पर वापस जाना, Microsoft Word के बारे में सोचें। Microsoft DOCX प्रारूप को विकसित करने के लिए स्वतंत्र थे, हालांकि वे कुछ बाधाओं के भीतर चाहते थे। उसी समय, Word विरासत या तृतीय-पक्ष प्रारूप (उदा PDF) को भी पढ़ता या लिखता है। यह कार्यक्रम अलग नहीं है: मैं जिस "बाइनरी" प्रारूप के बारे में बात करता हूं वह गति के लिए डिज़ाइन किया गया एक अभी तक परिभाषित आंतरिक प्रारूप है। उसी समय, इसे अपने डोमेन में खुले मानक स्वरूपों को पढ़ने और लिखने में सक्षम होना चाहिए (प्रश्न के लिए अप्रासंगिक) इसलिए यह अन्य सॉफ़्टवेयर के साथ काम करने में सक्षम हो सकता है।
अंत में, केवल एक प्रकार का विजेट है। इसमें बाल वस्तुएं होंगी, लेकिन वे इस क्रमिक तर्क द्वारा नियंत्रित की जाएंगी। कार्यक्रम कभी भी विजेट और स्प्रोकेट दोनों को लोड नहीं करेगा । इस डिज़ाइन को केवल विजेट्स और विजेटसेट से संबंधित होना चाहिए।