Va bene per una classe usare il proprio metodo pubblico?

23

Sfondo

Attualmente ho una situazione in cui ho un oggetto che è sia trasmesso che ricevuto da un dispositivo. Questo messaggio ha diversi costrutti, come segue:

public void ReverseData()
public void ScheduleTransmission()

Il metodo ScheduleTransmission ha bisogno di per chiamare il metodo ReverseData ogni volta che viene chiamato. Tuttavia, ci sono dei momenti in cui dovrò chiamare esternamente ReverseData (e dovrei aggiungere al di fuori dello spazio dei nomi interamente ) da dove viene istanziato l'oggetto nell'applicazione.

Come per "ricevere", intendo che ReverseData verrà chiamato esternamente in un% event-handler di object_received per annullare l'inversione dei dati.

Domanda

È generalmente accettabile per un oggetto chiamare i propri metodi pubblici?

    
posta Snoop 14.03.2016 - 18:15
fonte

6 risposte

33

Direi che non è solo accettabile, ma incoraggiato soprattutto se si prevede di consentire le estensioni. Per supportare estensioni della classe in C #, è necessario contrassegnare il metodo come virtuale per i commenti sottostanti. Potresti comunque documentarlo, in modo che qualcuno non sia sorpreso quando ReverseData () sovrascrive il modo in cui funziona ScheduleTransmission ().

Dipende davvero dal design della classe. ReverseData () suona come un comportamento fondamentale della tua classe. Se hai bisogno di usare questo comportamento in altri posti, probabilmente non vuoi avere altre versioni di esso. È sufficiente fare attenzione a non lasciare che i dettagli specifici di ScheduleTransmission () trapelino in ReverseData (). Ciò creerà problemi. Ma dal momento che lo stai già utilizzando al di fuori della classe, probabilmente lo hai già pensato.

    
risposta data 14.03.2016 - 18:21
fonte
20

Assolutamente.

La visibilità di un metodo ha il solo scopo di consentire o negare l'accesso a un metodo al di fuori della classe o all'interno di una classe figlia; metodi pubblici, protetti e privati possono sempre essere chiamati all'interno della classe stessa.

Non c'è niente di sbagliato nel chiamare metodi pubblici. L'illustrazione nella tua domanda è l'esempio perfetto di una situazione in cui avere due metodi pubblici è esattamente ciò che dovresti fare.

Tuttavia, puoi seguire attentamente i seguenti schemi:

  1. Un metodo Hello() chiama World(string, int, int) come questo:

    Hello()
    {
        this.World("Some magic value here", 0, 100);
    }
    

    Evita questo modello. Invece, utilizza argomenti facoltativi . Gli argomenti facoltativi facilitano la rilevabilità: il chiamante che digita Hello( non necessariamente saprà che esiste un metodo che consente di chiamare il metodo con valori predefiniti. Gli argomenti opzionali sono anche auto-documentanti. World() non mostra al chiamante quali sono i valori predefiniti effettivi.

  2. Un metodo Hello(ComplexEntity) chiama World(string, int, int) come questo:

    Hello(ComplexEntity entity)
    {
        this.World(entity.Name, entity.Start, entity.Finish);
    }
    

    Utilizza invece overload . Stessa ragione: migliore scopribilità. Il chiamante può vedere immediatamente tutti i sovraccarichi tramite IntelliSense e scegliere quello giusto.

  3. Un metodo chiama semplicemente altri metodi pubblici senza aggiungere alcun valore sostanziale.

    Pensaci due volte. Hai veramente bisogno di questo metodo? O dovresti rimuoverlo e lasciare che il chiamante invochi i diversi metodi? Un suggerimento: se il nome del metodo non sembra giusto o è difficile da trovare, dovresti probabilmente rimuoverlo.

  4. Un metodo convalida l'input e quindi chiama l'altro metodo:

    Hello(string name, int start, int end)
    {
        if (name == null) throw new ArgumentNullException(...);
        if (start < 0) throw new OutOfRangeException(...);
        ...
        if (end < start) throw new ArgumentException(...);
    
        this.World(name, start, end);
    }
    

    Invece, World dovrebbe convalidare i suoi parametri o essere privato.

risposta data 14.03.2016 - 18:25
fonte
8

Se qualcosa è pubblico, potrebbe essere chiamato in qualsiasi momento da qualsiasi sistema. Non c'è motivo per cui non puoi essere uno di quei sistemi!

Nelle librerie altamente ottimizzate, vuoi copiare l'idioma messo in java.util.ArrayList#ensureCapacity(int) .

ensureCapacity

  • ensureCapacity è pubblico
  • ensureCapacity ha tutti i limiti necessari per il controllo e i valori predefiniti, ecc.
  • ensureCapacity chiama ensureExplicitCapacity

ensureCapacityInternal

  • ensureCapacityInternal è privato
  • ensureCapacityInternal ha un controllo degli errori minimo perché tutti gli input provengono dalla classe
  • ensureCapacityInternal ALSO chiama ensureExplicitCapacity

ensureExplicitCapacity

  • ensureExplicitCapacity è ANCHE privato
  • ensureExplicitCapacity ha no controllo errori
  • ensureExplicitCapacity esegue il lavoro effettivo
  • ensureExplicitCapacity non viene chiamato da nessuna parte tranne da ensureCapacity e ensureCapacityInternal

In questo modo, il codice che ti fidi ottiene l'accesso privilegiato (e più veloce!) perché sai che i suoi input sono buoni. Il codice di cui non ti fidi passa attraverso un certo rigore per verificarne l'integrità, bombardare o fornire valori predefiniti o gestire in altro modo cattivi input. Entrambe indirizzano verso il luogo che effettivamente funziona.

Tuttavia, questo è usato in ArrayList, una delle classi più usate nel JDK. È molto probabile che il tuo caso non richieda quel livello di complessità e rigore. Per farla breve, se tutte le chiamate ensureCapacityInternal sono state sostituite con chiamate ensureCapacity , le prestazioni sarebbero comunque molto, molto buone. Questa è una microottimizzazione probabilmente fatta solo dopo un'attenta considerazione.

    
risposta data 14.03.2016 - 21:41
fonte
3

Sono già state fornite molte buone risposte, e anch'io sarei d'accordo con loro sul fatto che sì, un oggetto può chiamare i suoi metodi pubblici dagli altri metodi. Tuttavia, c'è un piccolo avvertimento sul design che dovrai fare attenzione.

I metodi pubblici di solito hanno un contratto per "prendere l'oggetto in uno stato coerente, fare qualcosa di sensato, lasciare l'oggetto in uno stato coerente (possibilmente diverso)". Qui, "coerente" può significare, ad esempio, che Length di List<T> non è maggiore del suo Capacity e che il riferimento agli elementi da indici da 0 a Length-1 non verrà generato.

Ma all'interno dei metodi dell'oggetto, l'oggetto potrebbe trovarsi in uno stato incoerente, quindi quando chiami uno dei tuoi metodi pubblici, potrebbe fare una cosa molto sbagliata, perché non è stato scritto con tale possibilità in mente. Quindi se pianifichi di chiamare i tuoi metodi pubblici dagli altri tuoi metodi, assicurati che il loro contratto sia "prendi l'oggetto in qualche stato, fai qualcosa di sensato, lascia l'oggetto in un (possibilmente diverso) (forse incoerente, ma solo se l'iniziale stato era incoerente) stato ".

    
risposta data 14.03.2016 - 22:48
fonte
1

