Τμήμα Πληροφορικής & Τηλεπικοινωνιών, ΕΚΠΑ Τεχνολογία Λογισμικού 8ο Εξάμηνο 2018 19 Πρότυπα σχεδίασης ΙI Δρ. Κώστας Σαΐδης saiko@di.uoa.gr
Περιεχόμενα Δομικά πρότυπα σχεδίασης Πρότυπα σχεδίασης συμπεριφοράς
Δομικά πρότυπα σχεδίασης Πρότυπα σχετικά με τη δομή και τη συσχέτιση κλάσεων 3
Περιεχόμενα 1. Adapter Wrapper 2. Bridge 3. Composite 4. Decorator 5. Facade 6. Proxy 4
Adapter Wrapper Σκοπός: Να κάνει μια κλάση συμβατή με κάποιο interface που αναμένει ένας χρήστης της Μια υπάρχουσα κλάση γίνεται συμβατή με ένα νέο interface 5
Παράδειγμα java.lang.runnable public interface Runnable { public void run() java.util.concurrent.callable public interface Callable<V> { public V call() throws Exception 6
public class RunnableAsCallable implements Callable<Void> { private final Runnable runnable; public RunnableAsCallable(Runnable runnable) { this.runnable = runnable; @Override public Void call() throws Exception{ runnable.run(); return null; 7
Συζήτηση Μηχανισμός για να κάνουμε δύο ξεχωριστά συστατικά με διαφορετικό σχεδιασμό να λειτουργήσουν μαζί, αφού έχουν υλοποιηθεί. "Ντύνουμε" ένα interface με τα ρούχα ένός άλλου. 8
Bridge Σκοπός: Να διαχωρίσουμε την αφαίρεση interface από την υλοποίηση class, ώστε να χρησιμοποιούνται ανεξάρτητα. 9
Παράδειγμα Δύο βασικές έννοιες Thread scheduler Platform 10
Παράδειγμα Subclassing ThreadScheduler PreemptiveThreadScheduler TimeSlicedThreadScheduler UnixPTS WindowsPTS UnixTSTS WindowsTSTS 11
Παράδειγμα Subclassing ThreadShceduler PreemptiveThreadScheduler TimeSlicedThreadScheduler UnixPTS WindowsPTS UnixTSTS WindowsTSTS JVM_PTS JVM_TSTS 12
Παράδειγμα Bridge ThreadScheduler PreemptiveThreadScheduler ThreadScheduler_Implementation TimeSlicedThreadScheduler UnixPTS WindowsPTS JVM_PTS 13
Συζήτηση Έμφαση στη σύνθεση και όχι στην κληρονομικότητα Χρήση δύο ιεραρχιών κληρονομικότητας: μία "δημόσια" και μία "ιδιωτική" Οι "δημόσιες" αφαιρέσεις χρησιμοποιούν τις "ιδιωτικές" υλοποιήσεις Οι δύο ιεραρχίες μπορούν να εξελιχθούν ανεξάρτητα μεταξύ τους 14
Composite Σκοπός: Να επιτρέψουμε σε μια κλάση χρήστη να χειρίζεται απλά και σύνθετα αντικείμενα με ομοιόμορφο τρόπο 15
Παράδειγμα interface Resolver { File resolve(string text) throws NotFoundException; class LocalFileResolver implements Resolver {... class URLResolver implements Resolver {... 16
class CompositeResolver implements Resolver { private final Resolver[] resolvers; CompositeResolver(Resolver... r) { this.resolvers = r; File resolve(text) throws NotFoundException { File f = null; for(resolver: resolvers) { try { f = resolver.resolve(text); catch(notfoundexception nfe) {.. if (f == null) { throw new NotFoundException(text); else { return f; 17
Decorator Σκοπός: Να προσθέσουμε επιλεκτικές και προαιρετικές δυνατότητες σε ένα αντικείμενο χωρίς κληρονομικότητα 18
Παράδειγμα Τέσσερις βασικές έννοιες Window Horizontal Scrollbar Vertical Scrollbar Border 19
Παράδειγμα Subclassing Window +draw() Window_With_Vertical_Scrollbar Window_With_Border Window_With_Horizontal_Scrollbar Window_With_Vertical_and_Horizontal_Scrollbar Window_With_Vertical_and_Horizontal_Scrollbar_and_Border 20
Παράδειγμα Decorator «interface» LCD +draw() Window Decorator +draw() +draw() Border VerticalSB HorizontalSB 21
Widget w = new BorderDecorator( new HorizontalScrollBarDecorator( new VerticalScrollBarDecorator( new Window("Title") ) ) ); w.draw(); 22
Συζήτηση Παρέχει μια πιο ευέλικτη εναλλακτική για την προσθήκη λειτουργικότητας σε μια κλάση σε σχέση με το subclassing κληρονομικότητα Recursive wrapping 23
Πραγματικό παράδειγμα Java IO OutputStream os = new BufferedOutputStream( new FileOutputStream("/path/to/file") ); 24
Facade Σκοπός: Να παρασχεθεί ένα απλό interface σε ένα σύνθετο υποσύστημα. 25
Παράδειγμα class VideoManagerFacade { File convert(string filename, String format) { VideoFile file = new VideoFile(fileName); Codec srccodec = CodecFactory.of(file); Codec destcodec; if (format.equals("ogg")) { destcodec = new OggCompressionCodec(); else { destcodec = new MPEG4CompressionCodec(); VideoFile buffer = BitrateReader.read(file, srccodec); VideoFile tmp = BitrateReader.convert(buffer, destcodec); File result = (new AudioMixer()).fix(tmp); return result; 26
Proxy Σκοπός: Να προσφέρει έναν ενδιάμεσο για ένα άλλο αντικείμενο, ελέγχοντας την πρόσβαση σε αυτό 27
Εφαρμογές Οκνηρή αρχικοποίηση virtual proxy Έλεγχος πρόσβασης access proxy Εκτέλεση απομακρυσμένων υπηρεσιών remote proxy Χρήση ενδιάμεσης μνήμη caching Καταγραφή ενεργειών logging proxy 28
Παράδειγμα class Router { void setup() { route("/index.html", new PublicResource()) route("/cart.html", new ProtectedResource()) void service(httprequest req, HttpResponse res) { MVCResource res = getresourcefor(req) LoggingProxy proxy = new LoggingProxy(res) proxy.doservice(req, res) 29
class LoggingProxy extends MVCResource { private static final Logger log =... private final MVCResource resource; LoggingProxy(MVCResource resource) { this.resource = resource; User createuser(httprequest req) { return res.createuser void doservice(httprequest req, HttpResponse res) { log.log("starting service of $resource") long t = System.currentTimeMillis() try { resource.doservice(req, res) finally { t = System.currentTimeMillis() t log.log("ending service of $resource (duration $t ms)") 30
Πρότυπα σχεδίασης συμπεριφοράς Πρότυπα σχετικά με τη συμπεριφορά / επικοινωνία των αντικειμένων 31
Περιεχόμενα 1. Visitor 2. Chain of responsibility 3. Command 4. Mediator 5. Memento 6. Strategy 7. Null Object 32
Επίσης 8. Iterator / Iterable 9. Observer / Observable 10. Callback / Future / Promise 33
Visitor Σκοπός: Να διαχωρίσει τον αλγόριθμο από τη δομή δεδομένων. Η "λογική" της επίσκεψης στα στοιχεία της δομής traversal διαχωρίζεται από τη δομή αυτή καθ' αυτή. Το έχουμε δει ξανά στην εισαγωγή για τα πρότυπα σχεδίασης 34
Chain of responsibility Σκοπός: Να περάσει ένα αίτημα μέσω μιας αλυσίδας αντικειμένων 35
Client Request Processing element Processing element Processing element Processing element 36
Παράδειγμα Το παράδειγμα του Composite προτύπου από το προηγούμενο μάθημα χρησιμοποιεί το Chain of responsibility 37
Πραγματικό παράδειγμα Servlet filters Restlet filters 38
Command Σκοπός: Να "ενθυλακώσει" ένα αίτημα ως αντικείμενο, διαχωρίζοντας τον αποστολέα από τον παραλήπτη 39
Παράδειγμα java.lang.runnable java.util.concurrent.callable 40
Mediator Σκοπός: Να δημιουργήσουμε έναν "ενδιάμεσο" που "γνωρίζει" πώς αλληλεπιδρούν διάφορα αντικείμενα Χρήσιμο για σχέσεις N N 41
Client Producer Mediator Consumer +notify() +store() +retrive() +notify() 42
Memento Σκοπός: Να "εξωτερικεύσουμε" με ασφαλή τρόπο την εσωτερική κατάσταση ενός αντικειμένου ώστε να μπορούμε να την επαναφέρουμε αργότερα. Για την υλοποίηση λειτουργιών undo ή rollback 43
Παράδειγμα class Memento { private final String state; public Memento(String state) { this.state = state; public String getstate() { return state; 44
class Originator { private String state;...//more fields that "depend" on the state private void setstate(string state) { this.state = state; //change fields depending on the state public Memento save() { return new Memento(state); public void restore(memento m) { setstate(m.getstate()); 45
Strategy Σκοπός: Να αναπαραστήσουμε μια οικογένεια αλγορίθμων με ομοιόμορφο τρόπο ώστε να τους εναλλάσσουμε 46
Παράδειγμα class Client { String id //... BillingStrategy billingplan interface BillingStrategy { //Fundamental billing functions class EnterprisePlan implements BillingStrategy { //Billing details of the enterprise plan class SimplePlan implements BillingStrategy { //Billing details of the simple plan 47
Null object Σκοπός: Να υλοποιήσουμε με ομοιόμορφο τρόπο το NO OP do nothing Αποφεύγουμε τους ελέγχους για null διατηρώντας τη λογική του αλγορίθμου απλή και καθαρή 48
Παράδειγμα Ένα ενδεχόμενο FreePlan στην περίπτωση του BillingStrategy παραδείγματος μπορεί να υλοποιηθεί ως Null object μια υλοποίηση του BillingStrategy με μεθόδους που δεν κάνουν τίποτα! 49
Iterator / Iterable Σκοπός: Να μοντελοποιήσουμε την επανάληψη ή τη δυνατότητα αυτής 50
Iterator java.util.iterator interface Iterator<E> { boolean hasnext(); E next(); void remove(); //we don't care about this in the class Χρήση while(iterator.hasnext()) { Element e = iterator.next(); //do something with e 51
Iterable java.lang.iterable interface Iterable<E> { Iterator<E> iterator(); 52
Pull paradigm Ο χρήστης/client του Iterable κάνει pull για την επόμενη τιμή καλεί τις hasnext/next 53
Observer / Observable Σκοπός: Να ενημερωνόμαστε για τις αλλαγές στην κατάσταση ενός αντικειμένου Σχέση 1 Ν 1 observable, πολλοί observers 54
Observable Subject java.util.observable class Observable { void addobserver(observer o); void deleteobserver(observer o); boolean haschanged(); void notifyobservers(); void notifyobservers(object arg); 55
Observer java.util.observer interface Observer { void update(observable o, Object arg); 56
Push paradigm Ο χρήστης/client του Observable ενημερώνεται για την επόμενη τιμή το Observable κάνει push Publish/Subscribe 57
Παράδειγμα Το knockout.js υποστηρίζει το Observer/Observable 58
Callback Σκοπός: Να εκτελέσουμε ένα κομμάτι κώδικα αφού έχει ολοκληρωθεί ένας υπολογισμός σύγχρονα ή ασύγχρονα 59
Παράδειγμα interface Callback { void call() Θα μπορούσε να είναι και Runnable ή Callable 60
abstract class Task { abstract void execute() void executeand(callback callback) { execute() callback.call() 61
Πραγματικό παράδειγμα Τα Javascript callbacks π.χ. σε μια AJAX κλήση 62
Callback hell getdata = function(param, callback){ $.get('http://example.com/get/'+param, function(responsetext){ callback(responsetext); ); getdata(0, function(a){ getdata(a, function(b){ getdata(b, function(c){ getdata(c, function(d){ getdata(d, function(e){ //... ); ); ); ); ); 63
Future / Promise Σκοπός: Να διαχειριστούμε το αποτέλεσμα ενός υπολογισμού a value that will eventually become available με ομοιόμορφο τρόπο, ανεξάρτητα του αν ο υπολογισμός γίνεται σύγχρονα ή ασύγχρονα 64
Τρεις καταστάστεις Pending not yet available Fulfilled with an optional value Rejected due to an error or a timeout, with an optional value Εκτέλεση Thread pool ή Event loop 65
Διαφορές ανά γλώσσα ή framework Future, Promise, Deferred, Delay 66
Συνήθως Future Μια read only αναφορά σε μια τιμή που δεν έχει ακόμα υπολογιστεί o χρήστης του Future δεν έχει έλεγχο στην τιμή που θα προκύψει. Promise CompletableFuture Μια single assignment μεταβλητή για την τιμή του Future o χρήστης του Promise μπορεί να θέσει άπαξ την τιμή που θα προκύψει. 67
Promise = a future with a public set method 68
Future στη Java Package java.util.concurrent interface ExecutorService { Future<V> submit(callable<v> callable) Το ExecutorService είναι συνήθως κάποιο ThreadPool 69
interface Future<V> { //Attempts to cancel execution of this computation. boolean cancel(boolean mayinterruptifrunning); //Waits if necessary for the computation to complete, //and then retrieves its result. V get(); //Waits if necessary for at most the given time for //the computation to complete, and then retrieves //its result, if available. V get(long timeout, TimeUnit unit) //Returns true if this computation was cancelled before it //completed normally. boolean iscancelled() //Returns true if this computation completed. boolean isdone() 70
Παράδειγμα class Bulk { private ExecutorService executor = //a thread pool private List<Future> futures = [] void add(final Job job) { Future f = executor.submit(new Callable<Void>() { @Override Void call() throws Exception { job.execute(); ); futures.add(f); void execute() { for(future f: futures) { f.get(); //wait for all futures to complete 71
Χρήση Bulk bulk = new Bulk(); bulk.add(job1); bulk.add(job2); bulk.add(job3); bulk.add(job4); bulk.add(job5); bulk.add(job6); bulk.execute(); 72
Promise στην Javascript ES6 var handlerfunction = function(resolve, reject) { //resolve is the function to call in case //of successful completion //reject is the function to call in case of //failure ; var promise = new Promise(handlerFunction); promise. then(somefunction). //gets the resolved value catch(errorfunction); //gets the rejected value 73
Με το νέο συντακτικό var promise = new Promise((resolve, reject) => { try { //perform a task (usually asynchronous) resolve(task.result); catch(e) { reject(e); ); 74
Πραγματικό παράδειγμα JQuery.ajax(...) and new stanard fetch facility return a promise function fetchjsondata(){ return fetch('http://example.com/data.json'). then(response => response.json()). catch(error => console.error(error)); 75
Async / await ES6 language facility that makes asynchronous calls appear like they are synchronous async marks a function that performs an asynchronous call and returns a promise await waits for the completion of the underlying Promise resolved or rejected and is used inside async functions 76
Example async function processjsondata() { try { const json = await fetchjsondata(); // project json data catch(error) { alert(error); 77