{"id":9614,"date":"2012-11-12T14:40:53","date_gmt":"2012-11-12T13:40:53","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=9614"},"modified":"2012-11-12T14:41:15","modified_gmt":"2012-11-12T13:41:15","slug":"introduzione-ai-design-pattern","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/introduzione-ai-design-pattern\/","title":{"rendered":"Introduzione ai Design Patterns"},"content":{"rendered":"<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/11\/oop-design-patterns-objective-c.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/11\/oop-design-patterns-objective-c.png\" alt=\"OOP Design Patterns Objective-c\" title=\"oop-design-patterns-objective-c\" width=\"200\" height=\"100\" class=\"alignleft size-full wp-image-9615\" \/><\/a>Su questo sito lo sforzo di tutti gli autori \u00e8 quello di scrivere articoli che non siano semplici spezzoni di codice da copiare\/incollare nei propri progetti, ma che siano d&#8217;aiuto nel migliorare la qualit\u00e0 del proprio codice, perch\u00e9 del codice scritto bene se non funziona si correggere, ma del codice scritto male se non funziona \u00e8 davvero un bel guaio.<br \/>\nAbbiamo parlato in passato di teoria della programmazione ad oggetti (<a href=\"http:\/\/www.devapp.it\/wordpress\/masterdetail-the-right-way-impariamo-a-pensare-ad-oggetti.html\"> qui <\/a>) metre oggi introdurr\u00f2 l&#8217;argomento &#8220;design pattern&#8221; e di come questi possano aiutare a migliorare la qualit\u00e0 generale dei nostri programmi.<br \/>\nVedremo in particolare un primo esempio di design pattern, molto semplice ma che, a mio avviso, \u00e8 esplicativo dell&#8217;approccio che sta dietro tutti gli altri.<!--more--><\/p>\n<h4>La storia<\/h4>\n<p>In principio c&#8217;era l&#8217;OOP&#8230;ma davvero &#8220;in principio&#8221;! Il primo linguaggio orientato agli oggetti risale infatti al 1967, non ascoltate quei professori che citano l&#8217;OOP come se fosse l&#8217;ultima frontiera in termini di paradigmi di programmazione&#8230;<\/p>\n<p>In principio c&#8217;era l&#8217;OOP, dicevo, e i suoi cardini principali:<\/p>\n<ul>\n<li>Incapsulamento<\/li>\n<li>Ereditariet\u00e0<\/li>\n<li>Polimorfismo<\/li>\n<\/ul>\n<p>Sfruttando sapientemente queste caratteristiche e con una buona dose di rigore si riuscivano e si riescono a scrivere programmi ben strutturati, chiari nella loro architettura e, soprattutto, facili da mantenere.<\/p>\n<p>La programmazione ad oggetti, in breve OOP, non nasce n\u00e9 con lo scopo di creare programmi pi\u00f9 veloci n\u00e9 per svolgere compiti che non possano essere svolti con la programmazione strutturata. Sopratutto su questo secondo punto fin dall&#8217;inizio erano tutti d&#8217;accordo: si sapeva bene infatti che tutti i linguaggi di programmazione in questo senso erano &#8220;Touring compatibili&#8221; che \u00e8 un bel parolone (vedi macchina di touring corso di C <a href=\"http:\/\/www.devapp.it\/wordpress\/1-storia-della-programmazione.html\" target=\"_blank\">qui<\/a>) per dire in pratica che tutti i linguaggi di programmazione come li conosciamo sono in grado di calcolare le medesime funzioni in pi\u00f9 o meno tempo, ma in ogni caso hanno tutti lo stesso potenziale.<\/p>\n<p>Ma allora serve realmente a qualcosa la OOP oppure \u00e8 solo fumo negli occhi?<\/p>\n<p>Noi del club &#8220;OOP: un amore a prima vista&#8221; \ud83d\ude09 diciamo che &#8220;s\u00ec la OOP \u00e8 l&#8217;invenzione migliore dopo il linguaggio C&#8221; (e trascuriamo volutamente il C++)<\/p>\n<p>Lo scopo della OOP \u00e8 quello di aiutare l&#8217;uomo, con le sue ridotte capacit\u00e0, a scrivere codice sorgente facile da capire, modificare, da testare e da mantenere. E ribadisco che \u00e8 un ausilio per l&#8217;uomo e non per la macchina, perch\u00e9 se \u00e8 vero che il computer &#8220;gradirebbe&#8221; meglio un piccolissimo file in assembly piuttosto che 10mila righe di codice in obj-c \u00e8 anche vero che il il computer impiega qualche secondo in pi\u00f9 per la fase di compilazione, ma un uomo ci metterebbe anni a scrivere un programma intero in assembly<\/p>\n<p>Ma la OOP da sola fornisce solo gli strumenti di base, non \u00e8 la bacchetta magica per realizzare dei programmi ben strutturati. Non soprende quindi che nel corso degli anni diversi GURU abbiamo poi proposto le loro tecniche di scrittura e organizzazione del software, fornendo consigli e linee guida in tal senso.<\/p>\n<p>Non vorrei <em>spoilerare<\/em> il seguito dell&#8217;articolo e quindi cito come esempio &#8220;Single point of responsability&#8221;, che non \u00e8 qualcosa che sta dentro la OOP e neache un design pattern, \u00e8 un&#8217;idea che bisogna avere in testa quando si progetta l&#8217;architettura di un software. L&#8217;idea \u00e8 che in un software deve essere facilmente identificabile il responsabile di qualcosa. \u00c8 un consiglio, \u00e8 una buona pratica da seguire.<\/p>\n<p>Io l&#8217;ho imparata a mie spese diversi (!!) anni fa, quando purtroppo non esisteva devAPP a spiegarmi le cose e non avevo mai badato al concetto di responsabilit\u00e0. Avevo sviluppato un piccolo gestionale per l&#8217;emissione di fatture\/bolle\/preventivi (erano circa una dozzina di tipi di documenti diversi) e io avevo inserito il codice per la formattazione dei report di stampa in 12 punti diversi&#8230;ovviamente quando il cliente mi chiamava per aggiungere un riga qui o un logo l\u00ec era una tragedia. Quella \u00e8 stata l&#8217;esperienza che mi ha insegnato il valore di &#8220;single point of responsability&#8221;.<\/p>\n<p>Con una buona conoscenza della OOP e cercando di seguire questa e diverse altre linee guida \u00e8 pi\u00f9 facile scrivere programmi ben strutturati.<\/p>\n<p>In questo contesto nascono e vivono i <strong>Design Pattern<\/strong>.<\/p>\n<p>La loro data di nascita coincide con la data di pubblicazione del libro che \u00e8, a tutt&#8217;oggi, ritenuta la bibbia dei design patter (<a href=\"http:\/\/www.amazon.it\/gp\/product\/0201633612\/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&#038;tag=de0d-21&#038;linkCode=as2&#038;camp=3370&#038;creative=23322&#038;creativeASIN=0201633612\" target=\"_blank\">amazon<\/a>) pubblicato nel 1997 da 4 ragazzi che si fanno chiamare &#8220;Gang of Four&#8221;. Uno dei quali (<a href=\"http:\/\/www.bignerdranch.com\/instructors\/hillegass.shtml\" target=\"_blank\">http:\/\/www.bignerdranch.com\/instructors\/hillegass.shtml<\/a>) ha lavorato alcuni anni in Apple come docente su Cocoa (nel perio di passaggio next-step\/apple ed \u00e8 per questo e per la loro oggettiva qualit\u00e0 che i libri della &#8220;Gang of four&#8221; sono ritenuti, soprattutto nel mondo mac, dei must.<\/p>\n<p>La definizione formale di design patter \u00e8 &#8220;una soluzione progettuale generale a un problema ricorrente&#8221;, che in pratica significa fornire una possibile soluzione al problema di come creare l&#8217;architettura delle classi quando ci si trova davanti ad un problema gi\u00e0 noto.<\/p>\n<p>Magari starai dicendo &#8220;Ma la mia app per iphone \u00e8 super cool, scialla di brutto, prende il testo scritto da me con le k, i xk\u00e9 e i tvb e magikamente lo fa diventare come vuole il vekkio prof di itaGLIano&#8230;come fanno quattro cowboy del 1997 a risolvere i miei problemi con il codice? E poi se c&#8217;ho problemi io scrivo <em>a devAPP<\/em>!&#8221;<\/p>\n<p>Ecco, se parli cos\u00ec magari qualche problema ce l&#8217;hai per davvero&#8230;ma il concetto \u00e8 che i design patterns sono una soluzione ad un problema <i>generico<\/i>, ma ricorrente, che si presenta durante la scrittura di un software, indipendentemente dalla specifica implementazione.<\/p>\n<p>Problemi ai quali i desgn pattern possono dare una risposta sono ad esempio:<\/p>\n<p>&#8220;Come faccio a creare una classe il cui comportamento non sia blindato all&#8217;interno della classe stessa, ma che possa variare a seconda del contesto in cui opera?&#8221;<\/p>\n<p>oppure:<\/p>\n<p>&#8220;Ho diversi oggetti nel mio programma interessati allo stato di un altro oggetto, come posso fare per mantenerli aggiornati ed evitare l&#8217;overhead di continue richieste?&#8221;<\/p>\n<p>Ho scelto questi due esempi di proposito, perch\u00e9 la risposta fornita dalla GoFour \u00e8 in ordine &#8220;<strong>Delegate design pattern<\/strong>&#8221; e &#8220;<strong>Observer design patter<\/strong>&#8220;. Anche se i nomi non vi dicono nulla li avrete sicuramente gi\u00e0 incontrati nello sviluppo delle applicazioni iOS, il primo ad esempio nella generazione delle tabelle (UITableViewDelegate) e il secondo nella KVO (Key Value Observer).<\/p>\n<h4>Troviamo una soluzione con un design pattern.<\/h4>\n<p>Non c&#8217;\u00e8 modo migliore per far comprendere l&#8217;utilit\u00e0 di un design pattern se non quello di formulare un esempio, in questo caso mancando totalmente di fantasia propongo un esempio molto simile a quello presentato nel libro &#8220;<a href=\"http:\/\/www.amazon.it\/gp\/product\/0596007124\/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&#038;tag=de0d-21&#038;linkCode=as2&#038;camp=3370&#038;creative=23322&#038;creativeASIN=0596007124\" target=\"_blank\">Head firs design patter<\/a>&#8220;.<\/p>\n<p>Immaginatevi programmatori neo assunti in un&#8217;azienda di produzione videogiochi, la D&#038;P Game Inc. il vostro primo compito \u00e8 quello di realizzare una classe che rappresenti un oggetto del loro nuovissimo videogioco, in particolare si tratta di un&#8217;anatra.<\/p>\n<p>Il responsabile del progetto nel brief iniziale vi liquida dicendovi &#8220;devi programmare una classe che rappresenta un&#8217;anatra&#8221;.<br \/>\nVoi provetti programmatori chiedete insistentemente almeno delle specifiche un filo pi\u00f9 dettagliate e ottenete questa risposta:<\/p>\n<p>&#8220;Un&#8217;anatra! che cosa c&#8217;\u00e8 da dire di pi\u00f9? Le anatre camminano, nuotano, starnazzano&#8230;e poi si, certo che pensiamo in futuro di avere altre versioni di anatre pi\u00f9 evolute, ma ancora l&#8217;amministrazione non ha approvato i fondi e quindi non abbiamo nulla di definitivo, tu conosci la OOP, crea qualcosa che domani possiamo stravolgere completamente senza dover riscrivere il codice&#8221;.<\/p>\n<p><nota><em>Alzi la mano chi pensa che stia usando devAPP per dare sfogo a mie personalissime frustrazioni \ud83d\ude00 <\/em><\/nota><\/p>\n<p>Bene, pensate, ve la siete cavati con poco, e via a scrivere il codice.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface Duck : NSObject\r\n\r\n-(void)swim;\r\n-(void)walk;\r\n-(void)quack;\r\n-(void)display;\r\n\r\n@end\r\n<\/pre>\n<p>Questa \u00e8 la classe principale, quando ci saranno delle specifiche anatre si sfrutter\u00e0 l&#8217;ereditariet\u00e0 e si creeranno delle subclass di Duck eseguendo l&#8217;override degli specifici metodi oppure aggiungendovene altri&#8230;intendiamo quindi sfruttare l&#8217;ereditariet\u00e0 per dare versatilit\u00e0 al programma.<\/p>\n<p>Immediatamente ecco che arriva la prima modifica:vi viene detto che sono previsti due tipi di anatra nel videogioco, un germano e un&#8217;anatra comune.<\/p>\n<p>Forti della OOP create immediatamente la subclass &#8220;MallardDuck&#8221; con l&#8217;override del solo metodo display, infatti un germano nuota, cammina e starnazza proprio come tutte le altre anatre.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import \"Duck.h\"\r\n\r\n@interface MallardDuck : Duck\r\n\r\n@end\r\n<\/pre>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@implementation MallardDuck\r\n\r\n-(void)display {\r\n    \/\/Display specifico per il germano reale.\r\n}\r\n@end\r\n<\/pre>\n<p>Fin qui tutto bene, avete applicato correttamente i principi della OOP sfruttando l&#8217;ereditariet\u00e0, inoltre il programa \u00e8 coerente con l&#8217;approccio DRY (Dont Repeat Yourself) perch\u00e9 se aveste optato per creare due classi separate vi sareste trovati a dover duplicare il codice degli altri metodi: swim, walk e quack.<\/p>\n<p>Felici della vostra soluzione partecipate alla successiva riunione con il team, dove sembra prendere piede un&#8217;idea che sapete vi complicher\u00e0 il lavoro: l&#8217;autore del videogioco ha deciso che l&#8217;anatra in realt\u00e0 \u00e8 un&#8217;anatra con dei superpoteri che in certe occasioni cammina e starnazza normalmente, certe altre, invece, corre a velocit\u00e0 supersonica ed emette un richiamo agli ultrasuoni.<\/p>\n<p>Questo \u00e8 un modo bizzarro per dire che il <i>comportamento<\/i> dell&#8217;anatra pu\u00f2 cambiare a runtime, cio\u00e8 durante l&#8217;esecuzione del programma.<\/p>\n<p>La OOP vi ha preparato anche a questo, in fondo tutti sanno che un oggetto software \u00e8 definito dal suo comportamento, il quale pu\u00f2 variare in funzione dello stato interno dell&#8217;oggetto, gi\u00f9 quindi a scrivere l&#8217;implementazione della class modificata per variare il suo comportamento a runtime<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@implementation Duck \r\n    \/\/Questa variabile memorizza lo stato interno dell'anatra\r\n    int duckState;\r\n\r\n -(void)walk {\r\n     if (duckState == 0) {\r\n         \/\/cammina normalmente\r\n     } else {\r\n         \/\/cammina velocit\u00e0 supersonica.\r\n     }\r\n }\r\n\r\n -(void)quack {\r\n      if (duckState == 0) {\r\n         \/\/starnazza normalmente\r\n     } else {\r\n         \/\/starnazza agli ultrasuoni.\r\n     }\r\n }\r\n@end\r\n<\/pre>\n<p>Bene, avete cambiato la vostra classe e avete risposto alle modifiche che vi erano state chieste durante la riunione. Ma siete ancora orgogliosi e fieri del vostro codice? Non iniziate ad avere *paura* che durante la prossima riunione possa venire fuori qualche altra modifica che vi obbligher\u00e0 a cambiare tutto un&#8217;altra volta?<\/p>\n<p>Per esempio se io avessi scritto quel codice inizierei a sperare che:<\/p>\n<ul>\n<li>non mi chiedano di far starnazzare l&#8217;anatra con gli ultrasuoni mentre cammina normalmente (bisognerebbe usare due variabili separate)<\/li>\n<li>non mi chiedano di aggiungere un nuovo stato dell&#8217;anatra (dovrei rivedere tutti gli if e aggiungere una nuova condizione)<\/li>\n<li>non mi chiedano di aggiungere un nuovo tipo di camminata, che mi costringerebbe ad usare due stati solo per distinguere il tipo di camminata&#8230;<\/li>\n<li>non siano presenti tante subclass, perch\u00e9 in ogni subclass devo ripetere la logica degli stati e degli if&#8230;<\/li>\n<\/ul>\n<p>Insomma, \u00e8 bastato aggiungere un&#8217;inezia al software per renderlo gi\u00e0 molto pi\u00f9 rigido e difficile da modificare e questo non \u00e8 un bene.<\/p>\n<p>La risposta data dalla GaOfFour nel 1997 \u00e8 racchiusa in un design pattern che si chiama &#8220;Strategy&#8221;.<\/p>\n<p>Questa \u00e8 la definizione formale:<\/p>\n<p>Strategy: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy let the algorithm vary independently from clients that use it.<\/p>\n<p>Il senso \u00e8 pi\u00f9 o meno: Prendi una famiglia di algoritmi, e crea delle classi che li incapsulino e fai in modo che abbiano tutti la stessa interfaccia. Strategy ti permette di usare uno o l&#8217;altro algoritmo senza che la classe che lo utilizza ne venga in qualche modo interessata.<\/p>\n<p>Detto cos\u00ec sembra che non c&#8217;entri molto con anatre e superpoteri, ma invece c&#8217;entra eccome, basta saper guardare bene.<\/p>\n<p>Il consiglio che ci arriva da questo design pattern \u00e8 quello di evidenziare le parti del codice (gli algoritmi) che sono pi\u00f9 soggetti a variazione e spostarli su classi separate, facendo in modo che abbiano la stessa interfaccia e che quindi possano essere usati alternativamente.<\/p>\n<p>In questo caso quello che varia nel nostro codice \u00e8 proprio il verso dell&#8217;anatra e il suo modo di camminare, proviamo quindi a seguire questo consiglio per questo secondo aspetto, per il primo poi seguiremo la stessa logica.<\/p>\n<p>Creiamo quindi la classe WalkBehavior che servir\u00e0 ad incapsulare il modo di camminare dell&#8217;anatra<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface WalkBehavior : NSObject\r\n\r\n-(void)walk;\r\n@end\r\n<\/pre>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@implementation WalkBehavior\r\n-(void)walk {\r\n    NSLog(@\"standard walking\");\r\n}\r\n@end\r\n<\/pre>\n<p>e ne creiamo immediatamente una subclass per la velocit\u00e0 supersonica<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface WalkBehaviorSuperSpeed : WalkBehavior\r\n\r\n-(void)walk;\r\n@end\r\n\r\n@implementation WalkBehaviorSuperSpeed\r\n-(void)walk {\r\n    NSLog(@\"supersonic walking speed\");\r\n}\r\n@end\r\n<\/pre>\n<p>Adesso possiamo dire che l&#8217;anatra <b>ha un<\/b> (&#8220;has a&#8221; in OOP ha un significato specifico) modo di camminare, vediamo infatti nell&#8217;header della classe la presenza di una property di tipo WalkBehavior.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface Duck : NSObject \r\n\r\n@property (nonatomic, retain) WalkBehavior *walkBehavior\r\n-(void)swim;\r\n-(void)walk;\r\n-(void)quack;\r\n-(void)display;\r\n\r\n@end\r\n<\/pre>\n<p>L&#8217;implementazione del metodo walk cambia e diventa<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n -(void)walk {\r\n  [walkBehavior walk];\r\n }\r\n<\/pre>\n<p>In questo modo possiamo cambiare a runtime il modo di camminare dell&#8217;anatra semplicemente assegnandole un nuovo WalkBehavior, come in questo esempio:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nDuck *duck = [[Duck alloc] init];\r\nWalkBehavior *walkBH = [[WalkBehavior alloc] init];\r\n\r\n[duck setWalkBehavior:walkBH];'\r\n\r\n[duck walk]; \/\/stampa \"standard walking\"\r\n\r\nWalkBehaviorSuperSpeed *walkBHSuperS = [[WalkBehaviorSuperSpeed alloc] init];\r\n[duck setWalkBehavior:walkBHSuperS];\r\n\r\n[duck walk]; \/\/stampa \"supersonic waking speed\"\r\n<\/pre>\n<p>Siamo riusciti in pratica a fare quanto suggerito dal design pattern Strategy, abbiamo separato le parti di codice che sono soggette a cambiamento, ne abbiamo fatto delle classi esterne e abbiamo dato a tutte la stessa interfaccia, in questo modo la classe che le utilizza pu\u00f2 usarne una qualsiasi indifferentemente.<\/p>\n<p>Spero che sia chiaro, nonostante l&#8217;esempio fantasioso, quale sia la logica alla base di queste modifiche e vorrei concludere soltanto citando qualcuno dei vantaggi ottenuti utilizzando questo approccio:<\/p>\n<ul>\n<li>Non abbiamo pi\u00f9 bisogno di mantenere informazioni sullo stato interno dell&#8217;oggetto<\/li>\n<li>Posso cambiare separatamente tutti i comportamenti, non \u00e8 un problema quindi mischiare comportamenti da superpoteri e comportamenti normali.<\/li>\n<li>La creazione di nuovi modi di camminare non influenza minimamente la classe Duck, che potr\u00e0 utilizzare <strong>senza alcuna modifica<\/strong> anche ne nuove subclass di WalkBehavior.<\/li>\n<\/ul>\n<p><strong><em>Una nota per i pi\u00f9 curiosi:<\/em><\/strong>I design pattern fanno un uso continuo del concetto di interfaccia e classe astratta, costrutti presenti in java e in altri linguaggi ma non presenti in obj-c.<br \/>\nIn particoalre Strategy prevede che la classe WalkBehavior sia una classe astratta, mentre nell&#8217;esempio \u00e8 una classe concreta. Avrei potuto utilizzare il concetto di protocol obj-c che \u00e8 molto simile ad una classe astratta, ma ho preferito evitare per non aggiungere complessita o creare confusione con il delegate design pattern.<\/p>\n<p>Alla prossima!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Su questo sito lo sforzo di tutti gli autori \u00e8 quello di scrivere articoli che non siano&#8230;<\/p>\n","protected":false},"author":53,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[1208,1210,1211,1209,18],"class_list":["post-9614","post","type-post","status-publish","format-standard","hentry","category-guide-teoriche","tag-delegate-design-pattern","tag-design-patterns","tag-guide-teoriche-2","tag-observer-design-patter","tag-oop"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9614","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=9614"}],"version-history":[{"count":11,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9614\/revisions"}],"predecessor-version":[{"id":9626,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9614\/revisions\/9626"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=9614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=9614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=9614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}