Perché aggiungiamo metodi di istanza alle classi? [chiuso]

-2

Riscrivi della domanda : esiste una ragione tecnica per cui non stiamo utilizzando metodi statici invece dei metodi di istanza.

I motivi tecnici sono ad esempio: prestazioni o sicurezza del tipo aggiunto.

Ritengo che con metodi statici e solo classi di dati puri si ottenga una sicurezza di tipo migliore e un codice più gestibile.

Perché aggiungiamo metodi alle classi?

Quando guardo cosa fa il compilatore, possiamo dedurre che un metodo di istanza su una classe viene trasformato in:

public static class Foo {
    public static int Something(object this) { ... }
}

Questo ci mostra che anche "sotto il cofano" passiamo semplicemente i dati come parametro ai nostri metodi o, più precisamente, alle funzioni.

Qual è il ragionamento alla base dei metodi di istanza?

La mia ricerca ha rilevato tre motivi principali per l'aggiunta di metodi di istanza:

  • Convenzione, ci è stato insegnato a farlo in questo modo
  • Ignoranza, non sappiamo in un altro modo
  • Convenienza, funzionalità più semplice da trovare, fondamentalmente un modo per raggruppare le funzionalità.

Trovo tutte e tre le ragioni traballanti al meglio. Penso che tutti possiamo essere d'accordo sul fatto che i primi due motivi non sono motivi validi, questo ci lascia con la terza ragione, Convenience , ma quando si guarda il codice chiamante di un metodo di istanza si vede qualcosa di strano accadere:

var o = new MyClass(..some parameters...);
o.Something(...some parameters...);

Abbiamo diviso la nostra firma di funzione su due righe. In pratica, abbiamo perso la possibilità di verificare realmente le modifiche apportate al nostro sistema. E il confronto con qualcosa come currying non è un paragone valido. Usare le classi per esprimere dati e utilizzare le funzioni (classi statiche con metodi statici) non sarebbe un modo migliore e ancora più conveniente per raggruppare i dati?

Potremmo riscrivere il precedente codice chiamante in:

StaticLib.Something(...some parameters...);

E sii appena fatto. Usare il codice sarebbe più facile da testare e implementare e anche il raggruppamento del codice è facile. Usa semplicemente i criteri di segmentazione di base sulla tua collezione di funzioni. Puoi persino automatizzare questo raggruppamento quando prendi in considerazione tipi e firme ...

EDIT: Come affermato da @Daniel T. Mi sembra che stia venendo giù per convenzione. Questo non è quello che voglio trasmettere. La Convention è eccezionale quando si lavora in un grande progetto o si lavora con colleghi. La convenzione è anche un ottimo modo per cambiare bottiglia, per favore leggi questa prossima modifica non come un attacco alla convenzione nei progetti ma come un attacco alla convenzione come argomento per il ragionamento.

EDIT: Non sto parlando di OOP vs FP, dovremmo essere tutti d'accordo sul fatto che le soluzioni poliglotta siano le migliori. Non sono interessato a risposte come:

  • La maggior parte dei programmatori si aspetta che il loro codice assomiglia ....
  • È una convenzione
  • Mi è stato insegnato a farlo in quel modo ...

Quelle sono risposte basate su convenzioni e dogmi e non daranno mai modi migliori di pensare o codificare.

Sono interessato alle prove e al solido ragionamento. La risposta accettata nel cosiddetto duplicato riduce la domanda a due cose:

  • Non sente OO abbastanza
  • Non mi piace

Questa non è una prova e non è una risposta alla domanda. Inoltre non faccio la stessa domanda quindi è dubbio che sia un duplicato.

EDIT: non è un duplicato . Non sono interessato all'eredità o ad altri aspetti OOP. Quasi la qualità e la leggibilità del codice.

    
posta Baudin999 11.02.2016 - 09:35
fonte

4 risposte

1

