5

Sorry for the confusing title - this is best illustrated by an example (hypothetical but hopefully illustrative).

For 99% of my application a Zip Code is considered as a string, so I consistently use the name zipCode for parameters, variables, property accessors etc. (obviously with initial capitalization as appropriate)

However, for the 1% of my application that needs to understand the internals of a zip code (e.g. for mapping), I need a ZipCode class. The natural naming convention is to use the name "zipCode" for a variable of type ZipCode.

This creates an inconsistency which is a code smell for me - sometimes a variable called zipCode will be a string and sometimes it will be a ZipCode object.

I could go through my whole code base and use only ZipCode objects everywhere, but that seems excessive as most of the time I only need the string representation, and there are some times when I must use a string (e.g. a URL parameter)

Alternatively I could call my class something like ZipCodeObject, or I could always use zipCodeString when referring to it as a string, but both of those seem like a bad naming convention too.

My current pragmatic solution is to use the name zipCode for both. Only if a method or class needs to have awareness of both representations, then I use the variable name zipCodeObject or zipCodeString to differentiate.

Does anyone else have a better solution to this conundrum?

  • 6
    "I could [...] use only ZipCode objects everywhere, but that seems excessive [...]" It doesn't seem excessive to me. Your value has a specific semantic and requires specific validation. You can't pass the string "foobar" to a function which expects a zip code. – Vincent Savard Nov 18 '16 at 15:55
  • 2
    Using a Zipcode object where you use Zipcode.AsString where you need strings is not an option? – Pieter B Nov 18 '16 at 15:56
  • Some languages allow you to specify a method on a class that converts it to another type without an explicit cast. Maybe add such behavior to the ZipCode class to implicitly convert it to a string so you can pass it to any method that accepts a zip code as a string. Then it doesn't matter. – Greg Burghardt Nov 19 '16 at 23:04
  • I feel like there is an answerable question here, but it's not about the name of a variable. What should you do in an application when data can be represented as a string or complex type? That's the question. – Greg Burghardt Nov 19 '16 at 23:07
  • I'm fearing your chosen example doesn't illustrate your point well, as it escapes me atm why a zip code should hold more data than the string version (and it's the data that matters, not the methods). As such I'm missing the "excessive" in that example. – tofro Jan 17 '17 at 23:24
3

Ideal solution:

Define a zipcode class to represent zipcodes and rewrite all your APIs in terms of it:

class ZipCode { string value; ... }

Advantages:

  • the internal apis of your code base are more semantically expressive;
  • you can restrict certain operations by type in a way that required extra (and repeated) computing time before. Consider a scenario where you print the zip code on package labels:

    An api like void printLabelField(ZipCode zipCode) will be able to rely on invariants of the ZipCode class that are already guaranteed on construction (like the maximum length of the value, or matching the character set of the output device to the zip code-compatible character set).

    On the other hand, with an API like void prinLabelField(string zipCode) you will need to perform those checks manually; If you forget, you have a bug, or an entire set of bugs, which can be repeated through inattention or lack of experience with the code base, throughout the development cycles of the project.

  • you have a natural customization point for further development that emerges from parameter-level polymorphism;

  • you have a natural point for debugging/logging/whatever.

  • you can test the functionality of the APIs in isolation.

Realistic solution (in case you have deadlines :) ):

Optimize for the common case: use string zipCode arguments throughout the code base and rename your ZipCode class to something else, making the name explicit in purpose (do not name your class ZipCodeObject: adding Object at the end of a name doesn't make the purpose of the code clearer, just like prefixing your method names with "do" or "run" doesn't make the method names more expressive).

Consider: DecomposedZipCode, ValidZipCode, ZipCodeComponents.

If in doubt, write some client code before choosing the name, and see how easy it is to read out loud.

  • 1
    That :) ): is making my head hurt :) – gardenhead Nov 18 '16 at 18:00
3

Consider using composition to have a single ZipCode class everywhere that consists of the simple string property that you want to use for the majority case and then composing in the more complex ZipCodeMap class via a constructor only when you need it. For example the simple representation of the ZipCode class would be (with constructors for each case):

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

The ZipCodeMap class would contain the methods for getting from the map but would be null and unused most of the time so wouldn't carry much overhead:

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

To make the class more usable for your case of just wanting to simply return a string zip code you could supply a ToString() method for the ZipCode class that would use the mode complex ZipCodeMap case only when it is initialized:

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

Then you can keep the client code and allow it to use the "zipCode" naming in both cases, e.g.

var zipCode = new ZipCode("77232");

Or for using with the mapping case:

var zipCode = new ZipCode(20.323, 23.233);

And in either case you could use it as a string:

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

Or access other properties as needed.

var urlQueryString = baseUri + "?latitude=" + zipcode.ZipCodeMap.Latitude
1

Firstly regarding having zip codes as strings versus having a ZipCode type that holds the string. There si a growing school of thought that favours the latter as it offers increased type safety, eg (in C# 7 syntax for brevity, but the language isn't important):

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

However, there are definite disadvantages in using a "heavy" type that contains/performs mapping details etc, when all you need is the zip code value. For example, to get a location, this type might need to perform a database lookup and thus either database details need to be injected into every instance, or it has to become coupled to a database service locator, thus making testing harder.

So for cases where the full details are needed, create a ZipCodeDetails class too, that contains that extra data. "zip code" will cause most people to picture just a zip code, so don't call your details class that. You hopefully will think of a better name than just ZipCodeDetails as "details" doesn't convey a great deal of information, but it'll hopefully give you the idea.

  • Do you think you could elaborate on the definitive disadvantages in using a heavy type versus just a string? I think it would be helpful in order to weigh the pros and cons and choose the most correct solution in a specific case – Vincent Savard Nov 18 '16 at 17:25
  • @VincentSavard, I don't really want to turn the answer into functional vs "rich data model" OO, so I've given a simple example only. – David Arno Nov 18 '16 at 17:46
-2

Since the ZipCode class is 1% just rename it to PostalCode and update your application. All the strings that are passed are still zip codes (99%).

This should be the smallest change and allow one to differentiate between the two items.

  • 3
    downvoted: such reasoning tends to disappear with time (and reassignment of developers and other factors) and this kind of compromise turns into pure cruft (the kind of thing that makes you look at the code and curse the guy who wrote it). It is the smallest change, but with time it will become a big snafu. – utnapistim Nov 18 '16 at 17:32
  • Every time I rename a class to add clarity it causes a big snafu. – Jon Raynor Nov 18 '16 at 18:26
  • @JonRaynor, um, maybe the problem is that when you rename classes, you aren't adding clarity? Just a thought, :) – David Arno Nov 18 '16 at 19:05
  • +1 for recognizing that zip codes are a localized form of postal code, so if this application will ever be used outside the US, it should have a PostalCode base class and a ZipCode subclass. – Ant Nov 18 '16 at 19:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.