Quanto è grande per una classe?

28

Sono uno sviluppatore da molto tempo (ho 49 anni) ma piuttosto nuovo allo sviluppo orientato agli oggetti. Ho letto di OO fin da Bertrand Meyer's Eiffel, ma ho fatto davvero poca programmazione OO.

Il punto è che ogni libro sul design OO inizia con un esempio di barca, auto o qualsiasi altro oggetto comune che usiamo molto spesso, e iniziano ad aggiungere attributi e metodi, spiegando come modellano lo stato dell'oggetto e cosa può fare da fare con esso.

Quindi di solito fanno qualcosa del tipo "migliore è il modello, meglio rappresenta l'oggetto nell'applicazione e migliore è il risultato".

Fin qui tutto bene, ma, d'altra parte, ho trovato diversi autori che danno ricette come "una classe dovrebbe rientrare in una singola pagina" (aggiungerei "su quale dimensione del monitor?" ora che proviamo a non stampare il codice!).

Prendi ad esempio una classe PurchaseOrder , che ha una macchina a stati finiti che ne controlla il comportamento e una raccolta di PurchaseOrderItem , uno degli argomenti qui al lavoro è che dovremmo usare una classe semplice PurchaseOrder , con alcuni metodi (poco più di una classe dati) e hanno una "classe esperta" PurchaseOrderFSM che gestisce la macchina a stati finiti per PurchaseOrder .

Direi che rientra nella classificazione "Feature Envy" o "Inappropriate Intimacy" di Jeff Atwood's Odori di codice post su Coding Horror. Lo definirei semplicemente buonsenso. Se riesco ad emettere, approvare o annullare il mio ordine di acquisto reale, la classe PurchaseOrder deve avere i metodi issuePO , approvePO e cancelPO .

Non è questo che va con "massimizzare la coesione" e "minimizzare l'accoppiamento", principi antichi che capisco come pietre angolari di OO?

Inoltre, questo non aiuta a mantenere la classe?

    
posta Miguel Veloso 14.10.2010 - 00:44
fonte

12 risposte

26

Una classe dovrebbe usare il Principio di Responsabilità Unica. Le classi più grandi che ho visto fare a molte cose è il motivo per cui sono troppo grandi. Guarda ogni metodo e il codice decidere dovrebbe essere in questa classe o separato, il codice duplicato è un suggerimento. Potresti avere un metodo issuePO ma contiene ad esempio 10 righe di codice di accesso ai dati? Probabilmente questo codice non dovrebbe essere lì.

    
risposta data 14.10.2010 - 02:46
fonte
11

A mio parere, le dimensioni della classe non contano finché le variabili, le funzioni e i metodi sono rilevanti per la classe in questione.

    
risposta data 14.10.2010 - 00:56
fonte
7

Il problema più grande con le classi grandi è quando si tratta di testare quelle classi, o la loro interazione con altre classi. Le grandi dimensioni della classe di per sé non sono un problema, ma come tutti gli odori, indica un problema potenziale . In generale, quando la classe è grande, è un'indicazione che la classe sta facendo più di una singola classe.

Per l'analogia della tua auto, se la classe car è troppo grande, probabilmente è un'indicazione che deve essere suddivisa in classe engine , classe door s e classe windshield s, ecc.

    
risposta data 14.10.2010 - 01:01
fonte
7

Non penso che esista una definizione specifica di una classe troppo grande. Tuttavia, vorrei usare le seguenti linee guida per determinare se dovrei rifattorizzare la mia classe o ridefinire l'ambito della classe:

  1. Ci sono molte variabili indipendenti l'una dall'altra? (La mia tolleranza personale è di circa 10 ~ 20 nella maggior parte dei casi)
  2. Esistono molti metodi progettati per casi angolari? (La mia personale tolleranza è ... beh, dipende molto dal mio umore, immagino)

Riguardo a 1, immagina di definire la dimensione del parabrezza, le dimensioni della ruota, il modello della lampadina della coda e altri 100 dettagli nella classe car . Sebbene siano tutti "rilevanti" per l'auto, è molto difficile tracciare il comportamento di tale classe car . E quando un giorno il tuo collega accidentalmente (o, in alcuni casi, intenzionalmente) cambia la dimensione del parabrezza in base al modello di lampadina posteriore, ti porta per sempre a scoprire perché il parabrezza non si adatta più alla tua auto.

