37

According to Understanding "programming to an interface", as I understand, I think I should depend on abstract class only. However, in some case, for example, Student:

public class Student {
    private String name;
    private int age;
}

to modify it so that it becomes depend on abstract class only (which MyIString may be a new abstract class that wraps String):

public class Student {
    private MyIString name;
    private java.lang.Number age;
}

I think the modified one is more complex. And a more "real" example, say Address:

public class Address {
    private ZipCode zipcode;
}

which I need one type of ZipCode only, but if I modify it as:

public class Address {
    private IZipCode zipcode;
}

which IZipCode is an interface, then I think it may mislead other teammates that there would have other types of ZipCodes.

I think the cases above becomes more complex and less maintainable if I was allowed a class to use abstract class members only. So my question is, should I still follow "programming to an interface not implementation" if the "followed" one becomes more complex (in my view)?

13
  • 4
    How did you come to that understanding?
    – Theraot
    Dec 4, 2019 at 10:35
  • 40
    Addresses are complex—have you ever seen some from dramatically foreign countries? How can you assume there won’t be multiple ZipCode implementations? Dec 4, 2019 at 13:23
  • 23
    Somewhere you must use concrete types, otherwise nothing would run. It all depends on where and how you use them. For example: every object is just a bunch of bytes containing data and instructions... nothing stops you from using a plain java.lang.String in place of all the objects! Your functions will just interpret the String to obtain the information and the instructions to execute and you wont need any other type than String! That's clearly going too far in using concrete types.
    – Bakuriu
    Dec 4, 2019 at 18:30
  • 20
    You should read more about addresses and how little you know: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Dec 4, 2019 at 18:32
  • 15
    @D.BenKnoble exactly due to variety of formats in zip codes - or, more in general, in addresses - about which you don't know (and don't want to know) a thing, in 99.9% of applications you aren't going to need a complex class hierarchy for zipcodes, but you are best served by the exact opposite choice, i.e. a plain string without particular constraints. Dec 5, 2019 at 0:19

7 Answers 7

80

Programming to an interface means that you should focus on what the code does, not how it is actually implemented. See Telastyn's answer to Understanding “programming to an interface”. Interface classes help to enforce this guideline, but this does not mean that you should never use concrete classes.

I really like the zip code example:

In 1963, the United States Postal Service introduces zip codes that consist of five digits. You may now get the (bad) idea that an integer is a sufficient representation and use it throughout your code. 1983, a new zip code format is introduced. It uses 5 digits, a hyphen, and 4 other digits. Suddenly, zip codes are not integers anymore and you need to change all places that use integers for zip codes to something else, for example, to strings. This refactoring could have been avoided if a designated ZipCode class would have been used. Then, only the internal representation of the ZipCode class needed to be changed and its usages would have stayed the same.

Using a ZipCode class is already programming to an interface: Instead of programming against some concrete implementation ("integer" / "string"), you program against some abstraction ("zip code").

If necessary, you can add another level of abstraction: You may need to support different postal code formats for different countries. For example, you could add an interface IPostalCode with implementations like PostalCodeUS (which is just the ZipCode from above), PostalCodeIreland, PostalCodeNetherlands etc. However, too many levels of abstraction can also make the code more complex and harder to reason about and I think it depends on your application's requirements whether you want to use some ZipCode class or some IPostalCode interface. Maybe a ZipCode that internally uses a (unicode) string is good enough for all countries and country specific stuff, for example, validation, can be handled outside the class in some PostalCodeValidator / IPostalCodeValidator.

