Quando divido grandi metodi (o procedure, o funzioni - questa domanda non è specifica per OOP, ma poiché lavoro nei linguaggi OOP il 99% delle volte, è la terminologia che sono più comodo con) in un sacco di piccoli, mi trovo spesso scontento dei risultati. Diventa più difficile ragionare su questi piccoli metodi rispetto a quando erano solo blocchi di codice nel grande, perché quando li estro, perdo un sacco di presupposti che derivano dal contesto del chiamante.
Più tardi, quando guardo questo codice e vedo i singoli metodi, non so da dove vengono chiamati, e li considero come normali metodi privati che possono essere chiamati da qualsiasi parte del file. Ad esempio, immagina un metodo di inizializzazione (costruttore o altro) diviso in una serie di piccoli: nel contesto del metodo stesso, sai chiaramente che lo stato dell'oggetto non è ancora valido, ma in un normale metodo privato probabilmente vai dall'assunto che l'oggetto è già inizializzato ed è in uno stato valido.
L'unica soluzione che ho visto è la clausola where
in Haskell, che consente di definire piccole funzioni che vengono utilizzate solo nella funzione "genitore". Fondamentalmente, sembra così:
len x y = sqrt $ (sq x) + (sq y)
where sq a = a * a
Ma le altre lingue che uso non hanno nulla di simile - la cosa più vicina è definire un lambda in un ambito locale, che è probabilmente ancora più confuso.
Quindi, la mia domanda è - lo incontri, e vedi che questo è un problema? Se lo fai, come lo risolvi tipicamente, in particolare nei linguaggi OOP "mainstream", come Java / C # / C ++?
Modifica dei duplicati: come altri hanno notato, ci sono già delle domande che discutono sui metodi di divisione e sulle piccole domande che sono one-liner. Li ho letti e non discutono il problema delle ipotesi sottostanti che possono essere derivate dal contesto del chiamante (nell'esempio sopra, oggetto inizializzato). Questo è il punto della mia domanda, ed è per questo che la mia domanda è diversa.
Aggiornamento: se hai seguito questa domanda e la discussione in basso, potresti leggere questo articolo di John Carmack in materia , in particolare:
Besides awareness of the actual code being executed, inlining functions also has the benefit of not making it possible to call the function from other places. That sounds ridiculous, but there is a point to it. As a codebase grows over years of use, there will be lots of opportunities to take a shortcut and just call a function that does only the work you think needs to be done. There might be a FullUpdate() function that calls PartialUpdateA(), and PartialUpdateB(), but in some particular case you may realize (or think) that you only need to do PartialUpdateB(), and you are being efficient by avoiding the other work. Lots and lots of bugs stem from this. Most bugs are a result of the execution state not being exactly what you think it is.