Riguardo a 2, immagina di provare a definire tutti i comportamenti che un'auto può avere nella classe car , ma car::open_roof() sarà disponibile solo per i convertibili e car::open_rear_door() non si applica a quelli 2 macchine della porta. Anche se le cabriolet e le auto a 2 porte sono "auto" per definizione, implementarle nella classe car complica la classe. La classe diventa più difficile da usare e più difficile da mantenere.

Oltre a queste 2 linee guida, suggerirei una definizione chiara dell'ambito di classe. Una volta definito l'ambito, implementare la classe rigorosamente in base alla definizione. La maggior parte delle volte le persone ricevono una classe "troppo grande" a causa della scarsa definizione dell'ambito o delle funzioni arbitrarie aggiunte alla classe

    
risposta data 14.10.2010 - 02:07
fonte
6

Torniamo indietro:

Doesn’t that goes with the “maximize cohesion” and “minimize coupling” age old principles that I understand as cornerstones of OO?

In realtà, è una pietra miliare della programmazione, di cui OO è solo un paradigma.

Lascio che Antoine de Saint-Exupéry risponda alla tua domanda (perché sono pigro;)):

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

Più semplice è la classe, più sarai sicuro di ciò che è giusto, più sarà facile provarlo completamente .

    
risposta data 14.10.2010 - 20:09
fonte
6

Di 'ciò di cui hai bisogno in 300 righe

Nota: questa regola empirica presuppone che tu stia seguendo l'approccio "una classe per file" che è di per sé una buona idea di manutenibilità

Non è una regola assoluta, ma se vedo oltre 200 righe di codice in una classe sono sospettoso. 300 linee e campane di avvertimento si accendono. Quando apro il codice di qualcun altro e trovo 2000 righe, so che è tempo di refactoring senza nemmeno leggere la prima pagina.

Per lo più questo dipende dalla manutenibilità. Se l'oggetto è così complesso che non puoi esprimere il suo comportamento in 300 righe, sarà molto difficile da capire per qualcun altro.

Sto scoprendo che sto piegando questa regola nel Modello - > ViewModel - > Visualizza il mondo in cui sto creando un 'oggetto di comportamento' per una pagina dell'interfaccia utente perché spesso occorrono più di 300 righe per descrivere la serie completa di comportamenti su una pagina. Comunque sono ancora nuovo in questo modello di programmazione e trovo buoni modi per rifattare / riutilizzare i comportamenti tra pagine / viewmodels che sta gradualmente riducendo le dimensioni dei miei ViewModels.

    
risposta data 14.10.2010 - 23:24
fonte
3

Sono con l'OP nella classificazione Feature Envy. Niente mi irrita di più che avere un sacco di lezioni con un sacco di metodi che funzionano su alcuni interni di altre classi. OO suggerisce di modellare i dati e le operazioni su quei dati. Ciò non significa che tu metta le operazioni in un posto diverso dai dati! Sta cercando di farti mettere i dati e quei metodi insieme .

Con i modelli car e person così diffusi, sarebbe come mettere il codice per creare la connessione elettrica, o addirittura far ruotare lo starter, nella classe person . Spero che questo sia ovviamente sbagliato per qualsiasi programmatore esperto.

Non ho una dimensione ideale della classe o del metodo, ma preferisco mantenere i miei metodi e le mie funzioni su uno schermo in modo da poter vedere la loro interezza in un unico posto. (Ho Vim configurato per aprire una finestra a 60 linee, che si trova esattamente intorno al numero di linee su una pagina stampata.) Ci sono posti dove questo non ha molto senso, ma funziona per me. Mi piace seguire le stesse linee guida per le definizioni di classe e cercare di mantenere i miei file sotto poche "pagine". Qualcosa di più di 300, 400 linee inizia a sentirsi poco maneggevole (soprattutto perché la classe ha iniziato ad assumersi troppe responsabilità dirette).

    
risposta data 14.10.2010 - 02:40
fonte
1

Quando una definizione di classe ha un paio di centinaia di metodi, è troppo grande.

    
risposta data 14.10.2010 - 11:16
fonte
1

