mercoledì 31 marzo 2021

Cosa va provato ?

Di qualcosa e di qualcuno bisogna fidarsi. Non si tratta di una scorciatoia, è il modo per non lavorare come dei buoi.

Se facciamo, per esempio, una funzione che somma due numeri, fare un unit test che provi le somme o farne cinque, sei oppure mille è la stessa identica cosa, infatti non potremo mai provare tutte le combinazioni di numeri possibili: dobbiamo solo verificare che il nostro programma sommi e che non faccia sottrazioni o altre operazioni. Fare tanti test significa provare "l'operatore somma" dello strumento e non ha senso: se non va, si cambia strumento immediatamente.

Allo stesso modo chi fa test di un' applicazione di database non deve provare il funzionamento del database stesso, perché se si scoprisse un bug sul database, bisognerebbe intervenire subito a sostituirlo o ad applicare una correzione. E' necessario avere dei punti certi, fissi.

Credo che troppo spesso nelle software house italiane si premi la velocità di sviluppo, perché la velocità determina il costo di produzione. Credo che questo dipenda in larga parte dal fatto che esse producono soluzioni con basso valore aggiunto, quindi il fattore determinante è il costo. E' chiaro infatti che produrre un sistema per una banca online senza il quale la banca non esisterebbe, ha un gran valore e il cliente è ben disposto a pagare la novità e la qualità. Fare lavoretti, che al massimo danno prestigio all'azienda di lustrini italiana, non porta valore e bisogna fare presto. Allora via libera a modi di lavorare alienanti e tempi asfissianti, che portano per assurdo a lavorare di più e a metterci di più.

Oltre alla frustrazione per il lavoratore, il costo è più alto! Vaglielo a spiegare.

I test vanno fatti con intelligenza, non con fretta.




domenica 28 febbraio 2021

Tutto passa, anche la jvm

Credo che il tempo glorioso dei linguaggi (ma sarebbe meglio parlare di ecosistemi) basati su virtual machine stia volgendo al termine.

Con questo non voglio dire che i vari Java (quindi Scala, Jython ecc...) così come .NET, spariranno domattina, vista la base di installato, ma anche vista l'enorme disponibilità di librerie e strumenti.

Voglio però dire che c'è sempre meno bisogno di un programma cross platform, grazie alla possibilità di virtualizzare un sistema operativo intero. Ci sono strumenti che compilano programmi cross platform e non hanno quindi tutto l'overhead di una virtual machine. Se poi guardiamo i sistemi con i container, i quali magari si lanciano e chiudono dinamicamente per la scalabilità orizzontale dell'applicazione, è preferibile avere strumenti che usino poche risorse e che siano veloci a caricarsi; è quindi evidente che l'overhead di una virtual machine è un problema. Non mi stupisce quindi che si spingano tecnologie come GraalVM, ma mi chiedo se a questo punto non valga la pena di guardare altrove.

