n° 219
Novembre 2017
Dicembre 13, 2017, 05:36:29 *
Benvenuto! Accedi o registrati.
Hai dimenticato l'e-mail di attivazione?

Accesso con nome utente, password e durata della sessione
Notizia:
 
   Indice   Linux Windows Techassistance Gameassistance videogame hardware Aiuto Ricerca Agenda Downloads Accedi Registrati  


* Messaggi recenti
Messaggi recenti
Pagine: [1]   Vai giù
  Stampa  
Autore Discussione: Consigli sull'applicazione dei fondamenti OOP  (Letto 1173 volte)
0 utenti e 1 Utente non registrato stanno visualizzando questa discussione.
DavideV
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 7


Mostra profilo E-mail
« inserita:: Giugno 13, 2017, 09:33:46 »

Buongiorno,

Ho iniziato a studiare C# a febbraio e i concetti più evoluti non riesco ancora a metterli in pratica, soprattutto quelli che girano intorno a classi e metodi generici. Il post premetto sarà un po' lungo...

Sto sviluppando un'applicazione Windows Forms che mi permette di analizzare ed archiviare automaticamente una serie di report in formato .pdf che mi giungono su un'e-mail aziendale. I report sono (finora) di due tipi e le operazioni da svolgere sono sostanzialmente identiche su entrambi, tranne qualche piccola differenza nell'analisi dei dati (uno per esempio è mensile e l'altro annuale).

Per gestire la cosa ho creato una classe base astratta in cui ho dichiarato i membri comuni e i metodi astratti che poi ho implementato (in override) in due classi derivate, una per ciascuna tipologia di report.

Uno dei primi problemi li ho riscontrati nel blocco Main al momento di istanziare la classe giusta. Avendo creato una Enum apposta per i due tipi ho in pratica lasciato decidere a un blocco switch quale delle due derivate istanziare:

Attestazione attestazione;
switch (tipo)
{
    case TipoAttestazione.Accessori:
        attestazione = new Accessori();
        break;
    case TipoAttestazione.Efficienza:
        attestazione = new Efficienza();
        break;
    default:
        break;
}

a essere sincero non mi sembra una soluzione molto ortodossa, inoltre avendo dovuto dichiarare l'istanza al di fuori dello switch ho dovuto dichiararla come Attestazione (che è la base), e quindi tutti i futuri riferimenti mi trovo a doverli fare non all'istanza ma alla classe, altrimenti invece di chiamarmi i campi della derivata mi chiama quelli della base e in alcuni casi si generano errori.

Oltretutto, sebbene con l'override dei metodi abbia evitato di scrivere una marea di blocchi condizionali, la cosa non è risolta affatto. Ci sono infatti altri metodi di altre classi per i quali l'uno o l'altro tipo di report fa la differenza, ma sono classi che non posso derivare dalla base e che comunque stanno in altri namespace e preferisco lasciare che siano separati - per esempio, la gestione della comunicazione con il server Exchange.

Insomma ci sono molti metodi fatti più o meno così (sto a casa col cellulare, perciò vado per grandi linee)

public void MioMetodi(TipoAttestazione tipo, AltroParametro X)
{
    switch (tipo)
        case TipoAttestazione.Accessori:
            // elabora x in un certo modo
        case TipoAttestazione.Efficienza:
            // elabora x in un altro modo
}

e mi chiedo: possibile mai?

Sono stato vago, lo so, forse proprio perché non ho nemmeno io inquadrato bene la problematica, ma sono sicuro che sto sbagliando da qualche parte e se qualcuno mi riuscisse a indicare una strada gliene sarei davvero grato.

Grazie, Davide.
Registrato
ubuntumate
Newbie
*

Karma: +10/-0
Scollegato Scollegato

Messaggi: 39


Mostra profilo
« Risposta #1 inserita:: Giugno 13, 2017, 10:37:14 »

Ciao.

