खोज…


परिचय

इस पृष्ठ पर, आप C ++ में डिज़ाइन पैटर्न कैसे लागू किए जाते हैं, इसके उदाहरण पा सकते हैं। इन पैटर्नों के विवरण के लिए, आप डिज़ाइन पैटर्न प्रलेखन की जांच कर सकते हैं।

टिप्पणियों

एक डिज़ाइन पैटर्न सॉफ्टवेयर डिज़ाइन में दिए गए संदर्भ के भीतर आमतौर पर होने वाली समस्या का एक सामान्य पुन: प्रयोज्य समाधान है।

ऑब्जर्वर पैटर्न

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

विषय और पर्यवेक्षक एक-से-कई संबंधों को परिभाषित करते हैं। पर्यवेक्षक इस विषय पर निर्भर हैं कि जब विषय की स्थिति बदलती है, तो पर्यवेक्षक अधिसूचित हो जाते हैं। अधिसूचना के आधार पर, पर्यवेक्षकों को नए मूल्यों के साथ भी अपडेट किया जा सकता है।

यहाँ गामा की पुस्तक "डिज़ाइन पैटर्न" का उदाहरण दिया गया है।

#include <iostream>
#include <vector>

class Subject; 

class Observer 
{ 
public:
    virtual ~Observer() = default;
    virtual void Update(Subject&) = 0;
};

class Subject 
{ 
public: 
     virtual ~Subject() = default;
     void Attach(Observer& o) { observers.push_back(&o); }
     void Detach(Observer& o)
     {
         observers.erase(std::remove(observers.begin(), observers.end(), &o));
     }
     void Notify()
     {
         for (auto* o : observers) {
             o->Update(*this);
         }
     }
private:
     std::vector<Observer*> observers; 
};

class ClockTimer : public Subject 
{ 
public:

    void SetTime(int hour, int minute, int second)
    {
        this->hour = hour; 
        this->minute = minute;
        this->second = second;

        Notify(); 
    }

    int GetHour() const { return hour; }
    int GetMinute() const { return minute; }
    int GetSecond() const { return second; }

private: 
    int hour;
    int minute;
    int second;
}; 

class DigitalClock: public Observer 
{ 
public: 
     explicit DigitalClock(ClockTimer& s) : subject(s) { subject.Attach(*this); }
     ~DigitalClock() { subject.Detach(*this); }
     void Update(Subject& theChangedSubject) override
     {
         if (&theChangedSubject == &subject) {
             Draw();
         }
     }

     void Draw()
     {
         int hour = subject.GetHour(); 
         int minute = subject.GetMinute(); 
         int second = subject.GetSecond(); 

         std::cout << "Digital time is " << hour << ":" 
                   << minute << ":" 
                   << second << std::endl;           
     }

private:
     ClockTimer& subject;
};

class AnalogClock: public Observer 
{ 
public: 
     explicit AnalogClock(ClockTimer& s) : subject(s) { subject.Attach(*this); }
     ~AnalogClock() { subject.Detach(*this); }
     void Update(Subject& theChangedSubject) override
     {
         if (&theChangedSubject == &subject) {
             Draw();
         }
     }
     void Draw()
     {
         int hour = subject.GetHour(); 
         int minute = subject.GetMinute(); 
         int second = subject.GetSecond(); 

         std::cout << "Analog time is " << hour << ":" 
                   << minute << ":" 
                   << second << std::endl; 
     }
private:
     ClockTimer& subject;
};

int main()
{ 
    ClockTimer timer; 

    DigitalClock digitalClock(timer); 
    AnalogClock analogClock(timer); 

    timer.SetTime(14, 41, 36);
}

आउटपुट:

Digital time is 14:41:36
Analog time is 14:41:36