Da un lato C++ spaventa i neofiti così come spaventa le aziende, che sono abituate a cercare programmatori a basso costo, bassa formazione e alta sostituibilità e ne ho viste tante, veramente troppe. Oggi i linguaggi possibili sul cloud non sono comunemente compilati, penso a Javascript con Node.js, che si può usare anche nelle funzioni o lambda dei cloud più comuni oppure sono semisconosciuti (come Swift e Rust, soprattutto quest'ultimo mi sembra una vera chicca, ma non trovo offerte di lavoro su di esso, quindi mi chiedo se in Italia lo usi qualcuno). Altre soluzioni mi sembrano troppo sperimentali.

Il concetto però è che usare Java o .NET in una funzione di un cloud come in un container non è la scelta migliore, perché quando sono nati non si parlava certo di container o di lambda. Se poi dobbiamo fare una piccola web app, con poco budget e magari ci occupiamo sia del front end che del back end, di sicuro dobbiamo imparare javascript, allora tanto vale usarlo anche sul server, perché imparare un linguaggio diverso?

Non voglio parlare poi del desktop, che sta diventando sempre più una nicchia, ma personalmente trovo che Java in quel settore sia sempre stato un pesce fuor d'acqua: evidentemente non muoveva il denaro dietro allo sviluppo lato server (il nuovo Cobol si diceva), quindi le proposte fatte nel tempo (AWT, Swing e alla fine JavaFX) non hanno mai convinto del tutto. Oggi JavaFX è stato scorporato dal JDK e se dovessi fare un'applicazione desktop, oggi cercherei delle alternative e questo vale anche per .NET, lento, grosso, un framework che non mi è mai piaciuto.

Java ha perso un'occasione fantastica con gli smartphone, perché se da un lato Android per me è un fork di Java e non mi stancherò mai di dirlo, su iOS non esiste proprio.

Ho investito gran parte della mia carriera lavorativa su questo linguaggio, ma oggi vedo sempre meno opportunità.

Oggi è meglio conoscere un po' di diverse tecnologie, imparare bene nei dettagli uno strumento richiede un investimento di tempo che difficilmente ritorna, le tecnologie invecchiano in fretta e si rischia veramente che l'investimento non ritorni, però non voglio giustificare chi si butta nei progetti e, soprattutto, butta i collaboratori, senza conoscere lo strumento. I corsi sono tutt'ora molto utili per evitare i guai, ma credo che imparare bene un framework intero paghi meno che sapersi districare su diversi strumenti. Non vale soltanto per la programmazione, anche con i database è così e oggi bisogna conoscere almeno un database NoSQL, perché i database relazionali non sono la soluzione migliore con i microservizi e a questo potrei dedicare un articolo più avanti.

Noi non siamo la tecnologia che usiamo. Non ci dobbiamo identificare con essa e dobbiamo essere pronti a cambiarla, quando i tempi e le condizioni di mercato lo impongono.

Bisogna sempre stare con la valigia in mano insomma. Da un lato si dice che serve una grande specializzazione, ma dall'altro specializzarsi non deve significare legarsi a una tecnologia, che passa.



giovedì 2 aprile 2020

Studia che poi avrai un buon lavoro

Ma perché? La maggior parte del tempo che passiamo sui libri, lo impegniamo di fatto a imparare nozioni completamente irrilevanti per il mercato del lavoro.

Gli insegnanti spesso vivono in una realtà, quella accademica, che è ben lontana dal mondo delle imprese, dai suoi bisogni, dai suoi ritmi e dalle sue condizioni.

Eppure è evidente come i laureati abbiano una strada preferenziale nelle assunzioni, credo che sia legittimo a questo punto chiedersi cosa cercano le aziende e cosa trovano in questi ragazzi freschi di laurea. Bene io credo che tra le prime caratteristiche necessarie per portare avanti un percorso di studi sia una buona resistenza alla noia e la capacità di "pensare per obiettivi", cioè di raggiungere un obiettivo in qualche modo, facendosi poche domande sulla sua effettiva utilità strategica e quindi di fare ciò che gli viene richiesto, indipendentemente dal fatto che gli piaccia, che lo ritenga veramente utile o che si illuda che ciò sia vero. Ed è proprio questo che moltissime aziende cercano: servi.

Ciò che effettivamente il candidato conosce è poco importante, perché deve solo obbedire al capo e intuire velocemente ciò che il capo gli dice male o parzialmente. Anche questa grande pagliacciata del lavoro di gruppo maschera il fatto, che chi comanda non sia in grado di coordinare (non dico di formare) una squadra, quindi si buttano nel calderone (il gruppo) persone sperando che si coordino tra di loro, quasi magicamente, come un sistema che si equilibra da solo. Anche i gruppi di studio all'università servono a preparare questa condizione. In realtà c'è uno scenario anche peggiore: quando la gerarchia nel gruppo c'è eccome, ma ci sono più capi in competizione tra di loro e il nuovo arrivato deve barcamenarsi nel cercare di capire quale padrone servire, mediare scontentando tutti o accontentarli a turni, ben consapevole degli strali che gli arriveranno dai capi scontenti.

Questo scenario riguarda purtroppo diverse software house italiane. Non sono tutte così, perché grazie al cielo ho avuto modo di confrontarmi anche con gente seria, ma i bravi si possono contare sulle punte delle dita di una mano, mozza di qualche dito, magari.

Difficile anche dire cosa vuol dire "bravo" o "fatto bene", diciamo che il termine andrebbe completato con " per chi ti comanda" o meglio per ciò che gli è stato insegnato svariati decenni prima da un insegnante a scuola o sul lavoro... e il loop continua.

Dubito che in fabbrica o in altre realtà la situazione sia diversa, temo anche che la condizione del lavoratore in ambienti con uno scarso livello di istruzione sia ben peggiore, ma non ho esperienza diretta di quei casi, quindi non me la sento di scriverne qui.

Tornando al tema principale di questo articolo, l'università, credo che rifletta pienamente la media della società in cui ci troviamo e che abbia fallito l'obiettivo di cercare di migliorarla.

Consiglierei senz'altro a un giovane di studiare, proprio perché questi difetti diventano pregi negli occhi di chi dovrà offrirgli uno stipendio e di fatto è un plus notevole avere un titolo di studio, ancora di più se il lavoro sarà statale, perché lo stato non può contraddirsi (troppo) apertamente e premia chi ha obbedito al di là della sua bravura in senso assoluto, che abbiamo visto prima come non esista, quindi lo stato premia con condizioni di lavoro decisamente migliori della media, chi è stato un bravo servo fedele e ha obbedito, dedicando tanti e insostituibili anni della sua vita a fare cose largamente inutili se prese come sono, ma utilissime a dimostrare la propria fedeltà all'esecuzione del compito assegnato.

Altro discorso invece è per chi si porta fuori da questo circo e cerca di risolvere i problemi per gli altri, posto che questi altri si fidino dei propri occhi e non si basino sul giudizio degli altri (certificazioni e titoli) per giudicare il lavoro fatto e in ultima analisi, chi lo ha fatto.

La conclusione sulla strategia migliore da adottare è: scegliere con cura i propri insegnanti, cercando scuole che preparino non tanto al lavoro, quanto a non diventarne schiavi, il che si traduce nella capacità di pensare con la propria testa e nel saper persuadere che ciò che è stato fatto risolve il problema per cui è stato pagato o almeno ha risolto altri problemi, che non erano evidenti all'inizio... La seconda scelta da fare è non lavorare per tutti, ma attorniarsi di gente che ci apprezza e apprezza ciò che facciamo al di là dei titoli. Questo non significa cercare una claque, che ci applauda al di là degli errori che facciamo, perché sarebbe un punto di vista narcisistico e praticamente irrealizzabile, oltre che palesemente ingiusto. Io dico che dobbiamo circondarci di gente che ci permetta di sbagliare, perché lo sbaglio non è un lusso: è uno dei tanti passi necessari per raggiungere il successo. Questo è necessario e stranamente pare non essere considerato dalla società latina nella quale viviamo, dove si incensano i vincenti e si ignorano gli altri. Vorrei veramente conoscere un'azienda che dica "ci hai provato tante volte senza riuscirci, cos'hai imparato dai tuoi fallimenti?" anziché "eh ma questo voto era basso, vabbé era giovane, secondo me puoi sbagliare solo fino ai 18 anni, poi non è più concesso". Il lettore mi creda, l'ho sentito dire veramente, per fortuna non su di me ma su una persona, che avevo proposto per una posizione; quando ci penso ancora mi chiedo cosa avesse fatto in vita sua quel tizio dopo i 18 anni...

Un'ultima cosa: affermare con sicurezza che si posseggono le carte giuste per raggiungere un obiettivo, per prendere un titolo, ma avere scelto di non farlo, generalmente non è creduto... quindi se chi abbiamo di fronte cerca un titolo e non l'abbiamo forse è talmente stolto che non vale nemmeno la pena di convincerlo; possiamo comunque sempre cercare di ottenere quel titolo per convincerlo, se proprio ci teniamo tanto a quel posto, ma forse l'errore è proprio tenerci così tanto.

In questo discorso rimangono fuori i corsi di formazione professionale. Imparare facendo anziché ascoltando. E' così semplice e chiaro che ancora non capisco come si possano sottovalutarli: un corso professionale prepara a fare un compito, quindi se i datori di lavoro proponessero i corsi formativi per i loro obiettivi, questi sarebbero il completamento ideale per un percorso formativo solo teorico. Perché non capiscono che la velocità nell'esecuzione di un compito dipende in larghissima misura, per non dire del tutto, dall'allenamento che si ha ad eseguirlo? Non è che forse il problema dei giovani che non trovano lavoro non è che sono choosy, ma solo che il tessuto produttivo soffre di un'ignoranza sconcertante, proprio perché chi decide ha ricevuto a sua volta un'istruzione inadeguata?




martedì 25 febbraio 2020

Unbreakable

Mi è capitata una discussione  decisamente spiacevole su un tema che oggi dovrebbe essere superato, ma evidentemente non è così: la laicità dell'uso dell'istruzione break per uscire da un ciclo for in Java.

Vediamo un esempio banale di ricerca su un array con uscita quando si trova l'elemento cercato

package com.byteliberi;
import java.util.List;
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = "";
for (String poligono : poligoni) {
if ("quadrato".equals(poligono)) {
trovato = poligono;
break;
}
}
System.out.println(trovato);
}
}

L'output è la parola: quadrato

Il codice in esempio non è e non vuole essere un esempio stilistico di come si cerca un elemento in una lista in Java,  però è un modo pratico e veloce per cercare un elemento e uscire una volta trovato.
L'istruzione break, il cui uso in Java è legittimo e non si ottiene alcun warning usandola, provoca un "salto" fuori dal for. C'è chi non accetta questa soluzione e si aspetta da un professionista l'uso del più elegante ciclo while.

Ora, concordo che i salti non siano il massimo dell'eleganza e che possano portare a codice difficile da leggere se si esagera e si annida l'uso dell'istruzione break con l'istruzione continue, però sono soluzioni pratiche. Esistono perché credo che derivino dal linguaggio C, ma sono pericolose per la leggibilità se ne si abusa, in pratica non creano problemi di affidabilità del codice.

L'uso del while ha un piccolo inconveniente, che lo rende meno pratico, vediamo la nuova versione:

package com.byteliberi;
import java.util.List;
import java.util.ListIterator;
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = "";
ListIterator<String> iterator = poligoni.listIterator();
while (iterator.hasNext() && trovato.isEmpty()) {
String poligono = iterator.next();
if ("quadrato".equals(poligono)) {
trovato = poligono;
}
}
System.out.println(trovato);
}

}

Accedere ad un elemento di una lista con il metodo get e l'indice della posizione è efficiente soltanto con gli ArrayList e non con le LinkedList. Non sappiamo e non dobbiamo preoccuparci di quale implementazione sia ritornata da asList, quindi si usa un iteratore. Ora un codice del genere francamente non mi sembra più leggibile di prima, invece ho dovuto introdurre una riga in più ed esplicitare un controllo in più nell'if (c'è un and con due condizioni).

Vediamo ora cosa esiste in Java da qualche versione, le famose lambda che a mio parare (e non solo mio) sono molto eleganti.

package com.byteliberi;
import java.util.Arrays;
import java.util.List;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = poligoni.stream()
.filter(poligono -> "quadrato".equals(poligono))
.findFirst().orElse(null);
assert(trovato != null);
System.out.println(trovato);
}
}

In questo modo non si deve neppure ricorrere all'uso di un ciclo e si sfrutta una "novità" (anche se è comparsa ormai diversi anni fa, con Java 8) che è la lambda. Questa mi sembra di gran lunga la strada più elegante, ma non la più comoda, confesso che trovo quel break semplicemente comodo.

Oggi credo che discussioni su se sia meglio usare un for o un while lascino il tempo che trovano e che i programmatori che discutono, per non dire litigano su queste cose probabilmente hanno una visione troppo ristretta e manierista del mestiere. Credo che gli elementi migliori debbano avere una visione d'insieme e che questi dettagli implementativi non valgano la pena di essere indagati ulteriormente, se non in sede accademica. Credo inoltre che si lavori assieme e si possa lavorare assieme introducendo un elemento di tolleranza verso lo stile degli altri e di rispetto delle conclusioni degli altri, anche quando non coincidono con le nostre. Lavorare assieme è molto difficile perché gli elementi che entrano in gioco non sono soltanto di natura tecnica, si parla sempre di più di competenze trasversali e discutere su un for o un while in azienda è qualcosa che non dovrebbe succedere.

Java mi piace più di altri linguaggi, anche se oggi non è più il mio solo linguaggio di riferimento, perché è molto potente e permette di superare questi vecchie, inutili diatribe.





giovedì 26 dicembre 2019

Chi è un programmatore ?

Ho letto questo blog che quoto in pieno:
https://alessandrobottoni.wordpress.com/2010/05/28/come-riconoscere-un-buon-programmatore/

L'unica cosa che mi da da pensare è che la programmazione Agile ha veramente cambiato lo scenario, non sempre in meglio: se nel team c'è disaccordo, chi comanda? Il più bravo o chi mette i soldi? A parità di ruolo, ha ragione il più arrogante, non il più timido. Potrei parlare a lungo della realtà di software house italiane, ma ho troppo schifo per farlo adesso.

sabato 24 novembre 2018

Back to school

E' passato veramente molto tempo dall'ultimo post, mi sono successe tante cose, tante porte si sono chiuse, ma ne ho aperta una a testate: mi sono iscritto a Interfacce e Tecnologie della Comunicazione a Rovereto, nonostante tutto.
Si tratta di una facoltà inserita nel dipartimento di Scienze Cognitive (psicologia).
La prima cosa che ci hanno insegnato è l'importanza di un design incentrato sull'utente. Mettere l'utente al primo posto significa sforzarsi di progettare in modo che l'utente abbia la miglior esperienza possibile: messaggi per guidarlo durante l'utilizzo del sistema, funzionalità pensate perché non si senta perso.
Ma la principale differenza tra questa scuola e le normali scuole tecniche sta nel trasmettere agli studenti la convinzione che la tecnica da sola non basta: per fare un buon prodotto, bisogna conoscere chi lo usa, cosa gli è comodo. Se non si studia e si conosce la società, così come ragionano le persone, è difficile capire cosa gli utenti troveranno comodo.
Tutto questo sembra ignorato dalle nostre aziende, dove si cercano ossessivamente tecnici, informatici, ingegneri, ma evidentemente queste aziende ignorano l'importanza di sociologi, antropologi e psicologi, come se quei prodotti non dovessero poi essere usati da degli umani...
La competenza si raggiunge osservano il sistema a trecentosessanta gradi, non solo con il punto di vista di un tecnico.

Stiamo facendo una società dominata dalla tecnica, che non si sa più servire della tecnica per raggiungere i propri scopi, ma siamo noi schiavi dei prodotti della tecnica. Peccato che questo non porti a niente, possiamo pensare che la gente usi un prodotto perché è cool e non perché è utile? Non è forse una moda passeggera?
Chi vuole fare dei prodotti che non siano meteore destinate a scomparire, non può non mettere al primo posto i bisogni reali degli utenti, ma per comprenderli, deve capire gli utenti.

lunedì 5 giugno 2017

Ping Applicativo con REST

Oggi voglio scrivere su alcune funzionalità presenti in Java 8: la Duration per indicare un intervallo di tempo e un nuovo client standard per chiamare un web service di tipo REST.

Si tratta di un servizio di Ping applicativo, cioè il client chiama il server un certo numero di volte e ne misura il tempo di risposta, poi fa una media del tempo di tutte le chiamate.
Il client si chiama a riga di comando indicando come parametri il numero di ping, l'indirizzo e volendo vedere la risposta completa, si può specificare il comando -showResponse

Per esempio un client potrebbe avere i seguenti parametri dopo il nome del file .jar: -c4 http://localhost:8080/MiaApp/rs/ping/ -showResponse

Veniamo ora ai dettagli applicativi.
Per provare ho usato come server wildfly in versione 10.1.0

Il codice più rilevante del server è questa classe, che risponde alle chiamate del client:

@Path("/ping")
public class ApplicativePingService {
@Inject
Logger logger;

private static final String VERSION = "0.0.1ALPHA";

@GET
@Produces("application/json")
public Response ping() {
  final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
  String hostName = null;
  try {
    hostName = InetAddress.getLocalHost().getHostName();
    final Pong pong = new Pong(VERSION, hostName,
    now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME), true); // TODO chiama il db
    return Response.ok(pong, MediaType.APPLICATION_JSON).build();
  }
  catch (UnknownHostException ex) {
    logger.log(Level.SEVERE, "I cannot find the host name in ApplicativePingService.ping()", ex);
    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
  }
}


Questa è la classe "Pong" che contiene i dati inviati dal server al client:

public class Pong {
  private String version;
  private String hostName;
  private String dateTime;
  private boolean databaseReady;

  public Pong() {
  }

  public Pong(final String version, final String hostName, final String dateTime, final boolean databaseReady) {

    this.version = version;
    this.hostName = hostName;
    this.dateTime = dateTime;
    this.databaseReady = databaseReady;
  }

  public void setVersion(String version) {
    this.version = version;
  }

  public void setHostName(String hostName) {
    this.hostName = hostName;
  }

  public void setDateTime(String dateTime) {
   this.dateTime = dateTime;
  }

  public String getVersion() {
    return version;
  }

  public String getHostName() {
    return hostName;
  }

  public String getDateTime() {
    return dateTime;
  }

  public boolean isDatabaseReady() {
    return databaseReady;
  }
}


Per quanto riguarda il client, voglio usare la nuova interfaccia standard per i client REST di Java Enterprise sfruttando le librerie fornite già da Wildfly, nel pom.xml le dipendenze da chiamare sono:


<dependency>
  <groupid>javax</groupid>
  <artifactid>javaee-api</artifactid>
  <version>6.0</version>
  <type>jar</type>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupid>javax</groupid>
  <artifactid>javaee-web-api</artifactid>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupid>javax.enterprise</groupid>
  <artifactid>cdi-api</artifactid>
  <version>1.2</version>
  <scope>provided</scope>
</dependency>


Ora veniamo al codice del client più importante: un metodo che fa n chiamate a seconda del parametro passato:
Per mappare i dati JSon che arrivano dal server, usiamo la libreria jackson

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;



public List ping() {
  final List result = new ArrayList<>(this.pingCount);
  final ObjectMapper objectMapper = new ObjectMapper();
  Client client = ClientBuilder.newClient();
  try {
    for (int i = 0; i < pingCount; i++) {

      final LocalDateTime before = LocalDateTime.now(); 
      boolean ok = true; 
      Pong pong = null;
      String message = null;
      boolean databaseReady = false; 
      objectMapper.registerModule(new JavaTimeModule());
      WebTarget resource = client.target(address); 
      Response response = resource.request(MediaType.APPLICATION_JSON).get();
      ok = (response.getStatus() == Response.Status.OK.getStatusCode()); 
      if (ok) {
        message = response.readEntity(String.class); 
        ObjectMapper mapper = new ObjectMapper();
        try { 
          pong = mapper.readValue(message, new TypeReference() { });
          ok = (pong != null); 

          if (ok)
            databaseReady = pong.isDatabaseReady();
        } catch (JsonParseException | 
JsonMappingException | IOException e)  {
          ok = false
          e.printStackTrace();
        }
      }

      final LocalDateTime after = LocalDateTime.now();
      final Duration deltaTime = Duration.between(before, after);
      result.add(new PingResult(ok, deltaTime, pong, message, databaseReady));
    }
  } finally {
    if (client != null)
      client.close();
  }
    return result;
}


