अच्छी प्रथाओं में DRY सिद्धांत?


11

मैं अपनी प्रोग्रामिंग में DRY सिद्धांत का पालन करने की कोशिश कर रहा हूं जितना कि मैं कर सकता हूं। हाल ही में मैं ओओपी में डिजाइन पैटर्न सीख रहा हूं और मैंने खुद को काफी गुच्छा बना लिया है।

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

मैंने अपने लिए जो समस्या बनाई है, वह यह है कि मेरे पास जितनी भी टेबल हैं, मैं उतने ही ऑब्जेक्ट बनाता हूं। उदाहरण के लिए ये वे वस्तुएं होंगी जिन्हें मुझे एक टेबल को संभालने की आवश्यकता है comments

class Comment extends Model {

    protected $id;
    protected $author;
    protected $text;
    protected $date;
}

class CommentFactory implements iFactory {

    public function createFrom(array $data) {
        return new Comment($data);
    }
}

class CommentGateway implements iGateway {

    protected $db;

    public function __construct(\Database $db) {
        $this->db = $db;
    }

    public function persist($data) {

        if(isset($data['id'])) {
            $sql = 'UPDATE comments SET author = ?, text = ?, date = ? WHERE id = ?';
            $this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date'], $data['id']);
        } else {
            $sql = 'INSERT INTO comments (author, text, date) VALUES (?, ?, ?)';
            $this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date']);
        }
    }

    public function retrieve($id) {

        $sql = 'SELECT * FROM comments WHERE id = ?';
        return $this->db->prepare($sql)->execute($id)->fetch();
    }

    public function delete($id) {

        $sql = 'DELETE FROM comments WHERE id = ?';
        return $this->db->prepare($sql)->execute($id)->fetch();
    }
}

class CommentRepository {

    protected $gateway;
    protected $factory;

    public function __construct(iFactory $f, iGateway $g) {
        $this->gateway = $g;
        $this->factory = $f;
    }

    public function get($id) {

        $data = $this->gateway->retrieve($id);
        return $this->factory->createFrom($data);
    }

    public function add(Comment $comment) {

        $data = $comment->toArray();
        return $this->gateway->persist($data);
    }
}

फिर मेरा कंट्रोलर कैसा दिखता है

class Comment {

    public function view($id) {

        $gateway = new CommentGateway(Database::connection());
        $factory = new CommentFactory();
        $repo = new CommentRepository($factory, $gateway);

        return Response::view('comment/view', $repo->get($id));
    }
}

इसलिए मैंने सोचा कि मैं सही तरीके से डिज़ाइन पैटर्न का उपयोग कर रहा हूं और अच्छी प्रथाओं को बनाए रख रहा हूं, लेकिन इस चीज के साथ समस्या यह है कि जब मैं एक नई तालिका जोड़ता हूं, तो मुझे अन्य नामों के साथ समान कक्षाएं बनानी होंगी। इससे मुझमें संदेह पैदा होता है कि मैं कुछ गलत कर रहा हूं।

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

क्या मैं इसे सही तरीके से समझ रहा हूं, या क्या मुझे एक अलग नजरिया देखना चाहिए?


जब आप एक नई तालिका बनाते हैं, तो क्या आप हमेशा एक ही सेट SQL क्वेरी (या एक समान सेट) का उपयोग करते हैं? इसके अलावा, क्या फैक्ट्री वास्तविक कार्यक्रम में किसी सार्थक तर्क को कूटबद्ध कर रही है?
Ixrec

@Ixrec आमतौर पर गेटवे और रिपॉजिटरी में कस्टम तरीके होंगे जो जॉक्स जैसे अधिक जटिल एसक्यूएल क्वेश्चन करते हैं, समस्या यह है कि फ़ंक्शंस को फिर से प्राप्त करना, पुनर्प्राप्त करना और हटाना - इंटरफ़ेस द्वारा परिभाषित - टेबल के नाम और संभवतः के लिए हमेशा समान होते हैं लेकिन प्राथमिक कुंजी कॉलम की संभावना नहीं है, इसलिए मुझे हर कार्यान्वयन में उन्हें दोहराना होगा। कारखाने में शायद ही कोई तर्क होता है और कभी-कभी मैं इसे बिल्कुल छोड़ देता हूं और डेटा के बजाय गेटवे को वस्तु वापस कर देता हूं, लेकिन मैंने इस उदाहरण के लिए एक कारखाना बनाया क्योंकि यह उचित डिजाइन होना चाहिए ?
एमिलियो रोड्रिग्स

मैं शायद एक उचित जवाब देने के लिए योग्य नहीं हूं, लेकिन मुझे यह धारणा मिलती है कि 1) फैक्टरी और रिपॉजिटरी वर्ग वास्तव में कुछ भी उपयोगी नहीं कर रहे हैं, इसलिए आप उन्हें खोदने से बेहतर होगा और सीधे टिप्पणी और टिप्पणी के साथ काम करेंगे 2) उन्हें कॉपी-पेस्ट करने के बजाय एक ही जगह पर सामान्य दृढ़ता / कार्यों को हटाना / हटाना संभव है, शायद "डिफ़ॉल्ट कार्यान्वयन" के एक अमूर्त वर्ग में (जैसे जावा में संग्रह क्या करते हैं)
Irerec

