Come scrivere codice procedurale in un linguaggio orientato agli oggetti?

0

Recentemente ho iniziato a lavorare nel dominio della generazione di mesh . I miei programmi di solito contengono grossi pezzi di codice procedurale composti da diverse fasi. Per esempio.

class MeshAlgo1
{
    /* A very long function */
    void DoSomething(MeshData data)
    {
       // Phase 1
       ...
       // Phase 2
       ...
       // Phase N

    }
};

Vorrei suddividere queste funzioni membro lunghe in funzioni più piccole per una migliore leggibilità e manutenibilità.

class MeshAlgo1
{
    /* Shorter function! :-) */
    void DoSomething(MeshData data)
    {
       DoSomething1();
       //...
       DoSomething2();
       //...
       DoSomethingN();
    }
};

Ma poiché questo codice è procedurale, DoSomething1 (), DoSomething2 (), ecc non sarebbe indipendente l'uno dall'altro e quindi non si adatta allo stile OOP. Cioè, DoSomething2 () presuppone che DoSomething1 () sia stato chiamato prima di esso. Mi piacerebbe sapere qual è il modo corretto di scrivere questo codice in stile Object Oriented. Grazie mille.

    
posta johngreen 22.07.2015 - 14:21
fonte

4 risposte

1

Anche se sono d'accordo con @Philipp che un passaggio più esplicito di parametri può risolvere il problema, il codice procedurale tende a lavorare su alcuni "dati globali" (o su alcune variabili membro), e cambiare questo potrebbe avere un impatto troppo grande sul exsiting codice base da fare facilmente. Per questo caso, posso pensare a un approccio diverso per risolvere questo problema:

DoSomething2() assumes that DoSomething1() has been called before it.

Ma perché? Forse DoSomething1 deve inizializzare una variabile membro che dovrebbe essere inizializzata da DoSomething2 :

class MeshAlgo1
{
    MyMesh mesh;    

    private void DoSomething1()
    {
        mesh=new MyMesh();
        // initialize 'mesh'
    }

    private void DoSomething2()
    {
        // ... prepare Phase 2 here ...
        // ...
        mesh.DoSomethingSpecial();
        // ...
    }
}

Quindi, se hai paura che qualcuno modifichi involontariamente l'ordine, puoi aggiungere un controllo del tempo di esecuzione al tuo metodo per farlo fallire più velocemente e significare più chiaramente cosa è andato storto:

    private void DoSomething2()
    {
        if(mesh==null)
           throw new Exception("mesh has to be initialized before calling DoSomething2");
        // ... prepare Phase 2 here ...
        // ...
        mesh.DoSomethingSpecial();
        // ...
    }

Inoltre, una corretta denominazione dei metodi sarà di aiuto, e l'aggiunta di un commento sulle precondizioni necessarie ridurrà anche il rischio di sbagliare l'ordine.

Ciò che non ottieni in questo modo è un controllo del tempo di compilazione dell'ordine, naturalmente, ma in molti scenari del mondo reale è sufficiente un controllo del tempo di esecuzione.

    
risposta data 22.07.2015 - 16:12
fonte
1

Altri menzionano metodi per classi che sono il modo orientato agli oggetti. Sembra che il tuo compito sia ordinare in sicurezza le procedure. Bene, una procedura è (astrattamente) un comportamento di un oggetto. Sei bloccato in una singola classe per gestire queste procedure come metodi o puoi creare due classi? Dì, DoerOne con DoerOne::DoSomethingOne() e DoerTwo con DoerTwo::DoSomethingTwo() . Ora, con due oggetti di tipi di classi diversi, il problema di sequenziare le procedure diventa sequenziamento del ciclo di vita di questi due oggetti (ad es. Quando ciascuno è istanziato). Come lo faresti?

    
risposta data 22.07.2015 - 16:33
fonte
0

Quando temi che qualcuno possa chiamare i metodi nell'ordine sbagliato, sembra indicare che ognuno dei metodi privati trasforma i dati da uno stato in uno stato diverso. Quando questo è il caso, potresti rappresentare ciascuno di questi stati con un tipo diverso. Ogni metodo accetta quindi un oggetto dello stato precedente come input e restituisce un oggetto che rappresenta i dati in uno stato diverso come output. Questo dovrebbe apparire come questo:

private MeshDataState1 DoSomething1(MeshDataState0 data) {
    //...
}

private MeshDataState2 DoSomething2(MeshDataState1 data) {
    //...
}

private MeshDataStateFinal DoSomething3(MeshDataState2 data) {
    //...
}


public DoEverything(MeshDataState0 data) {

    MeshDataState1 dataState1 = DoSomething1(data);

    MeshDataState2 dataState2 = DoSomething2(dataState1);

    MeshDataStateFinal dataStateFinal = DoSomething3(dataState2);
}

Tuttavia, un'implementazione più fedele del paradigma orientato agli oggetti sarebbe quando ogni stato avrebbe il metodo per trasformarsi allo stato successivo:

public DoEverything(MeshDataState0 data) {

    MeshDataState1 dataState1 = data.toState1();

    MeshDataState2 dataState2 = dataState1.toState2();

    MeshDataStateFinal dataStateFinal = dataState2.toStateFinal();
}

o più breve e senza le variabili intermedie:

public DoEverything(MeshDataState0 data) {

    MeshDataStateFinal dataStateFinal = data.toState1().toState2().toStateFinal();
}
    
risposta data 22.07.2015 - 14:46
fonte
0

Secondo me, questi stessi metodi (come entità separate) non devono essere a conoscenza di alcun cambiamento di stato. La preoccupazione per lo stato dovrebbe essere sotto forma di un'invocazione condizionale.

Potrei sbagliarmi, potrei essere sottovalutato, ma semplicemente lo strutturerei in questo modo:

class MeshAlgo1
{
    void DoSomething(MeshData data)
    {
       DoSomething1();

       if(data.ReadyForSomething2)
           DoSomething2();

       if(data.ReadyForSomething3)
           DoSomething3();
    }
};

Dove .ReadyForSomething2 è il cambiamento di stato che si è verificato come risultato di una chiamata "riuscita" a DoSomething1() .

    
risposta data 22.07.2015 - 15:51
fonte

Leggi altre domande sui tag