Sono pienamente d'accordo con l'uso del principio di responsabilità singola per le classi, ma se questo è seguito e risulta in classi grandi allora così sia. Il vero obiettivo dovrebbe essere che i singoli metodi e proprietà non siano troppo grandi, la complessità ciclomatica dovrebbe essere la guida lì e in seguito potrebbe portare a molti metodi privati che aumenteranno le dimensioni complessive della classe, ma lasceranno il tutto leggibile e verificabile a livello granulare. Se il codice rimane leggibile, la dimensione è irrilevante.

    
risposta data 14.10.2010 - 13:53
fonte
1

L'emissione di un ordine di acquisto è spesso un processo complicato che coinvolge più di un semplice ordine di acquisto. In questo caso, mantenere la logica aziendale in una classe separata e semplificare l'ordine di acquisto è probabilmente la cosa giusta da fare. In generale, però, hai ragione - non vuoi le classi della "struttura dei dati". Ad esempio, il metodo " issue() " della classe business "POManager" dovrebbe probabilmente chiamare il metodo issue() dell'ordine di acquisto. L'OP può quindi impostare il suo stato su "Rilasciato" e prendere nota della data di emissione, mentre il POManager può quindi inviare una notifica ai conti fornitori per far sapere loro di aspettarsi una fattura, e un'altra notifica all'inventario in modo che sappiano che c'è qualcosa che arriva e la data prevista.

Chiunque spinga "regole" per le dimensioni del metodo / classe, ovviamente, non ha trascorso abbastanza tempo nel mondo reale. Come altri hanno accennato, è spesso un indicatore di refactoring, ma a volte (specialmente nel lavoro di tipo "elaborazione dati") è davvero necessario scrivere una classe con un singolo metodo che si estende su oltre 500 linee. Non rimanere impigliato.

    
risposta data 14.10.2010 - 15:04
fonte
0

I’ve found several authors that give recipes such as “a class should fit in just a single page”

In termini di dimensioni fisiche di una classe, vedere una grande classe è un'indicazione di un odore di codice, ma non significa necessariamente che ci siano sempre odori. (Di solito lo sono comunque) Come direbbero i pirati dei Pirati dei Caraibi, questo è più ciò che chiamereste "linee guida" rispetto alle regole attuali. Ho provato a seguire rigorosamente "non più di cento LOC in una classe" una volta e il risultato è stato un sacco di inutili sovra-ingegnerizzazione.

So they usually go something like "the better the model the better it represents the object in the application and the better it all comes out".

Di solito inizio i miei progetti con questo. Cosa fa questa classe nel mondo reale? Come dovrebbe la mia classe riflettere questo oggetto come dichiarato nei suoi requisiti? Aggiungiamo un po 'di stato qui, un campo lì, un metodo per lavorare su quei campi, aggiungerne altri e voilà! Abbiamo una classe lavoratrice. Ora inserisci le firme con le opportune implementazioni e siamo a posto, o almeno così pensi. Ora abbiamo una bella classe con tutte le funzionalità di cui abbiamo bisogno. Ma il problema è che non tiene conto del mondo reale. E con questo intendo che non tiene conto di "CAMBIAMENTO".

Il motivo per cui le classi sono preferibilmente suddivise in parti più piccole è che è difficile cambiare le grandi classi in un secondo momento senza influire sulla logica esistente o introdurre un nuovo bug o due involontariamente o semplicemente rendere tutto difficile da riutilizzare. È qui che il refactoring, i pattern di progettazione, SOLID, ecc entrano in gioco e il risultato finale è solitamente una piccola classe che lavora su altre sottoclassi più piccole e più granulari.

Inoltre, nel tuo esempio, l'aggiunta di IssuePO, ApprovePO e Annulla PO nella classe PurchaseOrder sembra illogica. Gli ordini di acquisto non emettono, approvano o si annullano. Se l'ordine di acquisto deve avere metodi, i metodi dovrebbero lavorare sul suo stato e non sulla classe nel suo insieme. Sono davvero solo classi di dati / record su cui lavorano le altre classi.

    
risposta data 14.10.2010 - 03:39
fonte
0

Grande abbastanza da contenere tutta la logica necessaria per eseguire il suo lavoro.

Abbastanza piccolo per essere gestibile e seguire il principio di responsabilità singola .

    
risposta data 01.06.2015 - 15:08
fonte

Leggi altre domande sui tag