Non sono nelle condizioni di poterti suggerire l'approccio per sfruttare a pieno le caratteristiche OO di C#, perché non conosco il linguaggio e perché non ho alcuna competenza nel software engineering, e di conseguenza nelle metodologie OO.
Mi limito a suggerirti di effettuare una bella ricerca nel presente forum per cercare i manuali sul software engineering, in particolare quelli che trattano la programmazione ad oggetti. Un buon punto di partenza è la sezione del forum denominata "Bookshelf", all'interno della quale troverai numerose bibliografie stilate da M.A.W.1968 insieme ai suoi preziosi consigli.
Registrato
alex.75
invioattach
Full Member
***

Karma: +14/-4
Scollegato Scollegato

Messaggi: 357



Mostra profilo WWW
« Risposta #2 inserita:: Giugno 16, 2017, 02:02:40 »

Quando dici "istanziare la classe giusta" credo tu sua sulla via corretta, non capisco peró i tuoi dubbi/problemi ("...mi chiama quelli della base e in alcuni casi si generano errori").

Provo a darti un suggerimento, valuta tu se pertinente e valido o meno.
Invece di gestire le varie tipologie di documento in una sola classe, gestiscile ognuna in una classe specializzata.
In base al tipo di documento da elaborare verrá istanziato il relativo "worker".
Dato che molti metodi sono uguali dovresti usare anche una classe base astratta che implementa i metodi comuni, ma che definisce metodi astratti che devono essere implementati nelle classi derivate.
(Per brevitá ometto questo nell'esempio che segue).

Esempio:
 
Codice:
public enum TipoAttestazione
    {
        Accessori,   Efficienza,
    }

    public interface IEsecutore  // estrattore, generatore, costruttore, che "lavoro deve svolgere" ?
    {
        void Metodo1(string documento);
        string CreaDocumentoFinale();
    }

    public class EsecutorePerAccessori : IEsecutore
    {
        public string CreaDocumentoFinale() { throw new NotImplementedException(); }
        public void Metodo1(string documento) { throw new NotImplementedException(); }
    }

    public class EsecutorePerEfficienza : IEsecutore
    {
        public string CreaDocumentoFinale() { throw new NotImplementedException(); }
        public void Metodo1(string documento) { throw new NotImplementedException(); }
    }


    public class Main
    {
        public void Run()
        {
            var doc = "PDF da email";
            var tipoDocumento = TipoAttestazione.Accessori;

            IEsecutore esecutore;
            switch (tipoDocumento)
            {
                case TipoAttestazione.Accessori:
                    esecutore = new EsecutorePerAccessori();
                    break;
                case TipoAttestazione.Efficienza:
                    esecutore = new EsecutorePerEfficienza();
                    break;

                default:
                    throw new Exception($@"Il tipo documento ""{tipoDocumento}"" non é supportato.");
            }

            esecutore.Metodo1(doc);
            var documentoFinale = esecutore.CreaDocumentoFinale();
        }

    }

Ripeto, non mi é chiaro quali problemi incontri nell'usare una classe comune per il report.
Forse dichiarare "Attestazione" come astratta e definire due classi derivate per le specifiche tipologie di report potrebbe aiutarti.

Ciao,

Alex
Registrato
DavideV
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 7


Mostra profilo E-mail
« Risposta #3 inserita:: Giugno 20, 2017, 04:58:03 »

Ciao a tutti e scusate del ritardo...

Non è che abbia problemi, il codice funziona. Il punto è se sia efficiente o meno... per come è improntata la programmazione a oggetti, se ripeto troppe volte lo stesso codice secondo me è un sintomo che c'è qualcosa nella logica che non va.

Nel caso specifico, mi viene in mente che forse dovrei iniziare a pensare di implementare i Generics.
Registrato
alex.75
invioattach
Full Member
***

Karma: +14/-4
Scollegato Scollegato

Messaggi: 357



Mostra profilo WWW
« Risposta #4 inserita:: Giugno 21, 2017, 12:41:17 »

Performance, te ne devi davvero preoccupare?
Se la tua esecuzione eseguita 1000 volte impiega 100 secondi ed ottimizzata impiega 98 secondi, non penso tu debba "spremere" il codice per avere performance migliori (minor uso della CPU e della memoria).
Piuttosto metti in primo piano leggibilitá e manutenibilitá.
Pensa che ogni riga del tuo codice la scrivi una sola volta ma sará letta centinaia di volte (tu stesso la hai giá letta decine di volte). Se il codice é facile da leggere (e quindi da modificare) c'é un enorme risparmio di tempo nello sviluppo; é questo il "costo" del codice, non le performance (in casi come questo: un elaboratore di documenti).
Comunque molto spesso l'efficienza dell'esecuzione del codice é la diretta conseguenza della semplicitá dello stesso.

Se istintivamente riesci da solo a criticarti per aver scritto codice duplicato sei un buon programmatore.
Immagino non sia la prassi nella tua azienda ma perché non chiedi che qualcuno ti faccia una code review delle tue modifiche/implementazioni ?
Se é una cosa "nuova" puoi elencarne al tuo manager i vantaggi;
condivisione della conoscienza, correzione (preventiva) di bug, refactoring del codice per divenire piú semplice, comprensibile e performante.
Se invece non sei abbastanza soddisfatto della code review fatta prova a chiedere a qualcun altro.

La tua richiesta iniziale é un po' troppo generica (per me) e senza vedere il codice da analizzare non so aiutarti in questo.
Penso che i primi 7 o 10 video della serie Clean Code di R. Martin possano davvero esserti utili.

nel codice che hai postato:
public void MioMetodi(TipoAttestazione tipo, AltroParametro X)

perché il metodo riceve il tipo dell'enumeratore?
É l'implementazione di MioMetodo che deve essere differente, ovvero deve esistere nello specifico tipo (il quale verrá risolto solo a runtime).
Nel mio esempio l'ho implementata in EsecutoreX ma é la stessa cosa se la implementi in "Accessori" o "Efficienza".
Lo switch andrebbe usato per differenziare il tipo da istanziare per l'elaborazione corrente, non per eseguire codice alternativo (come hai giá notato).

Affermi che ci sono impedimenti tecnici a questo:
Citazione
Ci sono infatti altri metodi di altre classi per i quali l'uno o l'altro tipo di report fa la differenza, ma sono classi che non posso derivare dalla base e che comunque stanno in altri namespace e preferisco lasciare che siano separati - per esempio, la gestione della comunicazione con il server Exchange.

Dividi il problema in piú azioni e pensa sempre alla "responsabilitá" di ogni componente/classe.
La gestione della comunicazione con Exchange non mi pare centri nulla con l'elaborazione del documento da parte del componente predefinito allo scopo (che ovviamente non é il metodo main !).
Non dico che sia semplice, sono convinto che ci siano realmente delle dipendenze nel codice che impediscono di "lavorare" come si dovrebbe.
Non sono un esperto dei Design Patterns ma forse il "Adapter (o Wrapper)" é la risposta al tuo problema.
Qui "Clean Code" puoi aiutarti ancora, spendere una decina di ore di tempo per vedere quei primi video é un consiglio che rinnovo.
Non credo i Generics siano la risposta ai tuoi dubbi, hanno un altro scopo. Ti consiglio di non fissarti su questi.

Attualmente non mi viene in mente nessun progetto da indicarti come "esempio" concreto di una buona implementazione di un caso simile ma ovviamente ne esisteranno centinaia (su GitHub o simili), magari qualcun altro sa indicartene qualcuno valido da cui attingere.

Alex

Registrato
Pagine: [1]   Vai su
  Stampa  
 
Vai a:  

Copyright © 2017 Edizioni Master SpA. p.iva : 02105820787

Tutti i diritti di proprietà letteraria e artistica riservati. - Privacy



powered by Simple Machines