Una grande ragione è il polimorfismo (non l'ereditarietà, che è solo una forma di polimorfismo).

Considera il seguente esempio:

interface Payable {
  ...
  double calculatePay();
  ...
}

public class HourlyEmployee implements Payable {
  ...
  public double calculatePay() {
    return hourlyRate * timeCards.totalHoursWorked();
  }
  ...
}

public class SalariedEmployee implements Payable {
  ...
  public double calculatePay() {
    return salary;
  }
  ...
}

public class PayRun {
  ...
  private PaySlip buildPaySlip(Payable payable) {
    PaySlip paySlip = new PaySlip();
    ...
    paySlip.setGrossPay(payable.calculatePay());
    ...
    return paySlip;
  }
  ...
}

Se i metodi di istanza erano solo funzioni che ricevevano l'oggetto come parametro nascosto, il codice sopra non funzionava. Il chiamante del metodo calculatePay dovrebbe conoscere quali tipi di oggetti Payable esistono per chiamare la funzione corretta. Con OO, questa conoscenza non è necessaria, quindi l'oggetto PayRun è in grado di generare PaySlips per qualsiasi tipo di oggetto Payable . Possiamo aggiungere CommissionedEmployee o qualsiasi altro tipo senza modificare il core del processo di gestione stipendi.

Naturalmente, il polimorfismo non è nemmeno vicino all'essere esclusivi di OO. Tuttavia, è un concetto inserito nel nucleo del paradigma (e dei linguaggi correlati), che lo rende una caratteristica di prima classe invece di solo qualcosa che puoi ottenere (di solito facendo le cose in modo scortese e non univoco , come i tavoli virtuali in C). Tuttavia, critico i linguaggi tipizzati staticamente come Java e C ++ come troppo restrittivi sul polimorfismo. Le lingue che supportano a buon mercato digitazione anatra sono argomenti di studio migliori per comprendere il ragionamento alla base di OO.

    
risposta data 11.02.2016 - 13:21
fonte
3

Non sottovalutare il potere della convenzione. Se tutti fanno le cose allo stesso modo, rende i grandi programmi più mantenibili.

Il tuo esempio %codice% sembra solo più semplice perché hai sorvolato i dettagli. Non hai effettivamente indicato dove vengono inizializzati i dati di StaticLib.Something(...some parameters...); , né li hai passati alla funzione MyStruct .

Suppongo che tu stia facendo una programmazione strutturata, piuttosto che un codice procedurale con migliaia di variabili dappertutto. Se vogliamo programmare strutturati, dobbiamo archiviare i dati del programma in una raccolta di strutture / classi. Tutte le linee guida di programmazione popolari insistono sul fatto che i dati devono essere inizializzati prima di essere utilizzati. Supponiamo quindi di avere una struttura MyClass per contenere dati su qualcosa. Abbiamo bisogno di creare la struttura e inizializzarla a uno stato conosciuto.

MyClass o = StaticLib.CreateMyClass(... initial state ...);

o forse

MyClass o; StaticLib.InitMyClass(o, ... initial state...);

Ora vogliamo fare qualcosa con Something .

StaticLib.Something(o, ...some parameters...);

Metti insieme e ottieni

MyClass o = StaticLib.CreateMyClass(... initial state ...);
StaticLib.Something(o, ...some parameters...);

A quel punto, si potrebbe anche fare il "modo orientato agli oggetti", e basta scrivere

MyClass o = new MyClass(... initial state ...);
o.Something(... some parameters...);

La programmazione orientata agli oggetti offre naturalmente una migliore struttura del programma. La definizione della struttura dei dati è sempre strettamente associata alle funzioni che operano su di esso. È più semplice da gestire rispetto a una definizione di una struttura dati in un posto e una libreria di funzioni che operano su di esso da qualche altra parte.

E infine, sembra più bello scrivere

lion.Roar(LOUDLY);

di

Animals.Roar(lion, LOUDLY);
    
risposta data 11.02.2016 - 13:08
fonte
-2

Penso che potrebbe essere correlato a Java, dove tutto deve essere in una classe (anche quando, come fai notare, non è mai in un'istanza di una classe).

C ++ consente le normali funzioni, ma mantiene anche la parola chiave statica utilizzata per limitare l'ambito di una variabile o funzione al file contenente. Il C ++ ha mantenuto questo significato ma ha modificato l'ambito della classe contenente, anche questo è più una cosa dello spazio dei nomi, poiché è ancora necessario allocare esternamente lo spazio di archiviazione per esso.

Questa è una buona ragione per cui è stata fatta - per limitare l'ambito di definizione, quindi qualcuno che legge il tuo codice sa che una variabile o funzione statica è riferita solo all'interno (o in relazione a) di una particolare classe, anche se non è legata a istanze individuali.

    
risposta data 11.02.2016 - 09:44
fonte
-2

Una volta rimosso il dispatch dinamico dal mix, la risposta è che non esiste alcuna ragione, oltre alla convenzione, che lo facciamo in questo modo.

In inglese, leggiamo / scriviamo da sinistra a destra. Perché? Nessun motivo diverso dalla convenzione. Ho l'impressione che tu stia procedendo troppo in fretta a scartare le convenzioni. Il codice convenzionale è più facile da scrivere, più facile da leggere e più facile da mantenere ...

a = b.c(d, e).f(g).h()

vs

a = h(f(c(b, d, e), g))

Il primo legge più naturalmente da sinistra a destra rispetto al secondo. Dal momento che noi madrelingua inglesi siamo abituati a leggere da sinistra a destra fin da un'età molto giovane, una tale costruzione rende il primo più facile da leggere e scrivere.

Nel primo caso non ci sono parentesi nidificate. Per questo motivo, è meno probabile che ne mettiamo troppi o troppo pochi, e possiamo vedere più facilmente che i paren sono equilibrati. Questo rende il primo più facile da scrivere e leggere.

Quando il codice è più facile da leggere, è più facile da mantenere e capire. Quando è più facile scrivere, è più probabile che venga scritto correttamente la prima volta.

Fai ai tuoi amici, incluso il tuo futuro, un favore e rendi il tuo codice più facile da leggere e scrivere.

    
risposta data 11.02.2016 - 13:30
fonte

Leggi altre domande sui tag