Nome variabile che a volte si riferisce a un oggetto ea volte a una stringa

5

Ci scusiamo per il titolo confuso - questo è meglio illustrato da un esempio (ipotetico ma, si spera, illustrativo).

Per il 99% della mia applicazione un codice postale è considerato una stringa, quindi uso costantemente il nome zipCode per parametri, variabili, proprietà accessorie ecc. (ovviamente con la maiuscola iniziale appropriata)

Tuttavia, per l'1% della mia applicazione che deve comprendere le parti interne di un codice postale (ad esempio per la mappatura), ho bisogno di una classe ZipCode. La convenzione di denominazione naturale consiste nell'utilizzare il nome "zipCode" per una variabile di tipo ZipCode.

Questo crea un'incoerenza che è un odore di codice per me - a volte una variabile chiamata zipCode sarà una stringa ea volte sarà un oggetto ZipCode.

Potrei passare attraverso tutta la mia base di codice e usare solo oggetti ZipCode ovunque, ma ciò sembra eccessivo poiché la maggior parte delle volte ho solo bisogno della rappresentazione della stringa, e ci sono delle volte in cui devo usare una stringa (es. un parametro URL )

In alternativa, potrei chiamare la mia classe qualcosa come ZipCodeObject, o potrei sempre usare zipCodeString quando mi riferisco ad esso come una stringa, ma entrambi sembrano anche una cattiva convenzione di denominazione.

La mia attuale soluzione pragmatica è utilizzare il nome zipCode per entrambi. Solo se un metodo o una classe ha bisogno di avere consapevolezza di entrambe le rappresentazioni, allora uso il nome variabile zipCodeObject o zipCodeString per differenziare.

Qualcun altro ha una soluzione migliore per questo enigma?

    
posta Andy 18.11.2016 - 16:50
fonte

4 risposte

3

Soluzione ideale :

Definisci una classe zipcode per rappresentare i codici di avviamento postale e riscrivi tutte le tue API in termini di esso:

class ZipCode { string value; ... }

I vantaggi:

  • le apis interne del tuo codice base sono più espressive semanticamente;
  • puoi limitare alcune operazioni per tipo in un modo che richiedeva tempo di calcolo extra (e ripetuto) prima. Considera uno scenario in cui stampi il codice postale sulle etichette dei pacchetti:

    Un api piace void printLabelField(ZipCode zipCode) sarà in grado di fare affidamento su invarianti della classe ZipCode che sono già garantiti in fase di costruzione (come la lunghezza massima del valore, o che corrispondono al set di caratteri del dispositivo di output al codice di avviamento postale compatibile set di caratteri).

    D'altra parte, con un'API come void prinLabelField(string zipCode) dovrai eseguire questi controlli manualmente; Se dimentichi, hai un bug o un intero set di bug, che può essere ripetuto per disattenzione o mancanza di esperienza con il codice base, durante i cicli di sviluppo del progetto.

  • hai un punto di personalizzazione naturale per ulteriori sviluppi che emerge dal polimorfismo a livello di parametro;

  • hai un punto naturale per il debug / logging / qualunque.

  • puoi testare la funzionalità delle API in isolamento.

Soluzione realistica (se hai scadenze :)):

Ottimizza per il caso comune: usa string zipCode argomenti nel codice base e rinomina la classe ZipCode in qualcos'altro, rendendo esplicito il nome (non nominare la classe ZipCodeObject: l'aggiunta di Object alla fine di un nome non 'rendere lo scopo del codice più chiaro, proprio come il prefisso dei nomi dei metodi con "do" o "run" non rende i nomi dei metodi più espressivi).

Considera: DecomposedZipCode , ValidZipCode , ZipCodeComponents .

In caso di dubbio, scrivi un codice cliente prima di scegliere il nome e scopri quanto è facile leggere ad alta voce.

    
risposta data 18.11.2016 - 18:58
fonte
3