जवाबों:


12

आप जिस समस्या को संबोधित कर रहे हैं वह काफी मौलिक है।

मैंने उसी समस्या का अनुभव किया है जब मैंने एक ऐसी कंपनी के लिए काम किया था जिसने एक बड़ा J2EE एप्लिकेशन बनाया था जिसमें कई सौ वेब पेज और जावा कोड की एक लाख से अधिक लाइनें शामिल थीं। इस कोड ने दृढ़ता के लिए ORM (JPA) का उपयोग किया।

यह समस्या तब और बदतर हो जाती है जब आप आर्किटेक्चर की हर परत में तीसरी पार्टी प्रौद्योगिकियों का उपयोग करते हैं और उन सभी प्रौद्योगिकियों को अपने स्वयं के डेटा प्रतिनिधित्व की आवश्यकता होती है।

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

जिस तरह से मैं इसे देखता हूं वहां केवल 3 संभव समाधान हैं। व्यवहार में ये समाधान उसी के नीचे आते हैं।

समाधान 1: कुछ अन्य दृढ़ता फ्रेमवर्क का उपयोग करें जो आपको केवल वही बताने की अनुमति देता है जिसे निरंतर होना चाहिए। शायद ऐसी कोई रूपरेखा है। इस दृष्टिकोण के साथ समस्या यह है कि यह भोली है क्योंकि आप सभी पैटर्न दृढ़ता से संबंधित नहीं होंगे। आप उपयोगकर्ता इंटरफ़ेस कोड के लिए भी पैटर्न का उपयोग करना चाहते हैं, इसलिए आपको एक GUI फ्रेमवर्क की आवश्यकता होगी जो आपके द्वारा चुने गए दृढ़ता के डेटा अभ्यावेदन का फिर से उपयोग कर सके। यदि आप उन्हें फिर से उपयोग नहीं कर सकते हैं तो आपको GUI फ्रेमवर्क और दृढ़ता फ्रेमवर्क के डेटा अभ्यावेदन को ब्रिज करने के लिए बॉयलर प्लेट कोड लिखना होगा .. और यह DRY सिद्धांत के विपरीत है।

समाधान 2: एक और - अधिक शक्तिशाली - प्रोग्रामिंग भाषा का उपयोग करें, जिसमें निर्माण होता है जो आपको दोहराए गए डिज़ाइन को व्यक्त करने की अनुमति देता है ताकि आप डिज़ाइन-कोड का फिर से उपयोग कर सकें। यह शायद आपके लिए एक विकल्प नहीं है, लेकिन मान लीजिए कि यह एक पल के लिए है। फिर जब आप दृढ़ता परत के ऊपर एक उपयोगकर्ता इंटरफ़ेस बनाना शुरू करते हैं, तो आप बॉयलर प्लेट कोड लिखने के बिना जीयूआई बनाने में समर्थन के लिए भाषा को फिर से शक्तिशाली बनाना चाहेंगे। यह संभावना नहीं है कि कोई भाषा शक्तिशाली हो जो आप करना चाहते हैं क्योंकि अधिकांश भाषाएं जीयूआई भवन के लिए 3 पार्टी फ्रेमवर्क पर भरोसा करती हैं जो प्रत्येक को काम करने के लिए अपने स्वयं के डेटा प्रतिनिधित्व की आवश्यकता होती है।

समाधान 3: कोड के पुनरावृत्ति को स्वचालित करें और कोड पीढ़ी के कुछ रूप का उपयोग करके डिज़ाइन करें। आपकी चिंता हैंड-कोडिंग रिपीटिटिव कोड / डिज़ाइन के डीआरई सिद्धांत का उल्लंघन करने के बाद से पैटर्न और डिज़ाइन के हाथ-कोड पुनरावृत्ति होने के बारे में है। आजकल वहाँ बहुत शक्तिशाली कोड जनरेटर फ्रेमवर्क हैं। यहां तक ​​कि "भाषा कार्यक्षेत्र" भी हैं जो आपको जल्दी से (आधे दिन जब आपके पास कोई अनुभव नहीं है) अपनी खुद की प्रोग्रामिंग भाषा बनाते हैं और उस भाषा का उपयोग करके किसी भी कोड (PHP / Java / SQL - किसी भी विचारशील पाठ फ़ाइल) को उत्पन्न करते हैं। मुझे एक्सटैक्स के साथ अनुभव है लेकिन मेटाएडिट और एमपीएस के रूप में अच्छी तरह से ठीक लगता है। मैं आपको दृढ़ता से सलाह देता हूं कि आप इनमें से किसी एक भाषा के कार्यक्षेत्र की जांच करें। मेरे लिए यह मेरे पेशेवर जीवन का सबसे मुक्तिदायक अनुभव था।

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