यहाँ पैटर्न का सारांश दिया गया है:

  1. ऑब्जेक्ट ( DigitalClock या AnalogClock वस्तु) विषय इंटरफेस का उपयोग करें ( Attach() या Detach() ) या तो (रजिस्टर) पर्यवेक्षकों या सदस्यता समाप्त (निकालें) के रूप में किया जा रहा है पर्यवेक्षकों (से सदस्यता लेने के लिए खुद को subject.Attach(*this); , subject.Detach(*this);

  2. प्रत्येक विषय में कई पर्यवेक्षक ( vector<Observer*> observers; ) हो सकते हैं।

  3. सभी पर्यवेक्षकों को ऑब्जर्वर इंटरफ़ेस को लागू करने की आवश्यकता है। इस इंटरफ़ेस में केवल एक विधि, Update() , जिसे तब बुलाया जाता है जब विषय की स्थिति बदल जाती है ( Update(Subject &) )

  4. Attach() और Detach() विधियों के अलावा, ठोस विषय एक Notify() विधि को लागू करता है जिसका उपयोग सभी मौजूदा पर्यवेक्षकों को अद्यतन करने के लिए किया जाता है जब भी राज्य बदलता है। लेकिन इस मामले में, उन सभी को मूल वर्ग में किया जाता है, Subject ( Subject::Attach (Observer&) , void Subject::Detach(Observer&) और void Subject::Notify()

  5. ठोस वस्तु में इसकी अवस्था को स्थापित करने और प्राप्त करने की विधियाँ भी हो सकती हैं।

  6. कंक्रीट पर्यवेक्षक किसी भी वर्ग हो सकते हैं जो ऑब्जर्वर इंटरफ़ेस को लागू करता है। अद्यतन प्राप्त करने के लिए एक ठोस विषय के साथ प्रत्येक पर्यवेक्षक सदस्यता (रजिस्टर) ( subject.Attach(*this); )।

  7. ऑब्जर्वर पैटर्न के दो ऑब्जेक्ट्स को शिथिल रूप से युग्मित किया जाता है , वे परस्पर क्रिया कर सकते हैं लेकिन एक-दूसरे के थोड़े से ज्ञान के साथ।

रूपांतर:

सिग्नल और स्लॉट

सिग्नल और स्लॉट Qt में शुरू किया गया एक भाषा निर्माण है, जो बॉयलरप्लेट कोड से बचने के दौरान ऑब्जर्वर पैटर्न को लागू करना आसान बनाता है। अवधारणा यह है कि नियंत्रण (जिसे विगेट्स के रूप में भी जाना जाता है) घटना की जानकारी वाले सिग्नल भेज सकता है जो स्लॉट्स के रूप में ज्ञात विशेष कार्यों का उपयोग करके अन्य नियंत्रणों द्वारा प्राप्त किया जा सकता है। Qt में स्लॉट को एक वर्ग का सदस्य होना चाहिए। ग्राफिकल यूजर इंटरफेसेस को जिस तरह से डिज़ाइन किया गया है उससे सिग्नल / स्लॉट सिस्टम अच्छी तरह से फिट बैठता है। इसी तरह, सिग्नल / स्लॉट सिस्टम का उपयोग अतुल्यकालिक I / O (सॉकेट, पाइप, सीरियल डिवाइस आदि सहित) के लिए किया जा सकता है, घटना की अधिसूचना या उचित ऑब्जेक्ट इंस्टेंसेस और विधियों या फ़ंक्शन के साथ टाइमआउट ईवेंट को संबद्ध करने के लिए। कोई पंजीकरण / डेरेगेजेशन / इनवोकेशन कोड लिखने की आवश्यकता नहीं है, क्योंकि क्यूटी का मेटा ऑब्जेक्ट कंपाइलर (एमओसी) स्वचालित रूप से आवश्यक बुनियादी ढांचे को उत्पन्न करता है।

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

एडाप्टर पैटर्न

एक वर्ग के इंटरफ़ेस को दूसरे इंटरफ़ेस क्लाइंट की अपेक्षा में परिवर्तित करें। एडेप्टर (या रैपर) कक्षाओं को एक साथ काम करने देता है जो असंगत इंटरफेस के कारण अन्यथा नहीं हो सकता है। एडेप्टर पैटर्न की प्रेरणा है कि हम मौजूदा सॉफ़्टवेयर का पुन: उपयोग कर सकते हैं यदि हम इंटरफ़ेस को संशोधित कर सकते हैं।

  1. एडाप्टर पैटर्न ऑब्जेक्ट रचना पर निर्भर करता है।

  2. एडॉप्टर ऑब्जेक्ट पर क्लाइंट कॉल ऑपरेशन।

  3. एडेप्टर ऑपरेशन को अंजाम देने के लिए एडाप्टटी को बुलाता है।

  4. एसटीएल में, स्टैक वेक्टर से अनुकूलित होता है: जब स्टैक पुश निष्पादित करता है (), अंतर्निहित वेक्टर वेक्टर करता है :: push_back ()।

उदाहरण:

#include <iostream>

// Desired interface (Target)
class Rectangle 
{
  public:
    virtual void draw() = 0;
};

// Legacy component (Adaptee)
class LegacyRectangle 
{
  public:
    LegacyRectangle(int x1, int y1, int x2, int y2) {
        x1_ = x1;
        y1_ = y1;
        x2_ = x2;
        y2_ = y2;
        std::cout << "LegacyRectangle(x1,y1,x2,y2)\n";
    }
    void oldDraw() {
        std::cout << "LegacyRectangle:  oldDraw(). \n";
    }
  private:
    int x1_;
    int y1_;
    int x2_;
    int y2_;
};

// Adapter wrapper
class RectangleAdapter: public Rectangle, private LegacyRectangle 
{
  public:
    RectangleAdapter(int x, int y, int w, int h):
      LegacyRectangle(x, y, x + w, y + h) {
         std::cout << "RectangleAdapter(x,y,x+w,x+h)\n";
      }

    void draw() {
        std::cout << "RectangleAdapter: draw().\n"; 
        oldDraw();
    }
};

int main()
{
  int x = 20, y = 50, w = 300, h = 200;
  Rectangle *r = new RectangleAdapter(x,y,w,h);
  r->draw();
}

//Output:
//LegacyRectangle(x1,y1,x2,y2)
//RectangleAdapter(x,y,x+w,x+h)

कोड का सारांश:

  1. ग्राहक को लगता है कि वह एक Rectangle से बात कर रहा है

  2. लक्ष्य Rectangle वर्ग है। यह वह है जिस पर ग्राहक विधि लागू करता है।

     Rectangle *r = new RectangleAdapter(x,y,w,h);
     r->draw();
    
  3. ध्यान दें कि एडेप्टर वर्ग कई उत्तराधिकार का उपयोग करता है।

     class RectangleAdapter: public Rectangle, private LegacyRectangle {
         ...
     }
    
  4. एडाप्टर RectangleAdapter की सुविधा देता है LegacyRectangle (अनुरोध का जवाब draw() एक पर Rectangle दोनों वर्गों इनहेरिट द्वारा)।

  5. LegacyRectangle वर्ग में Rectangle के समान तरीके ( draw() नहीं हैं, लेकिन Adapter(RectangleAdapter) LegacyRectangle Adapter(RectangleAdapter) Rectangle पद्धति को कॉल कर सकते हैं और LegacyRectangle , oldDraw() पर पद्धति को LegacyRectangle

     class RectangleAdapter: public Rectangle, private LegacyRectangle {
       public:
         RectangleAdapter(int x, int y, int w, int h):
           LegacyRectangle(x, y, x + w, y + h) {
             std::cout << "RectangleAdapter(x,y,x+w,x+h)\n";
           }
    
         void draw() {
             std::cout << "RectangleAdapter: draw().\n"; 
             oldDraw();
         }
     };
    

एडाप्टर डिजाइन पैटर्न एक संगत लेकिन अलग इंटरफ़ेस में एक वर्ग के लिए इंटरफ़ेस का अनुवाद करता है। तो, यह प्रॉक्सी पैटर्न के समान है जिसमें यह एकल-घटक आवरण है। लेकिन एडेप्टर वर्ग और मूल वर्ग के लिए इंटरफ़ेस अलग हो सकता है।

जैसा कि हमने ऊपर उदाहरण में देखा है, यह एडेप्टर पैटर्न मौजूदा एपीआई के लिए एक अलग इंटरफ़ेस को उजागर करने के लिए उपयोगी है ताकि इसे अन्य कोड के साथ काम करने की अनुमति मिल सके। इसके अलावा, एडेप्टर पैटर्न का उपयोग करके, हम विषम इंटरफेस ले सकते हैं, और उन्हें संगत एपीआई प्रदान करने के लिए बदल सकते हैं।

ब्रिज पैटर्न में ऑब्जेक्ट एडॉप्टर के समान एक संरचना होती है, लेकिन ब्रिज का एक अलग इरादा होता है: इसका उद्देश्य इसके कार्यान्वयन से एक इंटरफ़ेस को अलग करना है ताकि वे आसानी से और स्वतंत्र रूप से विविध हो सकें। एक एडॉप्टर एक मौजूदा ऑब्जेक्ट के इंटरफेस को बदलने के लिए है

फैक्टरी पैटर्न

फैक्टरी पैटर्न ऑब्जेक्ट निर्माण को डिकम्पोज करता है और एक सामान्य इंटरफ़ेस का उपयोग करके नाम बनाने की अनुमति देता है:

class Animal{
public:
    virtual std::shared_ptr<Animal> clone() const = 0;
    virtual std::string  getname() const = 0;
};

class Bear: public Animal{
public:
    virtual std::shared_ptr<Animal> clone() const override
    {
        return std::make_shared<Bear>(*this);
    }
    virtual std::string getname() const override
    {
        return "bear";
    }
};


class Cat: public Animal{
public:
    virtual std::shared_ptr<Animal> clone() const override
    {
        return std::make_shared<Cat>(*this);
    }
    virtual std::string  getname() const override
    {
        return "cat";
    }
};

class AnimalFactory{
public:
    static std::shared_ptr<Animal> getAnimal( const std::string&   name )
    {
      if ( name == "bear" )
        return std::make_shared<Bear>();
      if ( name == "cat" )
        return std::shared_ptr<Cat>();
     
    return nullptr;
    }


};

धाराप्रवाह एपीआई के साथ बिल्डर पैटर्न

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

C # में ईमेल बिल्डर से प्रेरित होकर, मैंने यहाँ C ++ संस्करण बनाने का निर्णय लिया है। एक ईमेल ऑब्जेक्ट जरूरी एक बहुत ही जटिल ऑब्जेक्ट नहीं है , लेकिन यह पैटर्न प्रदर्शित कर सकता है।

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

// Forward declaring the builder
class EmailBuilder;

class Email
{
  public:
    friend class EmailBuilder;  // the builder can access Email's privates
    
    static EmailBuilder make();
    
    string to_string() const {
        stringstream stream;
        stream << "from: " << m_from
               << "\nto: " << m_to
               << "\nsubject: " << m_subject
               << "\nbody: " << m_body;
        return stream.str();
    }
    
  private:
    Email() = default; // restrict construction to builder
    
    string m_from;
    string m_to;
    string m_subject;
    string m_body;
};

class EmailBuilder
{
  public:
    EmailBuilder& from(const string &from) {
        m_email.m_from = from;
        return *this;
    }
    
    EmailBuilder& to(const string &to) {
        m_email.m_to = to;
        return *this;
    }
    
    EmailBuilder& subject(const string &subject) {
        m_email.m_subject = subject;
        return *this;
    }
    
    EmailBuilder& body(const string &body) {
        m_email.m_body = body;
        return *this;
    }
    
    operator Email&&() {
        return std::move(m_email); // notice the move
    }
    
  private:
    Email m_email;
};

EmailBuilder Email::make()
{
    return EmailBuilder();
}

// Bonus example!
std::ostream& operator <<(std::ostream& stream, const Email& email)
{
    stream << email.to_string();
    return stream;
}


int main()
{
    Email mail = Email::make().from("[email protected]")
                              .to("[email protected]")
                              .subject("C++ builders")
                              .body("I like this API, don't you?");
                              
    cout << mail << endl;
}

C ++ के पुराने संस्करणों के लिए, कोई सिर्फ std::move चालन संचालन को अनदेखा कर सकता है और रूपांतरण ऑपरेटर से && हटा सकता है (हालांकि यह एक अस्थायी प्रतिलिपि बनाएगा)।

operator Email&&() द्वारा निर्मित ईमेल को रिलीज़ करने पर बिल्डर अपना काम पूरा कर लेता है। इस उदाहरण में, बिल्डर एक अस्थायी ऑब्जेक्ट है और नष्ट होने से पहले ईमेल लौटाता है। आप रूपांतरण ऑपरेटर के बजाय Email EmailBuilder::build() {...} जैसे स्पष्ट संचालन का भी उपयोग कर सकते हैं।

बिल्डर को चारों ओर से गुजारें

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

void add_addresses(EmailBuilder& builder)
{
    builder.from("[email protected]")
           .to("[email protected]");
}

void compose_mail(EmailBuilder& builder)
{
    builder.subject("I know the subject")
           .body("And the body. Someone else knows the addresses.");
}

int main()
{
    EmailBuilder builder;
    add_addresses(builder);
    compose_mail(builder);
    
    Email mail = builder;
    cout << mail << endl;
}

डिजाइन संस्करण: म्यूटेबल ऑब्जेक्ट

अपनी आवश्यकताओं को पूरा करने के लिए आप इस पैटर्न का डिज़ाइन बदल सकते हैं। मैं एक वैरिएंट दूंगा।

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



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow