Perché è male scrivere qualcosa in linguaggio X come se si stesse scrivendo un programma in linguaggio Y in termini di utilizzo di un paradigma di codifica condiviso [chiuso]


25

Qualche tempo fa, ho posto una domanda su SO su qualcosa scritto in C ++, ma invece di ottenere una risposta al problema in questione, i commenti sono impazziti sul mio stile di codifica, anche quando ho indicato che era un pezzo di codice WIP e che intendevo ripulirlo più tardi quando avevo in esecuzione il case base. (Ho ottenuto così tanti voti negativi che ho deciso di porre la domanda, poiché il mio rappresentante su SO è già quasi abissale)

Mi sono chiesto perché la gente adotta un atteggiamento così duro "sei un noob, vai a farti fottere". Ero accusato di scrivere C ++ come se fosse Java. Qualcosa che non riesco a capire e che mi sconcerta ancora.

Ho programmato in diversi linguaggi OOP per un certo numero di anni, anche se a intervalli. Scelgo la lingua da utilizzare in termini di librerie disponibili e ambienti di esecuzione ottimali per il lavoro da svolgere. Adotto i modelli di progettazione nel codice OOP e sono abbastanza sicuro che il mio uso dei modelli sia corretto e che OO sia saggio, posso tenerne uno mio. Capisco la cassetta degli attrezzi OOP, ma scelgo di usare gli strumenti solo quando penso che sia davvero necessario, non usare semplicemente un trucco per mostrare il mio ingegno di programmazione. (Che io so non sono di prim'ordine, ma penso che non siano neanche a livello di n00b).

Progetto il mio codice prima di scrivere una sola riga. Per definire i test, elenco gli obiettivi di una determinata classe e i criteri di test a cui deve aderire. Poiché è più facile per me creare diagrammi di sequenza e quindi scrivere codice, ho scelto di scrivere i miei test dopo che l'interfaccia è diventata ovvia.

Devo ammettere che nel pezzo di codice che ho pubblicato nella domanda, stavo ancora usando i puntatori, invece di usare i puntatori intelligenti. Uso RAII ogni volta che posso. So che la RAII corretta significa salvaguardare dai punti null, ma lavoro in modo incrementale. Era un lavoro in corso e intendevo ripulirlo più tardi. Questo modo di lavorare è stato fortemente condannato.

A mio avviso, dovrei prima avere un esempio funzionante in modo da poter vedere se il caso base è un modo di pensare praticabile. Mi capita anche di pensare che ripulire il codice sia qualcosa che è tipico della fase di refactoring dell'agile, dopo che il caso di base è stato dimostrato. Devo ammettere che sebbene stia lentamente ottenendo lo standard Cxx, preferisco usare quello che ho capito, invece di correre il rischio di usare concetti che devo ancora padroneggiare nel codice di produzione. Ogni tanto provo nuove cose, ma di solito nei progetti di gioco che ho dalla parte, solo per quello scopo.

[modifica] Vorrei chiarire che il suggerimento di Gnat [1] non è stato mostrato nella ricerca che ho fatto prima di iniziare a porre la mia domanda. Tuttavia, sebbene il suo suggerimento copra un aspetto della domanda, la domanda a cui si è collegato non risponde al cuore della mia domanda, ma solo in parte. La mia domanda riguarda maggiormente la risposta che ho ricevuto al mio stile di codifica e agli aspetti professionali della gestione di diversi stili di codifica e livelli (apparenti) di abilità. Con la mia precedente domanda su SO e la sua risposta è un caso emblematico. [/modificare]

La domanda allora è: perché deridere qualcuno che non usa il tuo stile di programmazione?

Le questioni / suddivisioni a portata di mano per me sono:

  • Perché sarebbe una cattiva pratica di programmazione utilizzare più codice soggetto a errori in situazioni di prototipo, se il refactoring lo rendesse più robusto in seguito?
  • Come può il programma scritto in C ++ essere come se fosse stato scritto in Java? Ciò che lo rende un cattivo programma, (considerando che ho indicato l'intento dello stile attuale e il lavoro pianificato per migliorare?)
  • Come sarei un cattivo professionista se scegliessi di usare un costrutto utilizzato in un determinato paradigma di programmazione (ad es. OOP / DP)?

[1] Sviluppa velocemente e buggy, quindi correggi gli errori o sii lento, attento ad ogni riga di codice?


5
Potrebbe essere meglio porre alla gente in The C ++ Lounge questa domanda. Indossa prima la tua tuta ignifuga.
Robert Harvey,

48
La gente del C ++ è una razza insolita tra i programmatori. Il tuo toolkit Java è una cassetta degli attrezzi sensibile e ben comprensibile con strumenti familiari in una custodia imbottita in pelle che funzionerà in qualsiasi negozio di utensili. Il C ++ è un intero capanno con una sega circolare, un trapano elettrico e alcuni strumenti che nessuno riconosce, tutti che la folla del C ++ può gestire come ninja. Li affligge quando qualcuno entra e ripone uno strumento sullo scaffale nel posto sbagliato. Sono la "misura due volte, taglia una volta" dei programmatori.
Robert Harvey,

13
Forse è la stessa reazione che avresti al codice Java scritto come FORTRAN: tutto il codice in una singola classe, nessuna raccolta, solo array di dimensioni fisse, con intvariabili separate per tenere traccia della lunghezza effettiva.
Kevin Cline,

14
Se scrivi C ++ come Java, probabilmente finirai con troppe allocazioni di heap, il che renderà il tuo programma più performante di quanto potrebbe. Le lingue tendono ad essere progettate e ottimizzate per promuovere determinati schemi, e se li rompi, le cose tenderanno ad andare peggio per te.
Gort il robot

5
Hai pubblicato codice online per ottenere qualche forma di aiuto. Mi rendo conto che non volevi / non ti aspettavi tutti i feedback sul tuo stile, ma preferiresti non avere alcun aiuto? Prendi il buono con il cattivo. I programmatori devono cercare errori nel codice; è quello che facciamo.
JeffO,

Risposte:


26

Senza vedere il codice in questione, ci sono alcuni modi per scrivere il codice Java in C ++, alcuni peggiori di altri.

  1. Ad un estremo, c'è il layout del tuo sorgente come Java: tutto in un file, tutto all'interno della definizione della classe, ecc .:
    class HelloWorldApp {
    public:
        void main() {
            cout << "Hello World!" << endl;
        }
    };
    Ecco come sarebbe strutturato Java. È tecnicamente legale in C ++, ma mettere tutto nel file header e tutto in linea (definendolo nella dichiarazione di classe) è uno stile terribile e ucciderà le tue prestazioni di compilazione. Non farlo
  2. OO eccessivamente - Per semplificare eccessivamente, in Java, è il Regno dei sostantivi , dove tutto è un oggetto. Un buon codice C ++ (cioè idiomatico) ha maggiori probabilità di utilizzare funzioni gratuite, modelli, ecc., Invece di cercare di stipare tutto in un oggetto.
  3. No RAII - Ne hai già parlato - usando i puntatori e la pulizia manuale invece dei puntatori intelligenti. Il C ++ ti offre strumenti come RAII e puntatori intelligenti, quindi un buon codice C ++ (cioè idiomatico) usa quegli strumenti.
  4. Nessun C ++ avanzato - Le basi di Java e C ++ sono abbastanza simili, ma una volta entrati in funzionalità più avanzate (modelli, libreria di algoritmi di C ++, ecc.), Iniziano a divergere.

Tranne il numero 1, nessuno di questi rende un programma C ++ un cattivo programma, ma non è nemmeno il tipo di codice su cui preferisco lavorare come programmatore C ++. (Non mi piacerebbe nemmeno lavorare con Perl non idiomatico o in stile C, Python non idiomatico, ecc.) Un linguaggio ha i suoi strumenti, modi di dire e filosofia, e un buon codice usa quegli strumenti e modi di dire invece di provare a usare il minimo comune denominatore o cercando di riprodurre l'approccio di un'altra lingua. Scrivere codice non idiomatico in una particolare lingua / dominio problematico / qualunque cosa non renda qualcuno un cattivo programmatore, significa solo che hanno di più da imparare su quella lingua / dominio problematico / qualunque cosa. E non c'è niente di sbagliato in questo; c'è una lista molto lunga di cose che ho di più da imparare, e in particolare C ++ ha un sacco di cose da imparare.

Per quanto riguarda la particolare domanda di scrivere codice soggetto a errori con l'intento di ripulirlo in seguito, non è in bianco e nero:

  • Se un codice prototipo non riesce a gestire tutte le possibili eccezioni e tutti i possibili casi angolari, è prevedibile. Fallo funzionare, poi fallo funzionare in modo robusto. Nessun problema.
  • Se un codice prototipo è scritto in un semplice stile o in un cattivo design (cattivo per il linguaggio dato e i suoi modi di dire, un design fondamentalmente cattivo per il problema, ecc.), Allora a meno che tu non lo stia scrivendo come un buttare via prova di concetto, non stai guadagnando nulla.

Per utilizzare i puntatori non elaborati rispetto ai puntatori intelligenti come esempio, se si intende lavorare in C ++, l'utilizzo di RAII e i puntatori intelligenti sono abbastanza fondamentali che dovrebbe essere più veloce scrivere codice in questo modo piuttosto che tornare indietro e ripulirlo in seguito. Ancora una volta, non farlo non significa che qualcuno sia un cattivo programmatore, poco professionale, ecc., Ma significa che c'è ancora molto da imparare.


11
c'è perl idiomatico?
maniaco del cricchetto

21
@Onno Quando chiedi "Come posso martellare questa vite nel muro senza che si pieghi?" tutti ti diranno di non usare un martello.
Sjoerd,

9
@Onno In C ++, i puntatori e newsono considerati i powertools. La memorizzazione automatica è lo strumento manuale.
Sjoerd,

9
@Onno: devi capire che i corsi di programmazione in C ++ tendono ad essere in ritardo nella loro adozione di modi di dire moderni e che ciò che è considerato un buon C ++ si è evoluto enormemente da quando è stato inventato il C ++.
Bart van Ingen Schenau,

7
A causa della storia del C ++ e dell'enorme numero di programmatori che hanno imparato la lingua prima che apparissero molte delle funzionalità più avanzate della lingua, c'è ancora un sacco di C ++ scadente che viene ancora scritto da persone che scrivono come facevano dieci anni fa. La delicatezza di cose come RAII riguarda il fatto che tutti possano usare i metodi moderni in modo che possiamo smettere di vedere gli stessi guasti prevedibili.
Gort il robot

42

Ogni linguaggio di programmazione ha una serie di idiomi e buone pratiche, che di solito portano a un codice elegante, corretto e performante. Ecco alcune delle peggiori pratiche che vanno benissimo in qualche altra lingua:

  • Griderò se scrivi for ($i = 0; $i < 42; $i++) { … }in Perl, ma non in PHP
    (in Perl, le variabili dovrebbero essere dichiarate e tali loop dovrebbero iterare su un intervallo)
  • Piangerò se scrivi new Foo()in C ++ senza una buona ragione, ma non in Java
    (C ++ non ha Garbage Collection, quindi RAII dovrebbe essere usato. Noobs perderà la memoria altrimenti)
  • Farò rabbrividire se dichiarerai tutte le tue variabili all'inizio della tua funzione, tranne in C89, Pascal o JavaScript.
  • Affronteròpalpal se metti tutte le tue funzioni in una classe in Python, ma non in Java
    (Python è un linguaggio multi-paradigma invece forzando "OOP" e supporta le funzioni al livello più alto)
  • Piangerò se tu return nullalla Scala, ma non in un linguaggio simile al C
    (perché Scala ha un Optiontipo)
  • Mi lamenterò se scrivi un algoritmo ricorsivo in Java, ma non in OCaml
    (perché lo stack di Java trabocca rapidamente, mentre OCaml ha l'ottimizzazione delle chiamate di coda)
  • ...

È facile usare una nuova lingua come se fosse qualcosa che conosci, e per fortuna funzionerà anche. "Puoi scrivere Fortran in qualsiasi lingua". Ma non vuoi davvero ignorare le caratteristiche specifiche offerte dal linguaggio X, perché è probabile che X offra qualche vantaggio rispetto a U.

" Scelgo la lingua da utilizzare in termini di librerie disponibili e ambienti di esecuzione ottimali per il lavoro da svolgere ", ma se questa lingua X sia effettivamente migliore della lingua U per il lavoro da svolgere dipende anche dalla familiarità con quella lingua, oppure quanto tempo ci vorrà per familiarizzare a sufficienza per usarlo bene. Non potresti produrre una motosega pesante solo perché taglia il legno più velocemente, quando in realtà vuoi il tuo vecchio coltello da penna perché si adatta perfettamente alla tua mano. A meno che tu non voglia davvero far cadere un albero.

Ma la tua domanda è più su un problema culturale : apprendere tutte le migliori pratiche richiede molto tempo e i neofiti fanno il maggior numero di domande, mentre i guru rispondono a loro. Ma ciò che è ovvio per un guru non è ovvio per un principiante, e talvolta i guru lo dimenticano. Come principiante, la soluzione non è smettere di porre domande. Tuttavia, si può mostrare un'apertura per apprendere e applicare le migliori pratiche, ad esempio cercando di ripulire il codice il più possibile prima di mostrarlo agli altri. La maggior parte delle lingue ha alcune best practice di base che sono facili da imparare, anche quando l'intera curva di apprendimento è in realtà molto lunga.

Un problema comune è che le persone che non conoscono la programmazione ignorano qualsiasi rientro o altra formattazione e quindi vengono confuse perché il loro programma non funziona. Anche io sarei confuso, e il primo passo per capire un programma è assicurarsi che sia perfettamente strutturato. Quindi, semplici errori come una citazione di chiusura dimenticata o una virgola mancante diventano improvvisamente evidenti. Confido che pratichi già una buona formattazione e qui è una metafora di altre migliori pratiche: le migliori pratiche prevengono gli errori, le migliori pratiche rendono più facile la ricerca degli errori, l'applicazione delle migliori pratiche viene prima di trovare il problema .

È troppo economico per dire "Lo riparerò più tardi", quando risolverlo ora avrebbe risolto il tuo problema (inoltre, quella favolosa "fase di pulizia" potrebbe non arrivare mai, quindi l'unica opzione responsabile è farlo nel modo giusto prima volta). Per lo meno, provare a rendere il tuo codice il più buono possibile prima di chiedere aiuto agli altri rende più facile per loro ragionare sul tuo codice, quindi è la cosa educata da fare.


Hai ragione su alcuni dei punti che fai, ma alla fine non penso che tu stia colpendo il segno. Stai assumendo il raggiungimento della conoscenza prima di avere l'esperienza che acquisirà la conoscenza.
Onno,

4
@Onno I miei punti principali (che rispondono alle domande che hai posto) sono che non puoi riportare le abitudini da una lingua e presumere che sia una buona pratica in un'altra e che cercare di scrivere codice pulito dall'inizio sia preferibile alla pulizia più tardi. Pulire il codice - al meglio delle tue capacità - è un must prima di chiedere aiuto agli altri (vedi sscce.org per suggerimenti su buoni frammenti di codice). Scaricare il codice della spazzatura su SO con un "per favore risolvi questo" è inaccettabile, soprattutto se si conosce meglio.
amon,

2
Ma per quanto riguarda la domanda a cui stai accennando: la risposta di jm666 contiene un punto chiave: il guru pensa "perché dovresti chiedere di X se non vuoi diventare tu stesso un X-guru?" Mi trovo troppo spesso in questa mentalità inutile . Un possibile modo per disinnescare è quello di menzionare che sei ancora all'inizio della curva di apprendimento e lo esamineremo più avanti, ma per ora hai questa domanda più immediata a portata di mano. Tuttavia, questo è un problema di comunicazione, non un problema di programmazione.
amon,

6
Sia C ++ che C # hanno optional<T>e Nullable<T>rispettivamente. Inoltre, praticamente tutti perdono memoria in C ++ senza RAII, noob o no.
DeadMG

Trovo che dichiarare le variabili all'inizio di un blocco di solito renda il tuo codice più facile da leggere nella maggior parte delle lingue. In questo modo sai esattamente dove cercare quando hai bisogno di aiuto per ricordare quali tipi sono tutte le tue variabili e simili.
Assapora il

12

Non sono uno sviluppatore C ++ hardcore, ma ...

Perché sarebbe una cattiva pratica di programmazione utilizzare più codice soggetto a errori in situazioni di prototipo, se il refactoring lo rendesse più robusto in seguito?

Una cosa da tenere a mente è che un errore in C ++ di solito significa "comportamento indefinito". In un linguaggio sicuro il peggio che potrebbe accadere è un'eccezione che interrompe immediatamente il programma. In C ++, sei fortunato se ottieni un segfault. È del tutto possibile che il tuo programma continui a fare qualcosa di leggermente sbagliato. Potrebbe anche comportarsi correttamente per un periodo di tempo e manifestare bug molto più tardi, oppure potrebbe comportarsi correttamente tutto il tempo ma alla fine divorare tutta la tua memoria.

In ogni caso, basta un solo errore per togliere completamente l'esecuzione del programma dai binari e in un territorio inesplorato. È probabile che per lo sviluppatore C ++ a tempo pieno, il "caso base" significhi "nessuna possibilità di comportamento indefinito o perdite di memoria".

Come può il programma scritto in C ++ essere come se fosse stato scritto in Java? Cosa lo rende un cattivo programma?

Non credo che ci sia una risposta a ciò che non sarà in gran parte speculativa e supponente. Se vuoi la mia opinione su di esso, però, Java tende ad avere un paio di antipattern associati come il fatto che tutto deve essere un oggetto. Dove in altre lingue che ci si passa un puntatore, funtore, o una funzione, in Java di solito si trovano tonnellate di vacuo e stretto-utile ThingDoers, FooFactoriese IFrobnicatorsche sono solo le funzioni sotto mentite spoglie.

Allo stesso modo, dove in altre lingue potresti passare una semplice tupla o struttura senza nome, in Java per raggruppare anche solo 2 oggetti in un semplice contenitore di dati è necessario predefinire una classe NamedThing di oltre 30 righe con setter, getter e Javadocs. La relativa mancanza di funzionalità di Java costringe i programmatori a fare doppi backflip orientati agli oggetti per fare le cose a volte. Il codice risultante è raramente idiomatico al di fuori di Java.

Quindi c'è il fatto che in C ++ è necessario un grafico a oggetti molto semplificato per gestire manualmente la memoria; di solito un oggetto appartiene esattamente a un altro oggetto. In Java non è necessario seguire vincoli così rigidi, perché Garbage Collector garantisce che le cose vengano ripulite quando non ci sono più riferimenti ad esse. Quindi c'è sicuramente il rischio di un'errata gestione della memoria se si trascrive semplicemente il codice da Java a C ++.

Infine, potrebbe essere solo elitarismo. Non pretendo che costituiscano la maggioranza, ma ho sicuramente visto un sentimento di "Non ho bisogno di un linguaggio che mi tenga la mano e mi impedisca di fare cose stupide" tra alcuni sviluppatori C ++. Nella loro mente C ++ è un linguaggio "reale" e se non riesci a gestire le sue idiosincrasie non sei un "vero programmatore".


1
Gran parte di questo è alleviato con Java 8 - Lambdas cavalca per salvare soprattutto la giornata :-)
Martijn Verburg

@MartijnVerburg D'accordo, enorme passo avanti lì. Ma i problemi tecnologici sono facili da risolvere! Le vecchie abitudini sono dure a morire e gli stimmi ancora più difficili. Diamine, alcune persone si spingeranno fino al punto di lamentarsi che Java non ha bisogno di nessuna di queste "nuove" funzionalità e che è in procinto di diventare il prossimo C ++.
Doval,

Sì, scuoto sempre la testa: Java si evolverà deliberatamente sempre più lentamente rispetto alle altre lingue poiché è un cavallo di battaglia a lungo termine. Tuttavia, la JVM può saltare in avanti in modo leggermente più veloce, il che è ciò che consente a cose come Lambdas di arrivare alla lingua.
Martijn Verburg,

6

Risposta leggermente fuori tema ...

Non preoccuparti: questo è un "comportamento" comune in qualsiasi comunità di esperti. E ad essere sincero, se sei bravo in qualsiasi lingua e incontrerai un codice che è "strano", probabilmente lo criticherai anche tu. (perché, vuoi INSEGNARE).

Sono nel mondo perl - quando vedremo qualcosa del tipo:

$imax=$#array;
$str=""
for($i=0; $i<$imax; $i++) {
    $str = "$str" . $array[$i];
}

invece di:

my $str = join '', @array;

sicuramente lo commenterà - (leggi: insegna all'autore) riguardo al join funzione.

Comunque, troppe critiche sono controproducenti e uno dei migliori esempi è il prossimo: (preso da: http://perl-begin.org/humour/#How_can_I_switch_off_the_T.V..3F )

(Questo bit è stato pubblicato in forma anonima su un pastebot il 23 marzo 2011. È messo qui per i posteri dopo alcune modifiche.) - Anche leggermente modificato

Domanda: come posso accendere la mia TV?

Cosa vuole sentire l'OP?

Ad esempio: individuare il pulsante di accensione / spegnimento del telecomando della TV e premerlo. Il pulsante è generalmente rosso e si trova nella riga più in alto sul telecomando.

La risposta dell'esperto #perl: in primo luogo, cosa intendi con "accensione"? Definiscilo per primo. Copia la tua TV, il telecomando della TV e anche il soggiorno.

... dopo un nopaste:

La tua stanza è brutta. E la TV sembra terribile. Usa Mr. Clean sullo schermo e pulisci prima il tuo salotto. Utilizzare tre mop per la pulizia anziché due. Usa HDMI e non usare mai connettori scart (?), A meno che tu non voglia davvero. Il telecomando della tua TV ha pulsanti illeggibili, pulisci prima. Sei un principiante, quindi leggi:

http://experts.blog/how_to_design_a_future_3D_TV.html http://experts.blog/the_basics_of_tv_repairing.html http://experts.blog/viruses_in_living_room_short_essay.html http://experts.blog/global_chip_html

Ospite dell'IRC: Ma non voglio essere un esperto di TV.

Risposta: Perché vuoi accendere la TV allora ?!


1
Questa risposta non è fuori tema. Spiega esattamente perché qualcuno dovrebbe criticare l'uso non convenzionale di una lingua e perché occasionalmente tale critica automatica potrebbe andare troppo lontano. Mi piace.
trichoplax,

1

Perché sarebbe una cattiva pratica di programmazione utilizzare più codice soggetto a errori in situazioni di prototipo, se il refactoring lo rendesse più robusto in seguito?

Quando si scrive in modo rapido e sporco con la mente di risolvere in seguito, esiste il pericolo di dimenticare qualcosa che è necessario correggere.

Come può il programma scritto in C ++ essere come se fosse stato scritto in Java? Ciò che lo rende un cattivo programma, (considerando che ho indicato l'intento dello stile attuale e il lavoro pianificato per migliorare?)

In java non devi pensare a chi possiede un determinato oggetto, devi solo passare il riferimento e dimenticartene come se niente fosse. Tuttavia in C ++ deve esserci una definizione chiara di chi possiede l'oggetto e chi è responsabile della sua pulizia.

Come sarei un cattivo professionista se scegliessi di usare un costrutto utilizzato in un determinato paradigma di programmazione (ad es. OOP / DP)

Non lo faresti; Il C ++ è un linguaggio multi-paradigma, sembra supportare OOP piuttosto bene, ma può fare anche molte altre cose. Tuttavia, la maggior parte si riduce a utilizzare lo strumento corretto per il lavoro invece di estrarre il martello ogni volta che è necessario guidare un picco appuntito in un po 'di legno.

Il motivo per cui hai avuto una cattiva risposta è che la maggior parte delle persone su SO tende a giudicare le abilità da quanto idiomatico puoi programmare nella lingua che stai chiedendo. Le persone che hanno familiarità con il C ++ tendono a sussultare quando vedono un codice errato che sembra qualcosa che li ha morsi in passato.


Vedo che dimenticare di migliorare potrebbe essere un problema per alcuni, ma tengo un lungo elenco di attività e sono molto disciplinato nell'aggiungere tag todo per questo tipo di lavori. (ecco perché mi piace VS, è abbastanza bravo nella gestione del lavoro) È lo stesso per la documentazione. Non scrivo una riga di codice prima di aver scritto la documentazione per esso. Per quanto riguarda la questione della proprietà, a causa dei diagrammi di sequenza, penso di avere una buona idea di chi ha collegamenti a chi.
Onno,

1
@Onno: Francamente, nessuno sa o si preoccupa di quanto tu sia personalmente bravo a tenere traccia di quella roba. La stragrande maggioranza delle persone che scrivono C ++ che assomiglia a Java o C, non è nemmeno così meticolosa. È molto meglio per loro imparare a fare cose giuste la prima volta che scrivere una nota per tornare indietro e aggiustarlo in seguito, perché l'esperienza dimostra che praticamente non lo fanno mai.
cHao,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.