bloggers bloggers

Marco Napolitano
Messaggi: 79
Stelle: 0
Data: 17/02/22
Jader Jed Francia
Messaggi: 63
Stelle: 0
Data: 18/02/21
Paolo Gambetti
Messaggi: 2
Stelle: 0
Data: 11/11/19
Katia Pazzi
Messaggi: 1
Stelle: 0
Data: 27/06/19
Ezio Lombardi
Messaggi: 11
Stelle: 0
Data: 10/04/18
Chiara Mambretti
Messaggi: 25
Stelle: 0
Data: 27/02/17
Serena Traversi
Messaggi: 3
Stelle: 0
Data: 21/07/16
Francesco Falanga
Messaggi: 8
Stelle: 0
Data: 14/06/16
Antonio Musarra
Messaggi: 2
Stelle: 0
Data: 18/11/13
Simone Celli Marchi
Messaggi: 6
Stelle: 0
Data: 09/07/13
Indietro

Implementare una factory in un container OSGi

Ciao a tutti!

L'articolo di oggi verte su uno dei pattern applicativi più noti (nella mia personale classifica, nei colloqui che facciamo è mediamente il secondo che viene detto dopo il Singleton): il factory pattern!

Non farò una dissertazione sul pattern in se, perché credo che Google possa fornirvi tutti gli articoli dell'universo (fatti anche molto meglio di come li farei io) su di esso; tuttavia mi piaceva l'idea di darvi uno spunto per una sua possibile implementazione all'interno di un container OSGi.

Il problema

Come sempre, però, vi racconto il caso d'uso che mi ha portato a questa implementazione, così che anche voi possiate avere un po' di contesto.

Il caso d'uso di questa volta, incredibile a dirsi, non arriva da uno dei miei animali mitologici, bensì da un bisogno personale!

Mi spiego: come spesso accade, mi trovo a fare un po' di audit sul codice che ho scritto e mi rendo conto che, onestamente, un po' di refactor potrebbe migliorarne quanto meno la leggibilità se non, come in questo caso, la manutenibilità.

Nello specifico, in un metodo, avevo il classico "if / else / if" e, dopo il terzo "if", mi sono trovato ad aggiungerne un quarto.

Chiaramente tutti avete già storto il naso: chi non avrebbe implementato un factory già al terzo "if" alzi la mano! :)

Io invece, siccome sono mediamente un po' più indietro della media :), ho pensato di implementarlo al quarto.

Allora mi sono messo a ragionare (in realtà a scrivere codice mentre lo facevo.. :)), per implementarlo.

Siccome però ero all'interno del container OSGi, oggi vero cuore pulsante di Liferay, mi sono trovato davanti a un problema: come implemento una factory che mi ritorna Service OSGi? Eggià, perché il mio caso non era banalmente sostituire la cascata di "if / else" con un factory, era anche quello di permettere agli oggetti ritornati dalla factory stessa di beneficiare della dependency injection offerta dal container.

Allora mi sono messo di buona lena e, con un po' di buzzo buono, mi sono lanciato a fare test, leggere specifiche e, più in generale, a cercare una soluzione che risolvesse il mio problema.

Dopo circa due ore di mal di testa (sfido chiunque a non impazzire leggendo le specifiche di OSGi riga per riga alla ricerca di un suggerimento pratico..) ho avuto un'illuminazione!

"Ma sono un genio", ho pensato, "il service tracker è di fatto una factory (molto alla lontana ma seguite il ragionamento) e quello che ritorna, i Service OSGi, sono di fatto gli elementi che servono a me"!

Da questa riflessione, che vi giuro mi ha spaccato in due come una mela che cade su una motosega impazzita :), ho dedotto che il Factory pattern in OSGi è morto, perché, di fatto, sostituito dal Service pattern!

Il Factory pattern è morto: viva il Factory pattern!

Ok, direte voi: questo è ovvio. Lo so, lo avevo anche premesso: non sono un fulmine di guerra! ;)

Però mi sono detto: a questo punto come risolvo il problema dello "switch" che dovrei fare, per discriminare quale tra gli oggetti che sono ritornati devo usare?

La risposta è stata ovvia: property di un Component! Posso fare uno switch in base ad una property e quindi, con un filtro, avere quello che mi serve!

Anche qui, direte voi, ovvio. Siete un pubblico difficile quest'oggi! :)

Ancora una volta, però, non mi sono arreso e ho cominciato a cercare una soluzione che mi mettesse felice e mi permettesse di recuperare, dato il filtro, il Service a me più congeniale.

La soluzione

Ed ecco la mia possibile soluzione, con la speranza che possa tornare utile anche a voi!

Spoiler alert: quello che segue non è LA soluzione, bensì, "la migliore soluzione che ho trovato con i constraint di tempo e budget che avevo". Don't blame me for that! :)

Ometto le parti di codice "ovvie", quelle ad esempio dove accedo al ServiceTracker, ma mi concentro sul metodo che, di fatto, rappresenta la mia factory!

private TaskExecutor getTaskExecutor(Element element) {
   Class[] interfaces = element.getClass().getInterfaces();
   for (Class interfaceObj : interfaces) {
      List executors = serviceProvider.getServices(TaskExecutor.class,
          "task.executor.type", interfaceObj.getSimpleName());
      if (Validator.isNotNull(executors) || executors.size() > 0)
          return executors.get(0);
      }

      return new TaskExecutor() {
         @Override
         public boolean executeTask(ActivityTask task, ModelitDataRecord dataRecord,
             ModelitBPMEngineLocalService service, ServiceContext serviceContext) throws Exception {
                 return false;
         }
    };
}

Come potete vedere l'idea è semplice: i miei Component hanno una property (task.executor.type) sulla quale mi baso per fare il case.

Questa property, nella mia convenzione, è mappata su un'interfaccia applicativa che gli oggetti sui quali faccio lo switch implementano. Ora, mi è ben chiaro che questa soluzione non è assolutamente scalabile, è forse un po' troppo artigianale e magari non è elegantissima; so anche che se uno dei miei oggetti implementa più interfacce (possibile), la cosa non sta in piedi ma vi garantisco che nel mio modello tutte queste cose sono superabili! :)

Come vedete, però, l'obiettivo l'ho raggiunto: passo alla mia (finta) factory l'oggetto sul quale fare il case e a runtime, sfruttando la reflection, recupero l'oggetto a me più congeniale.

Concludo con un dettaglio: nel mio caso è assolutamente plausibile che non ci sia un oggetto di ritorno per ogni oggetto in input, quindi ho implementato come default un DummyObject che mi consente di mantenere consistente il client e mi previene dalle NullPointerException!

Bene: anche per oggi vi ho fatto perdere un po' di tempo ragionando su pattern e possibili implementazioni su container OSGi; come sempre, nel caso abbiate dubbi / domande, potete utilizzare il box dei commenti!

A presto e, buon (Factory)Service pattern a tutti! :)

Commenti
Nessun commento. Vuoi essere il primo.