Come rappresentare un algoritmo come classe?

1

Sto cercando di capire come progettare classi che prendono un input, elaborano e restituiscono un risultato. Più nello specifico, se l'oggetto memorizza i risultati intermedi tra le chiamate di funzione come stato, o se tutti i metodi sono statici e passano i parametri intorno.

Esempio: attualmente sto lavorando su una classe che rileva e rimuove le intestazioni e gli ampli; il piè di pagina dalle pagine di un documento. I passaggi dell'algoritmo sono descritti di seguito:

HeaderFooterFilter

removeHeaderFooterText(pages) ->
  candidates = getBoundaryBlocks(pages);
  toRemove = detectRepeatingBlocks(candidates);
  removeBlocks(toRemove);

Quindi la mia domanda è: dovrebbe pages , candidates , toRemove essere membri della classe, o i metodi dovrebbero essere statici e passarli intorno?

Esiste un modello di progettazione che si occupa delle classi dell'algoritmo?

    
posta levi 19.08.2015 - 18:32
fonte

4 risposte

5

Prima della discussione, nota che

Quando viene fornito un caso di utilizzo aziendale, i programmatori orientati agli oggetti di solito possono arrivare a un certo consenso su come dovrebbe essere la progettazione della superficie (la suddivisione delle responsabilità tra classi e metodi e proprietà pubblicamente accessibili di queste classi).

D'altra parte, quando viene fornito un algoritmo,

  • I programmatori orientati agli oggetti possono ancora ottenere un certo consenso sulla progettazione della superficie, in base agli input, ai passaggi (processo) e alle uscite.
  • C'è molto meno consenso su come partizionare gli interni degli algoritmi, perché questi appartengono ai dettagli di implementazione di cui l'utente dell'algoritmo non si preoccuperebbe.
  • La progettazione orientata agli oggetti non prescrive metodi per progettare algoritmi. La struttura di base dell'algoritmo dovrebbe essere decisa prima, e quindi può essere prodotta una progettazione orientata agli oggetti (incapsulamento).
  • Se il framework di base dell'algoritmo deve essere modificato, ad esempio, per sfruttare un algoritmo più veloce che richiede un framework diverso, la maggior parte di tale progettazione orientata agli oggetti dovrebbe essere rifatta.
  • La progettazione di algoritmi orientati agli oggetti è un argomento completamente diverso, con un'enfasi su blocchi fondamentali (funzionali) che sono riutilizzabili in molti diversi tipi di algoritmi. Una conseguenza è la maggiore riusabilità del codice. Lo svantaggio è che avrebbe richiesto ai progettisti di adottare un approccio diverso agli algoritmi, che in genere non viene insegnato nelle scuole.

Per quanto riguarda la progettazione orientata agli oggetti, la suddivisione di un algoritmo in passaggi dipende dal fatto che l'algoritmo stesso sia visto meglio, dal punto di vista dell'utente , come eseguito in un singolo passo, o come più passaggi.

Ricorda che l'utente dell'algoritmo non ha bisogno di sapere come vengono scomposti i passaggi, a meno che l'utente abbia bisogno di intervenire tra i passaggi, come :

  • Interrompe l'algoritmo tra due passaggi,
  • Controlla i dati dell'algoritmo tra due passaggi,
  • Modifica le impostazioni dell'algoritmo tra due passaggi,
  • ecc.

Se l'algoritmo è visto meglio come un singolo passo, l'implementazione orientata agli oggetti può scegliere di nascondere tutti i dettagli di implementazione, lasciando solo all'utente "input, processo, output". Inoltre, si può fornire un metodo factory o una funzione utility che prende l'input e calcola l'output. Questo metodo factory o utility può utilizzare l'implementazione orientata agli oggetti.

In generale è meglio memorizzare gli stati dell'algoritmo (dati mutabili) nell'oggetto stesso e renderli privati. Ci sono delle eccezioni:

  • No se così facendo aumenta la confusione. Ad esempio, se pages significa una cosa in alcune parti dell'algoritmo, e quindi pages viene riscritta con qualcosa di diverso in altre parti dell'algoritmo. Se questo è il caso,
    • Dovrebbero avere nomi diversi e descrittivi per evitare confusione.
    • Se pages ha le sue transizioni di stato (come una macchina a stati), quella logica dovrebbe essere estratta nella sua classe.

Design della classe per algoritmi che sono parallelizzati

Se viene usato simultaneamente dai suoi utenti (essendo chiamato da più thread), l'algoritmo deve essere bloccato (i suoi dati diventano immutabili dopo che il calcolo principale è terminato), o ha bisogno di protezione basata su lock.

Se l'algoritmo stesso è parallelizzato,

  • Gli stati mutabili dovrebbero essere partizionati e ogni lavoratore dovrebbe avere accesso esclusivo al suo stato mutevole.
  • L'esecuzione dell'algoritmo dovrebbe essere suddivisa in fasi. Una volta completata una fase, i dati di uscita della fase si congelano.
risposta data 19.08.2015 - 19:41
fonte
2