Questo codice chiama il metodo sopra per chiamare il server n volte, poi formatta la durata in minuti, secondi e millisecondi (forse solo il Java 9 vedremo dei metodi comodi per estrarre le parti da una Duration, perché i metodi attuali effettuano una conversione in una specifica unità di misura, così è veramente scomoda). Inoltre calcola la durata media di una chiamata.

Per mappare i dati JSon che arrivano dal server, usiamo la libreria jackson

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;



enum CommandLineState { UNDETERMINATE, PING_NUM, ADDRESS };

public static void main(String[] args) {
  int pingRepetition=4;
  String serverAddress = null;
  boolean showResponse = false;
  CommandLineState actualState = CommandLineState.UNDETERMINATE;

  for (String argument : args) {
    switch (actualState) {
    case UNDETERMINATE:
      if (argument.toLowerCase().startsWith("-c")) {
        if (argument.length() > 2)
          pingRepetition = Integer.parseInt(argument.substring(2));
        else
          actualState = CommandLineState.PING_NUM;
      }
      else {
        if ("-showResponse".equals(argument)) {
          showResponse = true;
        else
          serverAddress = argument;


        actualState = CommandLineState.UNDETERMINATE;
        }

        break;
      case PING_NUM:
        pingRepetition = Integer.parseInt(argument.substring(2));
        actualState = CommandLineState.UNDETERMINATE;
        break;
      case ADDRESS:
        serverAddress = argument;
        actualState = CommandLineState.UNDETERMINATE;
        break;
      }
  }

  if ((serverAddress == null) || serverAddress.isEmpty()) {
    System.err.println("Indirizzo del server mancante");
    System.exit(-1);
  }
  if (pingRepetition < 1) {

    System.err.println("Il numero di ripetizioni deve essere maggiore di 0");           System.exit(-2);
  }

  final AppPing pinger = new AppPing(serverAddress, pingRepetition); List result =   pinger.ping();
  if (result != null) {
    // Le prossime 3 righe possono provenire da un file di risorse
    final String outputStr = "%s versione %s ha risposto %s database %s in tempo: %s";
    final String outputAvg= "La durata media è : %s";
    final String rawOutputMsg = "La risposta completa del server è: ";
    final String OK = "OK";
    final String NOT_OK = "NON OK";
    Duration totalDuration = Duration.ZERO;
    for (PingResult pingResult : result) {
      System.out.println();

      final Pong pong = pingResult.getPong();

      String pingStatus = pingResult.isOk() ? OK : NOT_OK;
      String dataBaseStatus = pingResult.isDatabaseReady() ? OK : NOT_OK;

      Duration duration = pingResult.getDuration();
      totalDuration = totalDuration.plus(duration);
      String durationStr = LocalTime.MIDNIGHT.plus(duration).format(DateTimeFormatter.ofPattern("mm:ss:SSS"));

      System.out.format(outputStr, pong.getHostName(), pong.getVersion(),      pingStatus, dataBaseStatus, durationStr);

      if (showResponse) {
        System.out.println();
        System.out.println(rawOutputMsg);
        System.out.println(pingResult.getMessage());
      }
    }

    System.out.println();
    final Duration averageDuration = totalDuration.dividedBy(pingRepetition);
    final String avgResponse =     LocalTime.MIDNIGHT.plus(averageDuration).format(DateTimeFormatter.ofPattern("mm:ss:SSS"));
    System.out.format(outputAvg, avgResponse);
    System.out.println();
  }
}




lunedì 28 settembre 2015

Come linkare il client C di PostgreSQL da un programma C++ usando CMake

Mi sono imbattuto di recente sul problema di linkare una libreria condivisa con il driver di PostgreSQL, il tutto usando Linux su una macchina a 64 bit.

Il programma è in C++ quindi la prima scelta è ricaduta su libpqxx.
Con grande delusione ho notato che libpqxx da problemi se il programma principale segue lo standard C++ 11 o l'ultimo C++ 14.
Poco male, ho pensato di linkare direttamente il client C di PostgreSQL, tuttavia la documentazione in giro non è molta.
Sono riuscito nell'impresa con questi accorgimenti:

nel file CMakeList.txt bisogna mettere
link_directories (/usr/lib64)
target_link_libraries (nomedelprogetto pq)

pq indica il file libpq.so, che si trova in /usr/lib64.

Nella parte C++ bisogna che l'inclusione della parte del driver indichi che si tratta di una libreria in C, quindi:

extern "C" {
    include <libpq-fe.h>
}

Questo permette di usare strutture e funzioni del driver, come PQconnectdb, PQexec, PGconn, PGresult, PQgetvalue, PQclear o PQfinish ecc...


venerdì 11 settembre 2015

Riferimenti Incrociati

Una lezione che non mi aspettavo, durante questo mio studio del C++ è che i riferimenti incrociati sono un anti pattern, una pratica da evitare.
Il C++ onestamente gestisce malissimo i riferimenti incrociati.
Con riferimento incrociato si intente una classe A che ha un riferimento a una classe B, mentre proprio quest'ultima ha un riferimento alla classe A.

Esiste un trucco che si chiama class forwarding che consiste nel dichiarare la classe chiamata in una delle due, però non si possono chiamare i metodi degli oggetti di tale classe.

Diverso è il caso nel quale si ricorra all'uso dei vecchi puntatori puri, i quali però hanno il tragico difetto che non dicono chi è l'incaricato di liberare la memoria.

Questo succede perchè il compilatore, mentre lavora su A, non conosce la dimensione di B e, mentre esamina B, non ha ancora finito di compilare A.
Mentre in Java il problema non si presenta, perché il compilatore fa un lavoro più semplice, trattandosi di un linguaggio con molte meno regole e sfumature, è impossibile per un compilatore C++ lavorare su classi parzialmente compilate.

Funziona con i puntatori perché la dimensione del puntatore è fissa, indipendentemente dall'oggetto puntato.

Dopo il primo shock quando mi sono accorto di questa limitazione, ho fatto un po' di ricerche.

Effettivamente i riferimenti circolari sono una possibile fonte di problemi, si pensi per esempio ai loop infiniti. C'è chi li considera un esempio di cattiva progettazione. Personalmente considero questa affermazione come un po' forte, di sicuro con il C++ sono un problema.

In Java li ho sempre visti con grande libertà. Ripensare un programma con molti riferimenti circolari, di sicuro porta a una struttura finale decisamente più semplice.


mercoledì 9 settembre 2015

Puntatori intelligenti per problemi stupidi

Negli ultimi mesi ho studiato una parte delle novità introdotte negli ultimi anni nella libreria standard di C++.

I puntatori puri, le famose stelline * tanto famose con il C sono di fatto quasi deprecati, alcuni autori dicono che non ci sono ragioni oggi per usarli se non quando si presentino problemi di prestazioni o in caso di risorse molto ridotte.

Devo dire che districarmi tra puntatori, riferimenti, shared_ptr e unique_ptr per me abituato a Java, è stato piuttosto difficile: la gestione della memoria "semi automatica" del nuovo C++ e la scelta tra tenere un'istanza nello stack o nello heap impongono quasi di imparare a programmare di nuovo.

Attualmente il mio livello di comprensione mi porta alle seguenti conclusioni.

Effettivamente il puntatore puro è evitabile in un normale programma gestionale, a meno che non ci si debba interfacciare con delle librerie che lo richiedono.


In C++ oggi è importante stabilire quale oggetto è proprietario di un altro e il proprietario è quello che determina il rilascio del secondo oggetto, cioé quando il proprietario è distrutto, questo determina la distruzione del secondo oggetto. Tale operazione si può fare in automatico, usando un unique_ptr.

Nel caso di variabili di istanza, io ho individuato i seguenti casi:

Il riferimento indicato con la e commerciale & è comodissimo per fare riferimento all'istanza di un altro oggetto se sicuramente mai nullo e mai usato dopo il suo rilascio, senza che il primo ne sia il proprietario.

Una variabile di istanza di un oggetto di proprietà mai nullo, se non è troppo grande, può stare nello stack senza puntatori di alcun genere o riferimenti.

Un riferimento ad un oggetto di proprietà, ma che può valere null è un buon candidato per essere puntato da un unique_ptr o da uno shared_ptr, perché il riferimento & non può mai valere null.

Una variabile di istanza di un oggetto che è di proprietà di un altro oggetto, che potrebbe valere null, potrebbe essere un candidato per uno shared_ptr.

Una lista concatenata o un vettore di oggetti che sono proprietà di altri oggetti, dovrebbe contenere degli std::reference_wrapper.

Una lista concatenata o un vettore (si chiamano contenitori in C++, sarebbero le Collections di Java) di oggetti di proprietà,  può mantenere tali oggetti nello stack.

L'uso di shared_obj non poi così limitato come si legge in giro. Si usa dove si potrebbe usare & ma c'è il rischio che l'oggetto puntato sia tolto dalla memoria prima del riferimento e dove si cerchi di chiamare i metodi dell'oggetto tolto.

Questo unito all'impossibilità di valere null, è un vero e proprio rischio nell'usare il riferimento &. A differenza di Java, in C++ è possibile per errore togliere dalla memoria un oggetto che ha dei riferimenti attivi, così come è possibile tentare di cancellare gli oggetti più di una volta, creando problemi com'è ovvio...

In generale, a meno che non si tratti di oggetti enormi, conviene tenere tutto nello stack, perché è più semplice, il problmema è se il dato può valere null, personalmente non capisco perché si sia rimandata l'introduzione della classe Optional allo standard C++ 17, ma pazienza...

Dopo questi mesi di studio, mi sento di affermare che il C++ offre diverse possibilità di gestione della memoria manuale, che però richiede molta attenzione.


venerdì 21 agosto 2015

No, non sono un mago

A volte ci si aspetta che l'informatico, sia esso un programmatore o un sistemista o magari anche un più generico consulente aziendale, sia in realtà un mago in grado di risolvere tutti i problemi di natura tecnica, indipendentemente dalla complessità e natura del problema.

Siamo persone, fatte di carne e ossa, che hanno studiato e hanno più o meno esperienza nel settore, ma non possiamo risolvere tutto.
Il problema, secondo me, sta nella scarsissima cultura informatica di molte persone con compiti dirigenziali, le quali semplicemente non si rendono conto di cosa si può chiedere, di cosa ci si può aspettare. Si può chiedere di tenere in equilibrio una matita per la punta, se non si hanno nozioni di fisica o se non si sa cos'è una matita...

Un altro aspetto che mia sempre sconcertato  è la scarsa propensione a formare il personale che lavora in ambito tecnologico. Pensare che la tecnologia oggi è sempre più mettere assieme parti fatte da altri, con dei comportamenti e delle interfacce che vanno conosciute. Le stesse persone che non vogliono formare i dipendenti sono le stesse che si aspettano risultati.

Sempre più spesso di vedono aziende che cercano personale già formato, anche su tecnologie nate per gli hobbisti, che un professionista con esperienza imparerebbe in meno di 15 giorni... L'importante è non investire sul personale, usare il know how della gente full time per un periodo limitato di tempo, finito il quale se si rende necessaria una nuova tecnologia, si cambia persona...

E' un mondo feroce.

Le aziende non cercano tecnologie moderne e sofisticate, che garantiscono libertà dai vendor e magari una forte scalabilità, bensì in tecnologie buone per fare prototipi, l'importante è avere qualcosa presto, poi ci penseremo.

Cosa ci salverà?
  1. Le aziende dovrebbero prendere coscienza che sviluppare un progetto non significa semplicemente "tirare fuori qualcosa", che la fase di progettazione e studio hanno il valore maggiore.
  2. Le aziende dovrebbero adottare le soluzioni open source, non soltanto quelle banali e semplici, ma soprattutto le piattaforme e gli strumenti che permettono, con una fase di configurazione, magari dopo diversi tentativi, di realizzare il servizio del quale hanno bisogno. In questo modo i costi ti tengono bassi (l'open source spesso cosa meno) e i tempi sono corti (il programma spesso è giù fatto, va soltanto configurato).
  3. Le aziende dovrebbero finalmente capire che il personale non è un costo, le persone fanno l'azienda, non il denaro, se le persone crescono, conoscono e si trovano bene, in un ambiente umano, non se ne andranno, sprecando così l'investimento fatto su di loro, anzi saranno proprio le persone a creare il profitto.
  4. Noi dovremmo renderci pienamente conto di cosa cerca il mercato e capire una volta per tutte che ciò che rende soldi nel breve periodo, difficilmente li renderà nel lungo periodo e viceversa...

lunedì 3 agosto 2015

Chi me lo fa fare? Me stesso

Oggi chattavo con un caro amico, un ingegnere bravissimo, il quale mi ha chiesto come mai sto studiando il C++, chi me lo fa fare.
Il C++ potrà servire per le nuove Internet of Things o nel mercato embedded, ma quando il ferro costa poco, come con i cloud...

Le mie previsioni sono più ottimistiche.
Su internet  le risorse si pagano salate e soltanto consumandone poche, l'applicazione costa poco.

Ci sono strumenti per scrivere applicazioni veloci e cross platform, si possono scrivere applicazioni che girano ovunque, c'è il sempre verde PHP.

Io ho il massimo rispetto per le soluzioni buone per le demo e per gli strumenti che aiutano i neofiti a cominciare. Anche io sinceramente sento che se conoscessi meglio il PHP avrei dei vantaggi per fare un'applicazione web in poco tempo e c'è tutt'ora una discreta richiesta di programmatori PHP, il motivo è che le applicazioni che necessitano di carichi di lavoro bassi e devono essere scritte con budget molto risicati, possono essere tranquillamente scritte in PHP, il problema semmai è quando queste condizioni cadono, aumenta il carico di lavoro o l'aplicazione cresce e diventa più complicata.

E' ancora possibile usare un linguaggio semplice come il PHP, però le cose diventano ancora più complicate che se si fosse usato Java o, perché no, il C++.

Ritengo che il C++ possa svilupparsi anche in ambito web, oggi più che nel passato, a patto che ci sia una libreria utile. In questo caso si può sviluppare velocemente. Questa è la mia scommessa.

Quindi chi mi fa studiare il C++ ? Io !

martedì 21 luglio 2015

C++ che passione

  Seguo il C++ da anni. Non ho mai avuto il coraggio fino ad ora di consigliarne l'uso in dei progetti in produzione, a meno che non mi fosse espressamente richiesto.
Si tratta di un linguaggio potentissimo e terribilmente complicato da imparare.

  Rimanere compatibile (provarci almeno) con dei sorgenti scritti quasi 40 anni fa, ha "arricchito" il linguaggio a tal punto che la stessa azione si può compiere a volte in 4 o più modi diversi, però soltanto un modo è il migliore, gli atri sono da conoscere solo per capire cosa fa il vecchio codice esistente...

  Prendo per esempio i puntatori.
  Sono una delle prime cause di errore. I puntatori puri oggi, dopo la pubblicazione del C++ 14, sono suggeriti come "evil", il diavolo da evitare come la peste.
Quindi abbiamo:
  • I puntatori puri * che provengono dal C e sono da evitare.
  • I riferimenti & che sono un po' più comodi ma non risolvono il problema della memoria mai rilasciata
  • Gli smart pointers (shared_ptr e weak_ptr) per i quali spesso si sconglia l'utilizzo, perché creano il problema di condividere la proprietà di un oggetto e non si sa, se questo possa essere effettivamente tolto
  • Lo unique_ptr che pare essere la panacea di tutti i mali, la risposta al problema del memory leak, però non può essere copiato, ma solo spostato...
  E' interessante notare come un vettore non possa contenere dei riferimenti, ma solo puntatori puri...si possono usare gli shared_ptr, ma li trovo un po' complicati. Oggi non saprei dire con certezza se i puntatori puri siano da evitare sempre nei progetti nuovi.

  A differenza di quanto avviene in Java o in .Net, dove un pesantissimo garbage collector cerca di ripulire la memoria non più utilizzata, gli smart pointers del C++ richiedono al programmatore di porsi il problema di chi è il proprietario del dato. Lo smart pointer rilascerà la memoria, quando sarà rilasciato il proprietario... è un nuovo approccio, a mio avviso semplice e geniale, per eliminare l'overhead del GC, croce e delizia dei programmatori, soprattutto neofiti, ma croce e basta dei sistemisti...

  Il problema fondamentale con il C++ secondo me non è tanto che il tempo di apprendimento sia lungo, che la curva di apprendimento sia ripida o che costi tantissimo formare il personale, il vero problema con il C++ è che ti costringe a PENSARE. Non si può programmare meccanicamente, non c'è posto per le scimmiette ammaestrate che "vanno", che "producono" codice ripetuto e da buttare via dopo 6 mesi... Con il C++ ci vuole un thread in testa che continuamente ti dica: "stai facendo veramente la cosa giusta?"

  Si può spendere un sacco di tempo su poche righe di codice, ho scritto spendere, non buttare...

mercoledì 11 febbraio 2015

Terminare Finalmente

Chi proviene dal C++ sa bene che è molto importante rilasciare la memoria allocata precedentemente nello heap. Le classi C++ hanno il distruttore proprio per questo motivo.
Potrebbe sembrare che il metodo finalizer di Java sia l'equivalente del distruttore in C++.

NON LO E' !!!

Il metodo finalizer di Java è da usare soltanto in due casi:

  1. Come rete di protezione per assicurarsi che una risorsa nativa sia stata rilasciata e per questo scopo è stato creato un metodo apposito che l'oggetto client si potrebbe essere dimenticato di chiamare.
  2. Per rilasciare delle risorse native non critiche, cioè risorse la cui occupazione non deve essere rilasciata il prima possibile. Infatti se una classe Java chiama una libreria nativa, il garbage collector non può intervenire sulla parte nativa, quando rilascia l'oggetto in Java, quindi il finalizer in questo caso ha senso, però il programmatore potrebbe comunque fare un metodo di rilascio e chiamarlo.


Il metodo finalizer è chiamato dal Garbage Collector, quindi non c'è la certezza di quando è chiamato: dipende da quale algoritmo di GC è usato e dalla particolare implementazione della virtual machine.
La virtual machine potrebbe anche terminare senza che il finalizer sia mai stato chiamato.
I metodi finalizer rallentano il programma.

Il modo corretto di rilasciare le risorse critiche è un try finally, non il finalizer !

Anche se si chiama System.runFinalization non si ha la garanzia che il finalizer sia chiamato, mentre il System.runFinalizerOnExit e il Runtime.runFinalizerOnExit sono stati deprecati.

Il finalizer pone problemi anche con l'overriding di una sottoclasse, nel caso in cui il programmatore distratto si sia dimenticato di chiamare il super.finalizer() nella sottoclasse, a tal proposito ha senso definire nella superclasse un oggetto come proprietà privata, il quale deve avere il finalizer per liberare le risorse, in modo che, se anche la sottoclasse non chiama il super.finalizer(), quando interviene il garbage collector, questi chiami automaticamente il finalizer dell'oggetto privato, non si sa quando.

Una curiosità per i programmatori C++: la parentesi graffa chiusa } è un operatore nel mondo C++, quindi se abbiamo aperto un file e abbiamo il riferimento nello stack, la graffa chiusa chiama il metodo close() !!!

Questo in Java non succede, perché la parentesi graffa indica soltanto la fine del blocco di codice, si deve ricorrere al try finally o al try-with-resources che accetta oggetti chiudibili e si occupa di chiuderli automaticamente alla fine del blocco try, altra dimostrazione che il finalizer è da evitare come la peste, perché il modo giusto è ricorrere il più possibile al try.

domenica 1 febbraio 2015

Dove non arriva lo spazzino

Uno degli elementi che piace di più ai giovani programmatori e a ai manager anziani riguardo a Java è il Garbage Collector: uno spazzino che arriva a liberare la memoria, che noi programmatori abbiamo allocato in modo automatico, trovando tutto ciò comodissimo.
Invito il lettore a parlare con qualsiasi sistemista che si occupi di monitorare l'occupazione di memoria della macchina e a chiedergli se anche lui (o lei) è d'accordo sulle meraviglie del Garbage Collector (GC).
Il GC è un'arma a doppio taglio, si tratta di un algoritmo complesso, che è una delle prime cause della lentezza di Java, inoltre è un invito per i programmatori per diventare pigri.
Ci sono casi infatti dove la disabitudine dei programmatori Java a occuparsi della memoria, dando fiducia completa al Garbage Collector, portano a dei memory leack.

Si possono individuare 3 casi dove il programmatore Java deve prestare attenzione, per non rendere impossibile il lavoro del GC.

1) Raggiungere l'insufficienza di memoria tramite la ritenzione non intenzionale di oggetti. Se si ritengono dei riferimenti a degli oggetti, per esempio in un vettore o in uno stack dove si desidera rimpicciolire la dimensione della struttura e non si pone a null il valore degli elementi rimasti fuori dal limite abbassato, succede che tali riferimenti impediranno al Garbage Collector di liberare la memoria degli oggetti riferiti.

