Sign in | Join | Help
Καλωσήλθατε στο StudentGuru!

Jss

Linux, OSF, programming

Design Patterns in C++; Prototype

Συνεχίζοντας την περιήγησή μας στα design patterns και πως αυτά υλοποιούνται σε C++, θα ασχοληθούμε με ένα άλλο pattern το οποίο αφορά τη δημιουργία αντικειμένων. To Prototype Design Pattern.

Δεν είναι αναγκαστικό να διαβάσετε το προηγούμενο post που αφορούσε το Singleton Pattern αλλά θα σας το συνιστούσα. Συνήθως για την υλοποίηση ή χρήση ενός pattern χρειαζόμαστε ένα άλλο και οπότε όσα παρουσιάσω θα είναι με μία λογική σειρά.

Σκοπός: 
Προσδιορισμός του είδος των αντικειμένων που θα δημιουργούνται με βάση ένα προτότυπο instance μίας κλάσσης και στη συνέχεια δημιουργίας αντίγραφου αυτού.

Πρόβλημα:
Έχουμε διάφορες υλοποιήσεις μιας abstract κλάσης. Κατά την εκτέλεση της εφαρμογής μας θέλουμε να δημιουργούμε αντικείμενα μιας συγκεκριμένης υλοποίησης της abstract κλάσης, δηλαδή αντικείμενα μίας συγκεκριμένης concrete κλάσης. Το ποιά concrete κλάση θα χρησιμοποιείται για τη δημιουργία των αντικειμένων μπορεί να ορίζεται από τις παραμέτρους της εφαρμογής, από κάποια ρύθμιση και ενδεχομένως αυτή η ρύθμιση μπορεί να αλλάζει και κατά την εκτέλεση της εφαρμογής. Μία πολύ άσχημη λύση είναι κάθε φορά που θέλουμε να δημιουργίσουμε το αντικείμενο να το κάνουμε σε ένα switch ή σε ένα if με όλες τις concrete κλάσεις.

Λύση:
Σε κάθε concrete κλάση έχουμε μία μέθοδο Clone() η οποία επιστρέφει αντίγραφο του εαυτού της. Ορίζουμε μία κλάση με την οποία θα δημιουργούμε τα αντικείμενα. O constructor αυτής θα παίρνει σαν παράμετρο ένα προτότυπο instance της abstract κλάσης. Κατά τη δημιουργία ενός αντικειμένου η κλάση θα δημιουργεί ένα αντίγραφο του προτότυπου instance μέσω της Clone().

Yλοποίηση σε C++:
Για παράδειγμα ας πούμε πως αναπτύσουμε έναν html editor. Η εφαρμογή μας μπορεί να δημιουργεί δύο είδη html αρχείων: html και xhtml. Υπάρχει και μία ρύθμιση για το default είδος αρχείου. Έτσι κάθε φορά που στην εφαρμογή θέλουμε να δημιουργήσουμε ένα καινούργιο αρχείο θα πρέπει να δημιουργείται το default.

Ας πούμε πως το interface ενός html αρχείου ορίζεται από την abstract κλάση Document:

//document.h

//Η abstract κλάση που ορίζει το interface των αρχείων
//που χειρίζεται η εφαρμογή
class Document {
public:
    //Οι μέθοδοι του interface
    virtual void Preview(void)=0;
    //...
    //...
    
    //Μέθοδος κλωνοποίησης. Επιστρέφει αντίγραφο
    //του instance στο οποίο καλείται η μέθοδος
    virtual Document *Clone(void)=0;
};

Ορίζουμε τις concrete υποκλάσεις της Document για κάθε είδος αρχείου που χειρίζεται η εφαρμογή μας. Σε κάθε μία ορίζουμε και μία μέθοδο Clone() που επιστρέφει δείκτη σε ένα αντίγραφο του εαυτού της.

//filetypes.h

#include "document.h"

//concrete υποκλάση της Document για html αρχεία
class HtmlFile: public Document {
public:
    //Υλοποιήσεις των μεθόδων του interface
    void Preview(void);
    //...
    //...
   
    //Μέθοδος κλωνοποίησης. Επιστρέφει αντίγραφο
    //του instance στο οποίο καλείται η μέθοδος
    Document *Clone(void) {
        return new HtmlFile(*this);
    }
};

//concrete υποκλάση της Document για html αρχεία
class XHtmlFile: public Document {
public:
    //Υλοποιήσεις των μεθόδων του interface
    void Preview(void);
    //...
    //...
   
    //Μέθοδος κλωνοποίησης. Επιστρέφει αντίγραφο
    //του instance στο οποίο καλείται η μέθοδος   
    Document *Clone(void) {
        return new XHtmlFile(*this);
    }
};