Considera l'utilizzo della composizione per avere una singola classe ZipCode ovunque che sia costituita dalla semplice proprietà della stringa che desideri utilizzare per il caso di maggioranza e quindi componi nella classe ZipCodeMap più complessa tramite un costruttore solo quando ne hai bisogno. Ad esempio la semplice rappresentazione della classe ZipCode sarebbe (con i costruttori per ogni caso):

public class ZipCode
{
    public string Code { get; }

    public ZipCodeMap ZipCodeMap;

    public ZipCode(string zipCode)
    {
        Code = zipCode;
    }

    public ZipCode(double latitude, double longitude)
    {
        ZipCodeMap = new ZipCodeMap(latitude, longitude);
    }
}

La classe ZipCodeMap conterrebbe i metodi per ottenere dalla mappa ma sarebbe nulle e inutilizzate il più delle volte, quindi non comporterebbe un sovraccarico:

public class ZipCodeMap
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public ZipCodeMap(double latitude, double longitude)
    {
        Latitude = latitude;
        Longitude = longitude;
    }
    public string GetZipCodeFromLatLong()
    {
        // Insert the complex map method here...
        return "77077";
    }
}

Per rendere la classe più utilizzabile per il tuo caso di voler semplicemente restituire un codice zip stringa, potresti fornire un metodo ToString () per la classe ZipCode che userebbe la modalità ZipCodeMap complessa solo quando è inizializzata:

    public override string ToString()
    {
        if (ZipCodeMap != null)
        {
            return ZipCodeMap.GetZipCodeFromLatLong();
        }
        return Code;
    }

Quindi puoi mantenere il codice cliente e permetterlo di usare la denominazione "zipCode" in entrambi i casi, ad es.

var zipCode = new ZipCode("77232");

O per l'utilizzo con il caso di mappatura:

var zipCode = new ZipCode(20.323, 23.233);

E in entrambi i casi potresti usarlo come una stringa:

var urlQueryString = baseUri + "?zipcode=" + zipcode

O accedi ad altre proprietà se necessario.

var urlQueryString = baseUri + "?latitude=" + zipcode.ZipCodeMap.Latitude
    
risposta data 18.12.2016 - 23:03
fonte
1

In primo luogo per quanto riguarda l'avere codici di avviamento postale come stringhe contro l'avere un tipo ZipCode che contiene la stringa. C'è una crescente scuola di pensiero che favorisce quest'ultimo dato che offre maggiore sicurezza del tipo, ad esempio (in sintassi C # 7 per brevità, ma la lingua non è importante):

public class ZipCode
{
    pubic string Value { get; }

    public ZipCode(string value) 
    { 
        Value = ValidZipCode(value) ? value : throw new Exception("That's not a zip code!!");
    }

    ...
}

...

string zipCode1 = "elephant" // compiles, but is nonsense
ZipCode zipCode2 = new ZipCode("elephant"); // exception thrown

Tuttavia, esistono degli svantaggi definiti nell'utilizzo di un tipo "pesante" che contiene / esegue dettagli di mappatura ecc., quando tutto ciò che serve è il valore del codice postale. Ad esempio, per ottenere una posizione, questo tipo potrebbe richiedere una ricerca del database e quindi i dettagli del database devono essere iniettati in ogni istanza, oppure devono essere accoppiati a un servizio di localizzazione del database, rendendo così i test più difficili.

Quindi, per i casi in cui sono necessari tutti i dettagli, crea anche una classe ZipCodeDetails , che contiene quei dati extra. "codice di avviamento postale" indurrà la maggior parte delle persone a visualizzare solo un codice postale, quindi non chiamare la classe dei dettagli. Spero che penserai ad un nome migliore di ZipCodeDetails in quanto i "dettagli" non forniscono molte informazioni, ma spero che ti dia l'idea.

    
risposta data 18.11.2016 - 18:13
fonte
-2

Poiché la classe ZipCode è dell'1%, basta rinominarla in PostalCode e aggiornare la tua applicazione. Tutte le stringhe che vengono passate sono ancora codici postali (99%).

Questo dovrebbe essere il più piccolo cambiamento e consentire a uno di distinguere tra i due elementi.

    
risposta data 18.11.2016 - 17:26
fonte

Leggi altre domande sui tag