2) Le cache devono liberare gli elementi più vecchi. E' comune tenere in cache dei riferimenti a degli oggetti in cache e dimenticarsi poi di togliere tali riferimenti, quando questi non sono più usati da tanto tempo. Una buona struttura per realizzare una cache in Java è il WeakHashMap. Nel caso invece dove si scelga una LinkedHashMap, esiste un metodo molto interessante: removeEldestEntry che toglie l'elemento più vecchio, magari si può realizzare un Timer o uno ScheduledThreadPoolExecutor che periodicamente lo chiama per pulire la cache degli elementi più vecchi.

3) I listener sono spesso usati per le applicazioni desktop. Richiedono molta attenzione perché i listener vanno tolti dalla lista dei listener registrati, quando non servono più, perché troppo spesso i listener rimangono e cresce il numero di riferimenti a oggetti, compromettendo l'occupazione efficiente della memoria.

Questi punti si trovano descritti sempre nell'ottimo Effective Java di Joshua Bloch.

mercoledì 28 gennaio 2015

I benefattori

Osservando le nuove offerte in campo dello sviluppo, vedo strumenti cross platform sopratutto per i cellulari.
Ho parlato con un amico, titolare di una importante software house, il quale mi diceva che stava cercando uno strumento per scrivere il codice una volta sola per Android e iOS.
Scrivere il codice una volta sola vuol dire risparmiare, avere una base unica sulla quale fare le modifiche, vuol dire tagliare i costi di produzione del proprio prodotto e raggiungere un target di clienti il più ampio possibile.