11
  • 2
    In other words, using (the public interface of) ZipCode in the definition of Address is already programming against an interface. Dec 5, 2019 at 15:08
  • 7
    It seems to me that appending warts like I to the names of types is a symptom of missing the point. It suggests that the interface is something on the side and the concrete type is what really matters. Your point here is exactly right. If you program to the public methods of a class, you should be able to turn that class into an interface if needed. Renaming the type by adding a wart just generates a lot of unnecessary work when the interface of the type did not change.
    – JimmyJames
    Dec 5, 2019 at 16:27
  • 2
    Interfaces that focus on "what the code does" are called "role interfaces". Do not confuse these with library interfaces that had no idea what your code would be. Not only do role interfaces make for flexible code, they clearly communicate what clients need from their services. Both ZipCode and IZipCode could be a role interface. However, java.lang.Number permits people with negative ages. At the very least, that's a very curious need. Dec 5, 2019 at 21:19
  • 5
    I've never seen the point of a prefix/suffix for interfaces. The only reason for calling an interface IAddress is if the I distinguishes it from an Address concrete implementation — which suggests you don't know why you had the interface in the first place! An implementation should be named for whatever distinguishes it (e.g. USAddress and UKAddress, or StringBasedAddress and FieldBasedAddress). And you don't need a prefix/suffix at all. Conversely, if you can't see how implementations could possibly differ, then there's probably no need for an interface.
    – gidds
    Dec 6, 2019 at 0:16
  • 3
    @JimmyJames True. The I convention is a product of failed education that starts with classes and mentions interfaces almost as an afterthought. In languages that support interface types, interfaces are the first-class types. Methods should take interfaces as their arguments and return interfaces; callers seldom need to know the actual types they're working with. Hence the Impl convention is fine because nobody ever sees it. Dec 6, 2019 at 21:07
48

"Programming to an interface" does not require the language keyword interface. It means you care about what promises the type provides about it's behaviour.

You don't care how java.lang.String does all the things it does, only that you can write

name = "aacceeggiikk";
age = 42;

"Programming to an implementation" would be using reflection to get at the private members of String, and fiddling about with them. And then being upset that an update broke your code because you were relying on something that was changed.

2
  • 6
    The first sentence of your answer is perfect imo. Dec 5, 2019 at 7:22
  • Every class consists of interface and implementation details.
    – Mohsen
    Jan 14, 2021 at 15:04
13

It's only a guideline

Just as a concept like KISS is a guideline.

If you follow the guideline to "program to an interface, not an implementation", you'd be violating the KISS principle. But by making a bunch of simple classes that repeat some code or actions, you'd be violating the DRY principle.

There's no way to adhere to every single guideline, principle or "rule" out there. You need to find out what balance makes sense for your application.

My own experience is that trying to make everything enterprise proof with abstracted layers on top of abstracted layers, is often overkill, and unnecessarily complex for many applications.

3
  • 20
    I rather disagree here. There is nothing that prevents you from programming to the interface of a single, concrete class. Dec 4, 2019 at 18:50
  • @dmckee In reality, every class is both an interface and an implementation. Interfaces, are just purely interfaces
    – Cruncher
    Dec 6, 2019 at 18:13
  • This doesn't really answer the question.
    – Andres F.
    Dec 6, 2019 at 19:01
7
  1. Programming to an interface concerns objects, entities that can also have primitive-typed properties like boolean, int, BigDecimal and String, to name some. On that level programming to an interface no longer applies.

  2. There is an other principal however, that is based on the experience that primitive types are used where there should be a wrapper to a more abstract value class. So instead

    String telephone;
    

    you might better have a value class Telephone:

    Telephone telephone.
    

    This can take care of country number, canonical form, equality and such. Again Telephone could be an interface and there could be an implementing class TelephoneImpl, but I - fortunately - seldom see this.

    Even better is when more than one primitive fields can be replaced by a more abstract class, like FullName, say with int similarityPercentage(FullName other).

  3. Maybe it should be mentioned that internal objects with internal implementations can utilize interfaces. A List has an interface Iterator iterator() implementing a local object with access to the list container. But here interface is part of the solution, not coding style.

Succinctly:

Programming by interface decouples the implementation class, allows several implementations, has a single responsibility as API. It concerns behavior, methods. Take JDBC as example.

Fields are concrete properties. They are data. They should also be abstracted, hiding implementation by using a class name and provide access methods, for a correct usage. However that is a different level. Take the classes LocalDate, LocalDateTime, YearMonth instead of the old Date for all.