Non c'è niente di sbagliato nella scrittura di pure funzioni come statica nella programmazione orientata agli oggetti. Dato il tuo esempio, non sembra necessario rendere i campi di istanza fuori dai risultati intermedi.

Tuttavia, dovresti essere pronto a refactoring quando e se necessario, in un oggetto reale. Diversi motivi per cui potresti doverlo fare sono:

  • Hai scelto di definire l'algoritmo tramite un'interfaccia (o una base astratta classe) in modo da poter avere diverse implementazioni. Allora hai bisogno di più classi per il varie implementazioni, e avrete bisogno di un oggetto per contenere il stato, che in questo caso lo stato è la scelta tra gli algoritmi.

  • Ti rendi conto che hai bisogno di più di una operazione (funzione) sul oggetto e deve mantenere lo stato tra queste invocazioni.

risposta data 19.08.2015 - 18:43
fonte
1

I am trying to understand how to design classes which take an input, do some processing, and return a result.

C'è un principio nel design orientato agli oggetti chiamato "Tell, do not ask"

Ciò significa in genere che dici a un oggetto di cambiare se stesso, non chiedi a un oggetto i dati che poi manipoli altrove.

Quindi, quando pensi a come gestire qualcosa di simile nel design OO, devi continuare a tornare ai tuoi oggetti e quello che loro stessi fanno. In generale non hai un oggetto che prende input da un altro oggetto, perché fai in modo che gli oggetti facciano il lavoro.

Quindi, prendendo il tuo esempio, ci sono una serie di problemi di progettazione immediati che nascono. Stai passando una raccolta di pagine in un oggetto senza nome che le elaborerà. Probabilmente è meglio lasciare che siano le stesse pagine a gestirlo.

Quindi invece di

removeHeaderFooterText(pages)

prova invece

pages.each |page|
    page.removeHeaderFooterText

Niente quindi al di fuori dell'oggetto stesso della pagina deve sapere come diamine rimuovere il testo dell'intestazione e del piè di pagina dalla pagina.

Hai i passaggi per raggiungere questo come

getBoundaryBlocks
detectRepeatingBlocks
removeBlocks

Subito un nome salta fuori, "Boundary Blocks". Una pagina ha dei blocchi di confine, quindi non ci dovrebbe essere qualcosa al di fuori dell'oggetto Pagina interessato a ottenere blocchi di confine dalla pagina

class Page
    def removeHeaderFooterText
        page_blocks.each |block|
            if block.repeating_block?
               page_blocks.delete(block)

Vedi di avere un oggetto lì dentro di un blocco, che a sua volta sa come determinare se si tratta di un blocco ripetuto o meno. Se è la pagina lo rimuove. La pagina non interessa come il blocco determina se si tratta di un blocco ripetuto o meno.

Questo è solo un breve esempio. Ad essere onesti, non sono aggiornato sulla terminologia del mio documento, quindi potrebbe non corrispondere esattamente a quello che stai cercando di fare.

tl; dr versione

Il punto è che nella progettazione orientata agli oggetti un "algoritmo" non sarà rappresentato come una singola lista di operazioni procedurali da eseguire.

Sarà invece un insieme di interazioni tra oggetti. Come ha detto Adele Goldberg su Smalltalk, uno dei primi linguaggi orientati agli oggetti, "In Smalltalk, tutto accade un po 'altrove"

Non dovrai mai (dovrebbe) avere un elenco di istruzioni procedurali algoritmiche per eseguire un algoritmo. Invece di un certo numero di oggetti interagenti, ognuno con una piccola unità di comportamenti, il lavorare insieme produrrà il risultato dell'algoritmo. Ogni oggetto è un'unità di comportamento autonomo e il sistema raggiunge i suoi obiettivi da ciascuna di queste unità che dice ad altre unità di fare qualcosa.

Il problema, ovviamente, è che richiede un modo molto diverso di pensare a problemi e algoritmi, ed è per questo che spesso le affermazioni sulla progettazione orientata agli oggetti ricadono sulla progettazione procedurale.

    
risposta data 20.08.2015 - 14:36
fonte
0

Se removeHeaderFooterText è un'operazione atomica, la soluzione è semplice. La tua classe per implementare l'algoritmo avrà un punto di accesso pubblico. In pseudo codice, sarebbe simile a questo:

class HeaderFooterFilter
{
    public removeHeaderFooterText(pages);
};

Nessun chiamante dovrebbe mai preoccuparsi di come removeHeaderFooterText funziona. Tutto ciò che devono sapere è ciò che fa. Internamente, puoi fare tutto ciò che vuoi. Puoi sostituire completamente gli interni un giorno, se vuoi. Finché il tuo punto di ingresso rimane lo stesso, i chiamanti non devono cambiare nulla.

Come dettaglio di implementazione, questo può essere fatto come statico. Tendo ad evitare la staticità in queste situazioni, perché è un po 'più semplice gestire la concorrenza.

    
risposta data 19.08.2015 - 21:43
fonte

Leggi altre domande sui tag