Uso Java dalla versione 1.0.1beta su OS/2 Warp, che i più giovani forse nemmeno ricordano. Dovrei sentirmi inorgoglito del fatto che oggi sempre più persone riconoscano che avevo ragione, che è importante non escludere dei clienti possibili, rispettando le loro scelte, perché il sistema operativo è parte importante del modo di lavorare di una persona e una persona lavora diverse ore al giorno...
Ricordo il dileggio di chi mi faceva notare che Microsoft era la più forte, che il mercato era saldamente nelle sue mani e gli altri erano ininfluenti. A onor del vero oggi lo scenario delle piattaforme mobili è ben diverso, molto più variegato e probabilmente gli stessi che tempo fa imparavano a usare VisualBasic e si sentivano Grandi Programmatori non si preoccuperebbero minimamente di studiare HTML, JavaScript, Java ecc... se il loro campione da seguire oggi fosse ancora il dominatore assoluto delle percentuali di installato, come negli anni '90. A loro vorrei ricordare che senza Java non ci sarebbe nemmeno C#, tentativo fallito di Microsoft di copiare una tecnologia allora migliore della loro. Oggi C# è un ottimo strumento per sviluppare su piattaforma Microsoft e gli altri mercati desktop sono ininfluenti, diverso invece è il discorso per quanto riguarda il mercato mobile.

