Nome da variável que às vezes se refere a um objeto e, às vezes, a uma string

5

Desculpe pelo título confuso - isso é melhor ilustrado por um exemplo (hipotético, mas esperançosamente ilustrativo).

Para 99% da minha aplicação, um CEP é considerado como uma string, então eu uso consistentemente o nome zipCode para parâmetros, variáveis, acessadores de propriedades, etc. (obviamente com capitalização inicial, conforme apropriado)

No entanto, para o 1% do meu aplicativo que precisa entender os componentes internos de um código postal (por exemplo, para mapeamento), preciso de uma classe ZipCode. A convenção de nomenclatura natural é usar o nome "zipCode" para uma variável do tipo ZipCode.

Isso cria uma inconsistência que é um cheiro de código para mim - às vezes, uma variável chamada zipCode será uma string e, às vezes, será um objeto ZipCode.

Eu poderia passar por toda a minha base de código e usar apenas objetos ZipCode em todos os lugares, mas isso parece excessivo, pois na maioria das vezes eu preciso apenas da representação da string, e algumas vezes eu uso uma string (por exemplo, um parâmetro de URL )

Como alternativa, eu poderia chamar minha classe de algo como ZipCodeObject, ou eu sempre poderia usar zipCodeString ao me referir a ela como uma string, mas ambas parecem ser uma convenção de nomenclatura ruim também.

Minha solução pragmática atual é usar o nome zipCode para ambos. Somente se um método ou classe precisar ter reconhecimento de ambas as representações, use o nome da variável zipCodeObject ou zipCodeString para diferenciá-lo.

Alguém mais tem uma solução melhor para esse enigma?

    
por Andy 18.11.2016 / 16:50
fonte

4 respostas

3

Solução ideal :

Defina uma classe de código postal para representar os códigos postais e reescreva todas as suas APIs em termos de:

class ZipCode { string value; ... }

Vantagens:

  • as APIs internas da sua base de código são mais semanticamente expressivas;
  • você pode restringir certas operações por tipo de uma maneira que requer tempo de computação extra (e repetido) antes. Considere um cenário em que você imprime o código postal nos rótulos dos pacotes:

    Uma api como void printLabelField(ZipCode zipCode) poderá contar com invariantes da classe ZipCode que já estão garantidos na construção (como o comprimento máximo do valor, ou combinando o conjunto de caracteres do dispositivo de saída com o compatível com código postal conjunto de caracteres).

    Por outro lado, com uma API como void prinLabelField(string zipCode) , você precisará executar essas verificações manualmente; Se você esquecer, você tem um bug ou um conjunto inteiro de bugs, que pode ser repetido por desatenção ou falta de experiência com a base de código, durante os ciclos de desenvolvimento do projeto.

  • você tem um ponto de personalização natural para desenvolvimento adicional que emerge do polimorfismo de nível de parâmetro;

  • você tem um ponto natural para depuração / registro / o que quer que seja.

  • você pode testar a funcionalidade das APIs isoladamente.

Solução realista (no caso de ter prazos) :):

Otimize para o caso comum: use string zipCode argumentos em toda a base de código e renomeie sua classe ZipCode para outra coisa, tornando o nome explícito em propósito (não nomeie sua classe ZipCodeObject: adicionando Objeto no final de um nome) Torne o propósito do código mais claro, assim como prefixar os nomes dos métodos com "do" ou "run" não torna os nomes dos métodos mais expressivos).

Considere: DecomposedZipCode , ValidZipCode , ZipCodeComponents .

Em caso de dúvida, escreva algum código de cliente antes de escolher o nome e veja como é fácil ler em voz alta.

    
por 18.11.2016 / 18:58
fonte
3
Considere a composição para ter uma única classe ZipCode em todos os lugares que consista na propriedade de cadeia simples que você deseja usar para o caso majoritário e, em seguida, componha na classe ZipCodeMap mais complexa por meio de um construtor apenas quando precisar. Por exemplo, a representação simples da classe ZipCode seria (com construtores para cada 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);
    }
}

A classe ZipCodeMap conteria os métodos para obter do mapa, mas seria nula e não usada na maior parte do tempo, portanto, não teria muita sobrecarga:

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";
    }
}

Para tornar a classe mais utilizável para o seu caso de simplesmente querer retornar um código postal, você poderia fornecer um método ToString () para a classe ZipCode que usaria o caso ZipCodeMap complexo de modo apenas quando for inicializado:

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

Depois, você pode manter o código do cliente e permitir que ele use a nomenclatura "zipCode" nos dois casos, por exemplo,

var zipCode = new ZipCode("77232");

Ou para usar com o caso de mapeamento:

var zipCode = new ZipCode(20.323, 23.233);

E em ambos os casos, você pode usá-lo como uma string:

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

Ou acesse outras propriedades conforme necessário.

var urlQueryString = baseUri + "?latitude=" + zipcode.ZipCodeMap.Latitude
    
por 18.12.2016 / 23:03
fonte
1

Primeiramente, considere ter CEPs como strings versus ter um tipo ZipCode que contém a string. Há uma escola de pensamento em crescimento que favorece o último, pois oferece maior segurança do tipo, por exemplo (na sintaxe do C # 7 para brevidade, mas a linguagem não é 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

No entanto, há desvantagens definitivas em usar um tipo "pesado" que contenha / execute detalhes de mapeamento, etc., quando tudo que você precisa é o valor do CEP. Por exemplo, para obter uma localização, esse tipo pode precisar executar uma pesquisa de banco de dados e, portanto, os detalhes do banco de dados precisam ser injetados em todas as instâncias ou devem ser acoplados a um localizador de serviço de banco de dados, tornando o teste mais difícil. >

Portanto, para casos em que os detalhes completos são necessários, crie também uma classe ZipCodeDetails , que contenha esses dados extras. "código postal" fará com que a maioria das pessoas imagine apenas um código postal, por isso não chame sua classe de detalhes. Espero que você pense em um nome melhor do que apenas ZipCodeDetails , já que "detalhes" não transmitem muita informação, mas, esperamos, lhe dará a idéia.

    
por 18.11.2016 / 18:13
fonte
-2

Como a classe ZipCode é 1%, basta renomeá-la para PostalCode e atualizar seu aplicativo. Todas as strings que são passadas ainda são códigos postais (99%).

Esta deve ser a menor alteração e permitir diferenciar os dois itens.

    
por 18.11.2016 / 17:26
fonte