7

To quote a colleague of mine - "You can spend hours designing a car on a computer, but at some point, the wheels actually have to touch the ground.".

i.e. not everything can/should be abstracted out, at some point you need a concrete type.

For your class with your zipcode, the question really should be what is a zipcode (in terms of your application)?

Answer - it's a string, so the property should be a string.

public class Address {
     public String ZipCode...
}

P.S. don't start getting into abstracting out the underlying language types - this way lies madness!

There are a number of answers/comments here commenting on the variety of Zip/postal codes in various countries and that is true, however, they are all ultimately strings.

Hopefully this Address class does not care what is in the Zipcode (just that it is a string) and there is another class that you use to validate your Address (and being able to switch out the implementation of that class may well be useful).

So, program to an interface where it makes sense to do so and particularly where the implementation may need to be altered or behaviour injected. Do not use an interface for everything!

1

In your Address/ZipCode example:

public class Address {
private ZipCode zipcode;
}

Tying yourself to a concrete implementation instead of an Interface leaves with 3 problems:

  • A - Internationalization - An e.g. Canadian Postal Code is something like V1C3X8, other countries addresses are different again. Good luck storing your customer's billing addresses in a globalized economy.
  • B - A change in implementation, which is why everything should be consumed as an interface - You might decide one day that it's more efficient to store Addresses as a What Three Words GeoCode stored as a number and a floor/unit number for multi-story building. Therefore Address could be be changed from something like this:
public class Address : IAddress 
{
    public IZipCode ZipCode{get; private set;}
    public string StreetName{get;private set}
}

To something like:

public class Address : IAddress 
{
    public Address(IGeoCodeService geoCodeService/*Dependency injected*/){........}

    private long What3WordsGeoCodeAsLong {get; set;}

    public IZipCode ZipCode => GetZipCodeFromGeoCode();//calls out to a service to 
    public string StreetName => GetStreetNameFromGeoCode();
}

When serialized, the second implementation is much smaller to store, and immune to being out-dated if some country decides to move Postal Code areas around, which happens more often than you think (with a trade-off of having to use a service to look up a Postal Code each time.), while anything that uses IAddress will not even be "aware" that the implementation is now completely different!

  • C - Unit testing, I see that you're using a .Net naming convention. I am not aware of any .Net testing framework that will allow you to mock the methods on Address and ZipCode in any classes where they are used, you need to consume them as IAddress and IZipcode.
0

This is my take on "programming to an interface". There are two pieces to it: inputs and outputs.

Inputs

I differentiate between objects that are data and objects that have behavior, especially side effects.

In both of your examples, I'd use the concrete classes: strings and zip codes are data, IMO. (I would NOT just make the ZipCode a String, as one of the answers suggested!!! You cannot just pass any old string and treat it like a ZipCode. Your ZipCode class should have different constructors for the different kinds of zip codes you might deal with. Interally you might store a string, but at least do validation in the constructors!).

However, if I needed an object that did "computing" of some kind (a vague term, I know), I'd use an interface. For example, if I needed an object that allowed me to search for Students by name, I'd take an interface for input. The interface would be something like:

interface StudentSearchInterface {
    Optional<Student> search(String query);
}

The reason this is a good idea is because you can swap out providers later, and also it allows you to write much better tests where you can code up "fake" StudentSearchInterface objects that return exactly what you want for your specific test. It might be hard to test the production class that implements StudentSearchInterface: what if it queries a server on the internet? Or reads from a database? The results might change between calls to search(), so you can't really test it.

Outputs

Your return values should either be "data" or an Interface as well. By data, I mean a class that really only has getters and/or setters and maybe some trivial methods like toString(), etc. Otherwise, do an interface. A common example is returning a List<Foo> instead of an ArrayList<Foo>. This gives you the freedom to change the logic inside your method to use something else that implements List instead of being stuck with a specific List implementer because that's what you used first.

Just like we mark some methods and fields as private to encapsulate them, we should use interfaces to only provide info on a "need to know" basis.

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