उत्तर के लिए धन्यवाद, मैंने गंभीरता से कोड पीढ़ी को ध्यान में रखा है और मैं एक समाधान को लागू करना शुरू कर रहा हूं। वे 4 बॉयलरप्लेट कक्षाएं हैं इसलिए मुझे लगता है कि मैं PHP में ही ऐसा कर सकता हूं। हालांकि यह दोहराए जाने वाले कोड के मुद्दे को हल नहीं करता है, मुझे लगता है कि ट्रेडऑफ़ इसके लायक हैं - दोहराव वाले कोड के बावजूद अत्यधिक बनाए रखने योग्य और आसानी से परिवर्तनशील।
एमिलियो रोड्रिग्स

यह पहली बार मैंने XText के बारे में सुना है और यह बहुत शक्तिशाली लग रहा है। मुझे इससे अवगत कराने के लिए धन्यवाद!
मैथ्यू जेम्स ब्रिग्स

8

आपके सामने आने वाली समस्या एक पुरानी है: लगातार वस्तुओं के लिए कोड अक्सर प्रत्येक वर्ग के लिए समान दिखता है, यह केवल बॉयलरप्लेट कोड है। यही कारण है कि कुछ स्मार्ट लोगों ने ऑब्जेक्ट रिलेशनल मैपर का आविष्कार किया - वे ठीक उसी समस्या को हल करते हैं। PHP के लिए ORM की सूची के लिए यह पूर्व SO पोस्ट देखें ।

जब मौजूदा ओआरएम आपकी आवश्यकताओं को पूरा नहीं करते हैं, तो एक विकल्प भी है: आप अपना खुद का कोड जनरेटर लिख सकते हैं, जो आपकी वस्तुओं के एक मेटा विवरण को जारी रखने और कोड के दोहराए गए हिस्से को उत्पन्न करने के लिए लेता है। यह वास्तव में बहुत कठिन नहीं है, मैंने अतीत में कुछ अलग प्रोग्रामिंग भाषाओं के लिए ऐसा किया था, मुझे यकीन है कि इस तरह की चीजों को भी PHP में लागू करना संभव होगा।


मैंने इस तरह की कार्यक्षमता बनाई है, लेकिन मैंने इसे इस से बदल दिया है क्योंकि मैं डेटा ऑब्जेक्ट को डेटा दृढ़ता कार्यों को संभालता था जो एसआरपी का अनुपालन नहीं करता है। उदाहरण के लिए, मेरे पास Model::getByPKविधि थी और उपरोक्त उदाहरण में मैं कर पाऊंगा, Comment::getByPKलेकिन डेटाबेस से डेटा प्राप्त करना और ऑब्जेक्ट का निर्माण करना डेटा ऑब्जेक्ट क्लास के भीतर समाहित है, यही वह समस्या है जिसके लिए मैं डिज़ाइन पैटर्न का उपयोग करके हल करने की कोशिश कर रहा हूं ।
एमिलियो रोड्रिग्स

ORM को मॉडल ऑब्जेक्ट में दृढ़ता तर्क रखने की आवश्यकता नहीं है। यह सक्रिय रिकॉर्ड पैटर्न है, और लोकप्रिय होते हुए भी विकल्प हैं। ओआरएम क्या उपलब्ध हैं, इस पर एक नज़र डालें, और आपको यह पता लगाना चाहिए कि यह समस्या नहीं है।
जूल्स

@ जूल्स एक बहुत अच्छा बिंदु है, यह मुझे सोच में पड़ गया और मैं सोच रहा था - मेरे एप्लिकेशन में एक ActiveRecord और Data Mapper कार्यान्वयन दोनों उपलब्ध होने की समस्या क्या होगी। तब मैं उनमें से हर एक का उपयोग कर सकता हूं जब मुझे उनकी आवश्यकता होती है - यह ActiveRecord पैटर्न का उपयोग करके एक ही कोड को फिर से लिखने की मेरी समस्या को हल करेगा और फिर जब मुझे वास्तव में डेटा मैपर की आवश्यकता होगी तो आवश्यक कक्षाएं बनाने के लिए ऐसा दर्द नहीं होगा काम के लिए?
एमिलियो रोड्रिग्स

1
एकमात्र समस्या जिसे मैं अभी देख सकता हूं, वह यह है कि किनारे के मामलों में काम करना जब एक क्वेरी को दो तालिकाओं में शामिल होने की आवश्यकता होती है, जहां एक सक्रिय रिकॉर्ड का उपयोग करता है और दूसरा आपके डेटा मैपर द्वारा प्रबंधित किया जाता है - यह जटिलता की एक परत जोड़ देगा जो अन्यथा नहीं होगा 'उठता नहीं। व्यक्तिगत रूप से, मैं सिर्फ मैपर का उपयोग करूंगा - मुझे शुरू से ही सक्रिय रिकॉर्ड पसंद नहीं आया है - लेकिन मुझे पता है कि यह सिर्फ मेरी राय है, और अन्य लोग असहमत हैं।
जूल्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.