Παρατηρήστε την κάθε Clone(). Δημιουργούμε ένα καινούργιο concrete αντικείμενο καλώντας τον copy constructor για να αντιγραφτεί το instance στο οποίο έγινε η κλήση. Εδώ θέλει λίγη προσοχή. Χάρην παραδείγματος δεν ορίσαμε copy constructors στις κλάσεις HtmlFile και XHtmlFile οπότε θα γίνει shallow copy των instances, δηλαδή θα αντιγραφούν και πιθανοί δείκτες που αποθηκεύει το αντικείμενο χωρίς να αντιγραφούν όμως τα αντικείμενα στα οποία δείχνουν. Σε τέτοιες περιπτώσεις, που είναι και η πλειοψηφία, θα πρέπει να ορίζεται copy constructor που κάνει deep copy των αντικειμένων.

Στη συνέχεια δημιουργούμε και μία κλάση που θα δημιουργεί αντικείμενα τύπου Document. Ας την ονομάσουμε DocumentFactory. Για να καθοριστεί ο concrete τύπος των αντικειμένων που θα δημιουργούνται η DocumentFactory θα αποθηκεύει ένα πρωτότυπο instance της concrete κλάσης στο private κομμάτι. Η μέθοδος CreateDocument() θα δημιουργεί ένα αντικείμενο τύπου Document καλώντας την Clone() στο πρωτότυπο instance. Οπότε, με αυτόν τον τρόπο κάθε φορά που θα καλείται η CreateDocument() θα δημιουργείται ένα αντικείμενο του ίδιου τύπου με το πρωτότυπο.

//document.h

//Δημιουργεί νέα Documents αντιγράφοντας το prototype
class DocumentFactory {
    //αποθηκεύει το πρωτότυπο instance
    Document *_prototype;
public:
    //Δημιουργεί και ρυθμίζει το factory
    //με το πρωτότυπο που θα χρησιμοποιείται
    DocumentFactory(Document *proto) {
        _prototype=proto;
    }
    
    //Copy Constructor
    //Σε περίπτωση που αλλάζει η default ρύθμιση
    DocumentFactory &DocumentFactory(const DocumentFactory &factory) {
        _prototype=factory.prototye;
        delete factory.prototype;
    }
    
    //destructor
    ~DocumentFactory() {
        delete _prototype;
    }
    
    //Δημιουργία νέου Document
    //κλωνοποιώντας το πρωτότυπο
    Document *CreateDocument(void) {
        return _prototype->Clone();
    }
};

Οπότε, όταν ξεκινάει η εφαρμογή του editor και αφού διαβαστεί η default ρύθμιση για το είδος των νέων αρχείων θα μπορούσαμε να κάνουμε το εξής:

Registry *reg=Registry::getSingleton();

DocumentFactory *factory=reg->getDocumentFactory();
if(reg->getDefaultType()==std::string("html"))
    factory=new DocumentFactory(new HtmlFile());
else
    factory=new DocumentFactory(new XHtmlFile());

Σε αυτό το code fragment κάνουμε χρήση του Registry που ορίσαμε στο προηγούμενο design pattern που καλήψαμε, το Singleton, δείτε εδώ.

Το Prototype Design Pattern χρησιμοποιείται κυρίως σε άλλα desgin patterns που επιλύουν προβλήματα δημιουργίας αντικειμένων όπως το Abstract Factory. Οι σημαντικότερες επιδράσεις του Prototype είναι μείωση του αριθμού των κλάσεων και αύξηση των δυνατοτήτων δυναμικής ρύθμισης συμπεριφοράς όπου εφαρμόζεται.

Αυτό σχετικά με το Prototype και την υλοποίησή του σε C++. Την επόμενη φορά θα προσπαθήσω να καλύψω άλλο ένα pattern που λύνει προβλήματα δημιουργίας αντικειμένων.

Share/Bookmark

Comments

Alex Z. said:

Μπράβο Jss! Πολύ όμορφη περιγραφή για το συγκεκριμένο σχεδιαστικό πρότυπο. Δυστυχώς η Ελληνική βιβλιογραφία είναι πολύ περιορισμένη στο αντικείμενο αυτό.

Πρόσφατα έφτιαξα ένα δικτυακό τόπο αφιερωμένο αποκλειστικά για τα σχεδιαστικά πρότυπα στα Ελληνικά: http://www.designpatterns.gr

Τα μέλη μπορούν να δημοσιεύουν στα blogs...

Ελπίζω ότι η ελληνική κοινότητα θα δείξει αναγνωρίσει την αξία τους...

Χαιρετισμούς,

Alex

# Μαρτίου 7, 2010 7:23 μμ
Leave a Comment

(required) 

(required) 

(optional)

(required) 

Submit