[Μαθαίνοντας Design Patterns] Factory
Συνεχίζοντας λοιπόν αυτή τη μικρή αναζήτηση γύρω από τα Design Patterns, σήμερα θα μιλήσουμε για το Factory. Πριν αναφέρω τα χαρακτηριστικά του pattern, ακολουθώντας τον τρόπο που ξεκίνησα το προηγούμενο post μου, θα ήθελα να πω τα εξής:
Έρχεται μια άλλη φάση στην ζωή του προγραμματιστή :P , που έχοντας αποφασίσει ότι θα κάνει το βήμα να μάθει να σχεδιάζει σωστά τον κώδικά του, ψάχνει να βρει πηγές που θα του δώσουν αυτό που θέλει να μάθει με τον καλύτερο και πιο κατανοητό τρόπο. Έτσι λοιπόν έκανα και εγώ...
Αναζήτησα το Design Patterns: Elements of Reusable Object-Oriented Software. Ένα παλιό βιβλίο (94), μία έκδοση έχει βγάλει, όλοι το θεωρούν την βίβλο των design patterns. Το πήρα και εγώ με αισιοδοξία για το μέλλον. Όσο όμως προσεκτικά και αν το διάβαζα δεν μπορούσα να κάνω κτήμα μου κάθε τι που έλεγε εκεί μέσα. Ήταν απλά αδύνατο, οι πληροφορίες ήταν τόσες πολλές, οι λεπτομέριες χαοτικές και η θεωρία τόσο πλήρης (πράγματα που όντως, κάνουν ένα βιβλίο, θρησκεία) που μόνο για να έχεις μια πρώτη επαφή με Design Patterns δεν ήταν...όσο και αν είχα την ελπίδα ότι θα τα καταφέρω εύκολα και γρήγορα, τουλάχιστον σε θεωρητικό επίπεδο, τόσο μπερδευόμουν όχι από θέμα ποιότητας (που ήταν άριστη), αλλά περιεκτικότητας πληροφορίας. Ένιωθα όπως τότε που είχα πάρει το βιβλίο των KnR για να μάθω C. Δεύτερη φορά στην ζωή μου λοιπόν. Και για να μην βρεθείτε σε παρόμοια κατάσταση θα το πω φωναχτά.
ΔΕΝ ΠΑΙΡΝΟΥΜΕ CORE REFERENCE MANUAL ΣΤΗΝ ΠΡΩΤΗ ΜΑΣ ΕΠΑΦΗ ΜΕ ΚΑΠΟΙΟ ΘΕΜΑ
Αυτή η φάση του προγραμματιστή λοιπόν, τελειώνει όταν βρούμε ένα βιβλίο απλό, κατανοητό
και ταυτόχρονα να κρατάει υψηλό το επίπεδο της ποιότητας στις παρεχόμενες πληροφορίες. Ένα τέτοιο βιβλίο είναι αυτό που μου πρότεινε ο φίλος Νίκος Παλλαδινός πρόσφατα και θα ήθελα να το μοιραστώ και με εσάς, μιας και το βρήκα τρομακτικά ενδιαφέρον (σε σημείο να απειλήσει λοιπές υποχρεώσεις μου) και πολύ έξυπνα γραμμένο. Και ο τίτλος αυτού Head First Design Patterns με κώδικα σε Java και με ένα εξώφυλλο που έχω ιδιαίτερη αδυναμία. Heheheh. Ακολουθώντας ένα μείγμα εμπειριών απο διάφορα βιβλία συνεχίζω να δίνω μια μικρή περιγραφή στα patterns για να συζητάμε κάθε φορά και να μαθαίνουμε μέσα από την συζήτηση.
Design Pattern 2#: Factory
Με το Factory pattern μπορούμε να δημιουργήσουμε μία διεπαφή η οποία θα μας επιστρέφει το νέο αντικείμενο που θέλουμε αλλά το είδους του αντικειμένου που θα επιστραφεί θα καθορίζεται στις υποκλάσεις. Αρκετά με την θεωρία. Μία πρόταση φτάνει. Ας πάμε κατευθείαν στο παράδειγμα για να δούμε πώς λειτουργεί το Factory pattern και που χρησιμοποιείται.
Το αγαπημένο μου θέμα. Πείτε ότι θέλουμε να φτιάξουμε ένα κατάστημα που να φτιάχνει σουβλάκια (no comments pls...show some respect στο τρόφιμο :P:P..hehe). Θέλω μία τέτοια μέθοδο δηλαδη
Souvlaki orderSouvlaki()
{
Souvlaki souvlaki = new Souvlaki();
souvlaki.etoimase();
souvlaki.tilikse();
souvlaki.paketarise();
return souvlaki;
}
Έστω λοιπόν ότι θέλω να προσθέσω κώδικα που να ελέγχει τι τύπο σουβλάκι θέλω να φτιάξω με βάση την περιοχή (τοπική παραλλαγή), θα έχω κάτι σαν αυτό:
Souvlaki orderSouvlaki(String perioxi)
{
Souvlaki souvlaki;
if (perioxi.Equals("notia_ellada"))
souvlaki = new KanonikoSouvlaki();
else if (perioxi.Equals("voreia_ellada"))
souvlaki = new VoreiasElladasSouvlaki();
else if (perioxi.Equals("kipriako"))
souvlaki = new KipriakiPitaSouvlaki();
souvlaki.etoimase();
souvlaki.tilikse();
souvlaki.paketarise();
return souvlaki;
}
Αν το κατάστημα δεν πουλάει πολύ σουβλάκια τύπου βόρειας ελλάδας, για να μην έχει απώλεια κέρδους θα πρέπει να σταματήσει την παρασκευή τους. Αν βλέπει ότι του ζητάνε σουβλάκι τύπου Ιωαννίνων κάτι θα έπρεπε να κάνει, οπότε:
Souvlaki orderSouvlaki(String perioxi)
{
Souvlaki souvlaki;
if (perioxi.Equals("notia_ellada"))
souvlaki = new KanonikoSouvlaki();
else if (perioxi.Equals("voreia_ellada"))
souvlaki = new VoreiasElladasSouvlaki();
else if (perioxi.Equals("kipriako"))
souvlaki = new KipriakiPitaSouvlaki();
else if (perioxi.Equals("ioannina"))
souvlaki = new GianniotikoSouvlaki();
souvlaki.etoimase();
souvlaki.tilikse();
souvlaki.paketarise();
return souvlaki;
}
Είναι προφανές ότι όταν θέλουμε να ρυθμίσουμε ποια κλάση θα αρχικοποιείται, υπάρχει σοβαρό πρόβλημα. Ο κώδικάς μας δεν είναι καθόλου modular. Έχουμε ένα στοιχείο όμως!!!! Ξέρουμε τώρα τι πρέπει να κάνουμε encapsulate!!! Καταρχάς φτιάχνουμε μια καινούρια μέθοδο αφαιρώτας το παραπάνω κομμάτι βάζοντας το σε μία μέθοδο που αναλαμβάνει αυτό ακριβώς το σημείο. Και έχουμε όνομα να της δώσουμε...SouvlakiFactory.
public class SouvlakiFactory
{
public Souvlaki createSouvlaki(String perioxi)
{
Souvlaki souvlaki;
if (perioxi.Equals("notia_ellada"))
souvlaki = new KanonikoSouvlaki();
else if (perioxi.Equals("voreia_ellada"))
souvlaki = new VoreiasElladasSouvlaki();
else if (perioxi.Equals("kipriako"))
souvlaki = new KipriakiPitaSouvlaki();
else if (perioxi.Equals("ioannina"))
souvlaki = new GianniotikoSouvlaki();
return souvlaki;
}
}
Μα τι κάναμε τώρα; Πήραμε το πρόβλημα και το μεταθέσαμε. Άλλαξε κάτι; Ναι! Άλλαξε κάτι πολύ σημαντικό. Το SouvlakiFactory μπορεί να έχει πολλούς clients και όχι μόνο την orderSouvlaki. Μπορεί να υπάρχει άλλη κλάση που να υπολογίζει την τιμή, ή να κανονίζει την παράδοση εφόσον υπάρχει διανομή κατοίκων. Έτσι, τώρα έχουμε ένα σημείο να κάνουμε αλλαγές. Ορίστε πως έχει τώρα ο κώδικάς μας.
SouvlakiFactory factory;
public Souvlantzidiko(SouvlakiFactory factory)
{
this.factory = factory;
}
Souvlaki orderSouvlaki(String perioxi)
{
Souvlaki souvlaki;
souvlaki = factory.createSouvlaki(perioxi);
souvlaki.etoimase();
souvlaki.tilikse();
souvlaki.paketarise();
return souvlaki;
}
Έστω τώρα ότι το κατάστημά μας επεκτείνεται σε όλη την Ελλάδα και ο κώδικας είναι παντού ο ίδιος. Παρόλα αυτά, όταν θα πάμε στην Σπάρτη, υπάρχει ένας ειδικός τύπος για Σουβλάκι Νότιας Ελλάδας(σπεσιαλ σαλτσα) που αρέσει στους ντόπιους. Δεν θα έπρεπε να έχω λοιπόν ένα ειδικό factory όπως το παρακάτω;
SpartiFactory spFactory = new SpartiFactory();
Souvlantzidiko spartiSouvlantzidiko = new Souvlantzidiko(spFactory);
spartiSouvlantzidiko.orderSouvlaki("notia_ellada");
Το πρόβλημα εδώ όμως είναι ότι δεν έχουμε τον έλεγχο το ποιος θα κάνει create τι. Πχ. κάποιος κάνει την δική επιχείρηση χρησιμοποιώντας το δικό σας factory. Χρειαζόμαστε μία ακόμα πινελιά για να έχουμε το Factory μας δεμένο με το κάθε κατάστημα. Παρατηρείστε το σχόλιο στην abstract κλάση
public abstract class Souvlantzidiko
{
public Souvlantzidiko(SouvlakiFactory factory)
{
}
Souvlaki orderSouvlaki(String perioxi)
{
Souvlaki souvlaki;
souvlaki = createSouvlaki(perioxi);
souvlaki.etoimase();
souvlaki.tilikse();
souvlaki.paketarise();
return souvlaki;
}
//To factory object υπάρχει εδώ. Αυτό ακριβώς το σημείο
//είναι εκείνο στο οποίο αποφασίζει η κάθε υποκλάση την υλοποίηση που θέλει.
protected abstract Souvlaki createSouvlaki(String perioxi);
}
Το factory pattern όπως το αναλύσαμε εδώ μπορεί να χρησιμοποιηθεί σε πάρα πολλές καταστάσεις και αν το καταλάβουμε καλά, θα μας λύσει τα χέρια σε πολλά σημεία. Στο .NET υπάρχει υλοποιημένο σε πολλές λειτουργίες της βιβλιοθήκης εξυπηρετώντας κάθε φορά αυτήν την λειτουργικότητα. Για παράδειγμα, για να διαβάσετε/στείλετε δεδομένα στο δίκτυο με βάση ένα uri μπορείτε να χρησιμοποιήσετε την System.Net.WebRequest. Αν ξέρετε λίγο από system programming κάθε πρωτόκολλο ακολουθεί αυστηρά τις δικές του συμβάσεις επικοινωνίας, τόσο στην δημιουργία sockets όσο και στα μηνύματα που ανταλάσσονται κλπ. Η WebRequest.Create δημιουργεί ένα αρμόδιο αντικείμενο με βάση το URI που θα του δώσουμε επιστρέφοντας μας το κατάλληλο instance που θα μας επιτρέψει την επικοινωνία. Το uri μπορεί να είναι ένα από τα τρία (default) "http://", "https://", and "file://".
Enjoy, stay tuned!!!