Un altro esempio quando si chiama il metodo pubblico all'interno di un altro metodo pubblico è perfettamente valido è un approccio CanExecute / Execute. Lo uso quando ho bisogno di entrambi validazione e conservazione invariata .

Ma in generale sono sempre cauto a riguardo. Se un metodo a() è chiamato all'interno del metodo b() , significa che il metodo a() è un dettaglio di implementazione di b() . Molto spesso indica che appartengono a diversi livelli di astrazione. E il fatto che siano entrambi pubblici mi fa chiedere se sia un Single -responsibility principle violazione o no.

    
risposta data 11.12.2017 - 09:55
fonte
-5

Scusa ma non sarò d'accordo con la maggior parte delle altre risposte "sì che puoi" e dici che:

Scorrei una classe chiamando un metodo pubblico da un altro

Ci sono un paio di potenziali problemi con questa pratica.

1: ciclo infinito nella classe ereditata

Quindi la tua classe base chiama method1 da method2 ma tu o qualcun altro ne erediti e nasconde method1 con un nuovo metodo che chiama method2.

2: Eventi, Registrazione ecc.

Ad esempio, ho un metodo Add1 che attiva un evento '1 aggiunto!' Probabilmente non voglio il metodo Add10 per sollevare quell'evento, scrivere su un registro o altro, dieci volte.

3: threading e altri deadlock

Ad esempio InsertComplexData apre una connessione db, avvia una transazione, blocca una tabella, quindi chiama InsertSimpleData, apre una connessione, avvia una transazione, attende che la tabella venga sbloccata ....

Sono sicuro che ci sono più motivi, una delle altre risposte toccate su "tu modifica il metodo1 e sei sorpreso che il metodo2 cominci a comportarsi diversamente"

In generale se hai due metodi pubblici che condividono il codice, è meglio che entrambi chiamino un metodo privato anziché uno chiama l'altro.

Modifica ----

Consente di espandere il caso specifico nell'OP.

non abbiamo molti dettagli, ma sappiamo che ReverseData è chiamato da un gestore di eventi di qualche tipo così come il metodo ScheduleTransmission.

I dati inversi cambiano anche lo stato interno dell'oggetto

Dato questo caso, penserei che la sicurezza del thread sarebbe importante e quindi la mia terza obiezione alla pratica si applica.

Per rendere sicuro il thread di ReverseData è possibile aggiungere un blocco. Ma se ScheduleTransmission ha bisogno di essere thread-safe, vorrai condividere lo stesso blocco.

Il modo più semplice per farlo è spostare il codice di ReverseData in un metodo privato e chiamarlo con entrambi i metodi pubblici. È quindi possibile inserire l'istruzione di blocco nei metodi pubblici e condividere un oggetto di blocco.

Ovviamente puoi obiettare che "non succederà mai!" o "Potrei programmare il blocco in un altro modo", ma il punto su una buona pratica di codifica è strutturare bene il tuo codice in primo luogo.

In termini accademici direi che questo viola la L in solido. I metodi pubblici sono più che accessibili pubblicamente. Sono anche modificabili dai suoi eredi. Il tuo codice dovrebbe essere chiuso per la modifica, il che significa che devi pensare a quale lavoro fai nei metodi pubblici e protetti.

Ecco un altro: violentemente stai anche violando il DDD. Se il tuo oggetto è un oggetto di dominio, i suoi metodi pubblici dovrebbero essere termini di dominio che significano qualcosa per il business. In questo caso è molto improbabile che "compri una dozzina di uova" sia uguale a "compra 1 uovo 12 volte" anche se inizia da quella parte.

    
risposta data 15.03.2016 - 02:51
fonte

Leggi altre domande sui tag