Invece io non sono più così convinto che il cross platform sia la scelta vincente sempre.

E' più facile oggi fare cross platform rispetto a 20 anni fa: ci sono più librerie che nascondono la piattaforma e la crescente potenza dell'hardware permette di tenere degli strati di compatibilità tra l'applicativo nativo e il sistema sottostante. Penso per esempio all'ottimo Parallels su Mac OS X che permette di fare girare ad esempio  Microsoft Office per Windows senza vedere il desktop di Windows, ma di vedere le finestre Windows assieme a quelle del Mac. Penso proprio ai tempi di OS/2 quando di vedevano le finestre dei programmi per windows 3.1 assieme a quelle di OS/2, ma allora l'hardware non era così generoso come oggi e andava tremendamente piano.

20 anni di sviluppo cross platform mi hanno però insegnato che ci sono delle spine in questo mondo di rose. Le applicazioni cross platform non sfruttano mai le caratteristiche della macchina, ma soprattutto NON SI COMPORTANO SECONDO LE ABITUDINI DEGLI UTENTI.

Parlavo con un utente Windows che per le prime volte usava il fantastico The Gimp per il fotoritocco. Egli si arrabbiò perché la finestra di dialogo per aprire i file non era quella alla quale lui era abituato (The Gimp usava finestra di dialogo per Gnome) e questo particolare sminuiva tutto il progetto.

Chi ci "regala" applicazioni cross platform lo fa per LEGARCI ALLA SUA PIATTAFORMA, rendendo inutile il sistema operativo sottostante, per creare un ecosistema di applicazioni e quindi di persone, che hanno bisogno di lui. Questo spiega anche perché spesso le piattaforme cross platform impongono il loro look and feel anche quando potrebbero (e dovrebbero) appoggiarsi ai componenti del sistema sottostante. Garantire lo stesso aspetto su più piattaforme non serve tanto agli utenti, i quali tipicamente ne usano soltanto una. HTML5 mi sembra un'ottima specifica, applicata solo in parte dai produttori di piattaforme, i quali vedono positivamente la sostituzione di Flash, che è proprietario e i plugin sono pesanti, con uno standard aperto, ma l'idea che gli applicativi possano farsi senza dipendere da loro non va loro certamente a genio: quindi l'uso di uno strumento cross platform è sempre più scomodo per l'utente rispetto alle applicazioni native.

Prima di scegliere lo strumento bisogna pensare bene a cosa serve e a CHI LO USA.

NON DICO CHE BISOGNA TORNARE ALLE SOLUZIONI NATIVE in massa, dico che vedo un futuro migliore per chi sviluppa una soluzione nativa per il core dell'applicazione, che deve espandersi e crescere tramite dei generatori di codice o con dei motori che creano l'applicazione a partire da regole o parametri. Questa soluzione è oggi usata da pochi, per quando posso vedere dalla piccola finestra che ho sulla realtà, il motivo è che progettare un'applicazione è un conto, ma progettare un'architettura magari personale è tutt'altra cosa. Su questo fronte c'è ancora spazio per la piccola azienda creativa, che riscopra il valore della progettazione e dell'analisi sui dettagli che si ripropongono nei vari progetti dei quali si occupa.

