Qual è un buon modo per condividere gli helper interni?

3

Tutti i miei progetti condividono la stessa libreria di base che ho accumulato da un po 'di tempo. Contiene utility e classi di helper statiche per aiutarli dove .NET non offre esattamente ciò che voglio. Originariamente tutti gli aiutanti erano scritti principalmente per servire a uno scopo interno e deve rimanere tale, ma a volte si rivelano molto utili per altre assemblee. Ora renderli pubblici in modo affidabile è più complicato di quanto la maggior parte potrebbe pensare, ad esempio tutti i metodi che assumono i tipi nullable ora devono contenere il controllo degli argomenti senza caricare le utilità interne al prezzo di farlo. Il prezzo potrebbe essere trascurabile, ma è tutt'altro che corretto.

Durante il refactoring, ho rivisto questo caso più volte e ho trovato le seguenti soluzioni finora:

  1. Avere una classe interna e pubblica per ogni helper La classe interna contiene il codice effettivo mentre la classe pubblica funge da punto di accesso che controlla gli argomenti.
    Contro:
    • La classe interna richiede un prefisso per evitare l'ambiguità (la migliore presentazione dovrebbe essere riservata ai tipi pubblici)
    • Non è possibile discriminare metodi che non richiedono il controllo degli argomenti
  2. Avere una classe che contiene membri sia interni che pubblici (come implementato convenzionalmente in .NET framework).
    All'inizio, questa potrebbe sembrare la migliore soluzione possibile, ma ha la stessa prima sgradevole truffa della soluzione 1.
    Contro:
    • I metodi interni richiedono un prefisso per evitare l'ambiguità
  3. Ha una classe interna implementata dalla classe pubblica che sostituisce tutti i membri che richiedono il controllo degli argomenti.
    Contro:
    • È non statico, è necessaria almeno un'istanza. Questo non si adatta perfettamente all'idea della classe helper, dato che generalmente consiste di frammenti di codice indipendenti, non dovrebbe richiedere l'istanziazione.
    • Anche i metodi non statici sono più lenti di un livello trascurabile, il che non giustifica realmente questa opzione.

C'è una conseguenza generale e inevitabile, un sacco di manutenzione è necessaria perché ogni membro interno richiederà una controparte pubblica.

Una nota sulla soluzione 1: La prima conseguenza può essere evitata inserendo entrambe le classi in spazi dei nomi diversi, ad esempio puoi avere il vero helper nel namespace root e l'helper pubblico in uno spazio dei nomi chiamato "Helpers".

    
posta toplel32 23.05.2014 - 22:55
fonte

3 risposte

1

No, questo non risponde alla tua domanda direttamente, ma soprattutto perché credo che il tuo problema sia radicato altrove.

Personalmente tendo a stare lontano da "classi di aiuto" e biblioteche. Tale codice tende ad essere una discarica per il codice che non ha una casa e porta a una progettazione scadente. Da quello che ho raccolto suggerisco di avere un'assemblea allegata a ciascuno dei tuoi progetti "reali" in modo che possa condividere un codice comune. E quello che vedo accadere è un mucchio di codice che viene caricato nel dominio dell'app e forse il 10% di esso effettivamente utilizzato da un determinato segmento del tuo dominio. Il che potrebbe costare più di qualche controllo sui membri pubblici.

Suggerirei di analizzare quali parti del tuo dominio avranno effettivamente bisogno di codice specifico condiviso e progettare il codice riusabile attorno a questo. Personalmente preferisco l'utilizzo di metodi di estensione; Puoi associare un codice riutilizzabile specifico a un tipo specifico (evita di estendere string , int o simili) dandogli una bella casa constrongvole e un codice facile da capire.

Quando tutto il resto fallisce, ricorda: "Le prestazioni non sono un problema finché non è un problema. Il tuo tempo è più prezioso di alcuni cicli della CPU"

    
risposta data 27.05.2014 - 05:49
fonte
3

È possibile seguire la convenzione utilizzata all'interno di .NET Framework stesso.

Se si guarda il codice sorgente di .NET Framework, i metodi pubblici contengono i controlli richiesti, quindi delegano il lavoro stesso ai metodi privati. Questi metodi privati sono identificati in modo identico ai metodi pubblici, tranne che hanno un suffisso specifico ("Interno"). Entrambi i metodi sono all'interno della stessa classe.

Ad esempio, System.String ha un metodo Split (riga 948) , che delega il lavoro a SplitInternal (riga 983).

Il fatto che gli autori utilizzino i suffissi anziché i prefissi è fondamentale e lo rende una soluzione molto interessante: puoi trovare facilmente il metodo nell'elenco di metodi all'interno di una classe in Visual Studio e non è necessario digitare "InternalS" per ottenere il metodo Split che fa il lavoro con Intellisense.

    
risposta data 23.05.2014 - 23:07
fonte
1

Now making them public in a reliable way is more complicated than most would think, for example all methods that assume nullable types must now contain argument checking while not charging internal utilities with the price of doing so. The price might be negligible, but it is far from right.

Perché? Perché il codice interno è in qualche modo immune dagli errori?

No, la soluzione corretta è mangiare il tuo cibo per cani. Se la classe / funzione dovrebbe eseguire il controllo dei limiti, allora dovrebbe fare il controllo dei limiti per tutti. Se l'utilizzo è abbastanza chiaro che "spazzatura in-garbage out" è un approccio accettabile, allora dovrebbe farlo per tutti.

Certo - occasionalmente ti imbatti in scenari in cui una variante che evita il controllo dei limiti è accettabile e appropriata, ma nella mia esperienza, questi casi tendono ad avere bisogno di un accesso privato, non interno - e non sono la norma. Se le cose ti sono utili, saranno quasi certamente di aiuto per tutti. Disegnali in questo modo.

    
risposta data 23.05.2014 - 23:14
fonte

Leggi altre domande sui tag