La logica si può sviluppare con uno dei tanti linguaggi cross platform (Java, C#, JavaScript, anche il C++ 14 va bene) o con sistemi più sofisticati di scripting.
Anche le tanto vituperate stored procedure oggi sono piuttosto portabili e possono essere prese in considerazione e chi le sceglie merita rispetto.

Ci vuole coraggio per sviluppare la propria libreria di base. Scrivere generatori di codice o addirittura scrivere in xml dei layout che poi si applicano automaticamente ai dati da rappresentare, magari seguendo il Naked Object pattern e mettendo da parte il sopravvalutassimo MVC.

Sono queste secondo me le linee da seguire per chi nel 2015 non lavora solo su progetti altrui e vuole continuare a fare il proprio mestiere di produttore di progetti software, senza reinventare la ruota ma mantenendo la propria indipendenza e libertà.


domenica 18 gennaio 2015

Il Pinguino Talebano

  Sono un talebano, l'ho scoperto, me l'hanno detto tanto tempo fa e non ci volevo credere.
  Sono un incoerente che a volte apre il software a volte lo chiude, odia i progetti ai quali partecipa e che lo impegnano anima e corpo in una condizione monodimensionale, sottraendo tempo ed energie al resto della propria vita, come tutti i talebani insomma.

  Quindi?

  Per coerenza se sbatto la testa sul muro devo forse continuare a dare craniate affinché la gente mi applauda per la mia perseveranza, mentre io li ringrazio, con i cerotti in faccia magari?

  Stanco della classificazione e dei commenti, posso dire ora, dopo circa 20 anni di carriera nel campo del software, che la coerenza non mi interessa: mi interessano gli obiettivi e volta per volta cerco di decidere al meglio. Essere considerato un talebano perché i compromessi proposti dagli altri non risolvono il problema ora lascia il tempo che trova.

 Veniamo ora a capire perché si fanno progetti open source e dove invece l'open source non è da preferirsi.
  Ci sono diverse ragioni e motivazioni dietro alla rinuncia della segretezza: narcisismo, voglia di fare cose con gli altri, voglia di contattare altre realtà che ci possono dare lavoro, progetti scolastici, voglia di aiutare gli altri, voglia di restituire alla comunità mondiale una parte di ciò che si ha ricevuto, come se fosse un parziale risarcimenti di un debito ecc...

 Le scelte dietro alla volontà di aprire il codice VANNO RISPETTATE e ho sempre considerato dei cafoni coloro i quali hanno si facevano beffe all'inizio della comunità open, considerando a stregua di fessi quelli che rinunciavano al segreto del proprio lavoro per mettere le manette ai polsi dei propri utenti, pratica assai comune per costringere il malcapitato che ci aveva dato fiducia a continuare a pagare il bravissimo autore del proprio software.

 Ora, stando alle parole di Mr. Stallman, usare il cloud sarebbe stupido e toglierebbe libertà agli utenti. A parte il fatto che nessuno punta una pistola alla tempia dell'utente se non usa il cloud e usare il cloud è semplicemente un servizio comodo, poi ognuno fa le sue scelte.

  Ha senso aprire sempre il proprio codice, sviluppato magari a costi bassi come strategia commerciale per puntare a una lunga assistenza dopo ? Ha senso consentire ad altre persone di trarre giovamento dai nostri sforzi  e dalle nostre procedure o non è forse favorire un concorrente? E' una decisione troppo egoistica pretendere di vivere ? Oggi posso dire che a volte la chiusura è necessaria, per proteggerci da competitori senza scrupoli, però è altresì vero che sviluppare procedure ad hoc alle quali delegare la nostra sopravvivenza economica, in un mondo globalizzato, ha sempre meno senso.

  Quest'ultimo punto merita un approfondimento: 30 anni fa dove sviluppatori indipendenti e software house erano come isole staccate tra di loro, con comunicazione pressoché inesistente e delegata ai libri e alle riviste, era alta la probabilità che un diverse persone giungessero alle stesse conclusioni ma non venissero mai in contatto. La richiesta di software era in aumento costante, quindi la protezione e la chiusura erano strategie vincenti.

  Ma oggi assistiamo in ambito commerciale ciò che è stato per anni vero in ambito scientifico: le scoperte veramente originali sono pochissime, si cresce piano partendo da ciò che già esiste, le forze a disposizione delle piccole società di sviluppo sono poca cosa rispetto alla massa di software e di soluzioni testate e affidabili già disponibili.
  I grandi progetti custom sono necessari e sostenibili più che altro per le banche, le assicurazioni e la pubblica amministrazione, realtà che tipicamente rimangono proprietarie dei sorgenti dei quali commissionano lo sviluppo. La chiusura della soluzione geniale e originale venduta al cliente serve in pratica sempre meno, perché di originale non c'è proprio niente.

 Oggi l'open source è una necessità sempre più spesso, non necessariamente sempre.

 



 

 

lunedì 12 gennaio 2015

Mutatis mutandis

Ultimamente si vedono diversi articoli sulla rete che parlano di oggetti immutabili.

Addirittura classi final con metodi final che non dovrebbero essere nemmeno estensibili per evitare che una sottoclasse possa modificane il comportamento.
Indubbiamente in un contesto multi thread questa pratica risolve in partenza il problema dell'accesso concorrente, tuttavia si pongono dei problemi di ordine pratico rilevanti: per esempio se un oggetto è la radice di un albero e si desidera cambiare il nome del nodo radice, trattandosi di oggetti immutabili, bisognerebbe creare un nuovo oggetto radice e clonare tutti i nodi figli, cioè clonare l'intero albero.

Un approccio che non prevede alcun metodo set potrebbe essere devastante per lo spreco di risorse, prima di tutto per quanto riguarda l'uso della memoria, per non parlare del tempo di esecuzione.
Un altro problema degli oggetti immutabili riguarda i riferimenti alle vecchie versioni degli oggetti, che andrebbero cambiati per aggiornarli ai nuovi.

Ci può essere però una soluzione intermedia e di buon senso che risolve il problema tra oggetti mutabili e oggetti immutabili: il comportamento.

Un oggetto ha una identità, definita da uno o più proprietà, ma ha anche un comportamento, cioè ci sono dei dati che di solito sono archiviati da qualche parte e che possono cambiare senza che si cambi l'identità dell'oggetto. Per esempio prendiamo un oggetto anagrafica che ha nome, cognome indirizzo, per semplicità non ne consideriamo altri. Ora, cambiare il nome e il cognome significa cambiare l'identità della persona, quindi sono campi immutabili, il cui cambiamento è giusto che generi un nuovo oggetto anagrafica. Diverso invece è il discorso per quanto riguarda l'indirizzo, che può cambiare e che dovrebbe riflettere un  dato sul database, cioè la modifica dell'indirizzo si dovrebbe tradurre in un aggiornamento di un campo su un record dell'archivio.

In questo modo il campo indirizzo è mutabile, ma l'oggetto non muta, muta soltanto il suo "comportamento".
Il concetto di comportamento è più intuitivo se pensiamo a un oggetto che abbia il nome di un giocatore e il suo punteggio, il nome definisce l'identità e dovrebbe essere immutabile, invece il punteggio è mutabile. Dal momento che il punteggio magari è archiviato in un unico luogo (potrebbe essere una cache ma questo complicherebbe questo ragionamento, perché una cache non è altro che una copia più veloce dei dati), il riferimento degli altri oggetti all'oggetto giocatore non cambia.

Potrebbe essere questo compromesso una soluzione di buon senso. Peccato che tutto il mondo Java Enterprise non si comporti in questo modo e gli entity siano a tutti gli effetti una cache che copia spesso il modello delle tabelle sul db, essendo oggetti completamente mutabili.

La domanda che ci si può porre a questo punto è se gli oggetti del dominio applicativo possano essere degli entity o se invece non sarebbe più corretto ricorrere a oggetti che facciano da proxy verso gli entity, facendo venire i capelli bianchi a qualche manager di progetto, per i tempi di sviluppo iniziale... chissà... lascio aperto il dubbio ma credo di sì.


lunedì 5 gennaio 2015

Singleton con gli Enum

Chiudiamo questa breve carrellata sui pattern creazionali con la presentazione di una variante del Singleton Pattern.
L'intento di un pattern singleton è assicurare che una classe abbia soltanto un'istanza e che fornisca un punto globale per accedervi.

Questo implica che bisogna impedire agli altri programmatori di creare le proprie istanze, per non venire meno alla specifica di avere una sola istanza di questa classe.
Perché si debba volere una sola istanza è una scelta dell'architettura, che si può condividere o meno.
Personalmente ritengo che le singleton siano una pratica di programmazione non ottimale, soprattutto se diverse parti possono scriverci: espongono al rischio di avere dei problemi, tuttavia bisogna riconoscere alle singleton che possono tornare comode, mi viene da pensare per esempio ad una cache di dati o a un  insieme di proprietà di configurazione da accedere in sola lettura.

Le classi singleton sono spesso un problema, infatti negli ambienti managed non si possono realizzare usando semplicemente un metodo statico, che è pratica sconsigliata sia con Java Enterprise, che con Android e in generale è sconsigliabile in tutti quei casi dove ci possono essere diverse virtual machine o nodi paralleli.

La novità qui è che si può definire un costruttore privato oppure un tipo enum.

public enum Paolo {
  INSTANZA;

  public void faiQualcosa() {
  ...
  }
}

Non è una tecnica molto utilizzata, perché pochi la conoscono, però il tipo enum a un solo elemento rappresenta la soluzione migliore per implementare un singleton.

In caso di deserializzazione non si creano altre istanze e non c'è bisogno di scrivere altro codice per questo, come avviene con gli altri approcci.

Riferimenti presi da "Design Pattern Java Workbook" di Steven John Metsker ed. Eddison Wesley e Effective Java di Joshua Bloch editore Pearson Informatica.

lunedì 29 dicembre 2014

Oggetti Builder

Vediamo oggi brevemente un'altra tecnica per la creazione di oggetti in Java: i Builder.

Abbiamo già visto nell'articolo precedente come in linguaggi C-like come Java manchino i nomi nei parametri passati nelle chiamate.
Questo diventa un problema nella leggibilità del codice soprattutto nei costruttori, dove oltretutto siamo obbligati a specificare soltanto un nome di metodo uguale al nome della classe.
I metodi factory presentati la volta scorsa non risolvono il problema dei nomi dei parametri e con il crescere del numero di parametri da passare, la difficoltà di comprensione e lettura del codice può solo peggiorare.
Il Builder Pattern risolve questo problema definendo una classe la quale mantiene internamente i parametri sia obbligatori che facoltativi che saranno da passare poi al costruttore vero della classe per la quale si desidera creare un'istanza.

La classe Builder deve avere dei metodi con un nome significativo per ciascun parametro da passare.
Un metodo "build" della classe builder stanzia la classe, passando i parametri valorizzati con i metodi dei quali scrivevo prima.

Si potrebbe obiettare che è sufficiente chiamare un costruttore senza parametri e poi usare dei metodi setter per passare i parametri. Purtroppo però questo presenta un altro svantaggio: usando i metodi setter non c'è un unico punto dove si può fare la validazione, cioè se diversi parametri devono concorrere per un criterio, che stabilisca la validità della creazione di un oggetto, è quasi impossibile verificare tale criterio, se i parametri sono passati con diversi metodi "set".

Il builder con il metodo "build" può concentrare il processo di validazione in un unico punto.

Inoltre JavaBeans, suggerendo di usare dei metodi setter, rende impossibile creare una classe immutabile, che oggi più che mai è importante, visto che l'hardware con il processori multi core a basso costo permette sempre di più di avere programmi con parti che girano in parallelo.

In Java l'abstract factory è realizzato dal metodo newInstance dell'oggetto Class. Purtroppo però newInstance chiama un costruttore senza parametri, con le limitazioni già discusse.

Anche per questi consigli e per vedere un esempio, si faccia riferimento all'ottimo Effective Java di Joshua Bloch.

A differenza di Bloch però, mi permetto di notare che è meglio non ricorrere a classi Builder statiche, proprio per potere usare i builder anche in ambienti multi thread, dove le parti statiche sono una fonte di problemi...