9

I've heard the phrase being thrown arround and to me the arguments sound completely insane (sorry if I'm strawmaning here, Its not my intention), generally it goes something along the lines of:

You don't want to create an abstraction before you know what the general case is, otherwise (1) you might be putting things in your abstractions that don't belong, or (2) omitting things of importance.

(1) To me this sounds like the programmer isn't being pragmatic enough, they have made assumptions that things would exist in the final program that doesnt, so they are working with to low of a level of abstraction, the problem isn't premature abstraction, it's premature concretion.

(2) Omitting things of importance is one thing, it's entirely possible something is omitted from the spec that later turns out to be important, the solution to this isn't to come up with your own concretion and waste resources when you find out you guessed wrong, it's to get more information from the client.

We should always be working from abstractions down to concretions as this is the most pragmatic way of doing things, and not the other way around.

If we don't do so then we risk misunderstanding clients and creating things that need to be changed, but if we only build the abstractions the clients have defined in their own language we never hit this risk (at least nowhere near as likely as taking a shot in the dark with some concretion), yes it's possible clients change their minds about the details, but the abstractions they used to originally communicate what they want tend to still be valid.

Here is an example, lets say a client wishes you to create an item bagging robot:

public abstract class BaggingRobot() {
    private Collection<Item> items;

    public abstract void bag(Item item);
}

We are building something from the abstractions the client used without going into more detail with things we don't know. This is extremely flexible, I've seen this being called "premature abstraction" when in reality it would be more premature to assume how the bagging was implemented, lets say after discussing with the client they want more than one item to be bagged at once. In order to update my class all I need to is change the signature, but for someone who started bottom up that might involve a large system overhaul.

There is no such thing as premature abstraction, only premature concretion. What is wrong with this statement? Where is the flaws in my reasoning? Thanks.

  • 1
    The opposite of premature abstraction is YAGNI - You Ain't Gonna Need It, which in my opinion is almost always wrong. Almost. I have seen it happen, but it's rare. I mostly agree with what you're saying here. – user1118321 Feb 4 at 4:01
17

At least in my opinion, premature abstraction is fairly common, and was especially so early in the history of OOP.

At least from what I saw, the major problem that arose was that people read through the typical examples of object oriented hierarchies. They got told a lot about making everything ready to deal with future changes that might arise (even though there was no particularly good reason to believe they would). Another theme common to many articles for a while was things like the platypus, which defies simple rules about "mammals are all like this" or "birds are all like that."

As a result, we ended up with code that really only needed to deal with, say, records of employees, but were carefully written to be ready if you ever hired an arachnid or maybe a crustacean.

  • So premature abstraction isn't the act of abstracting, it's the act of abstracting beyond the specs level of abstraction. That makes a lot more sense, and I could see that actually happening. I guess I just need to make it clearer to my colleagues that I'm working at the level of abstraction outlined by my manager. – James Feb 4 at 11:02
  • 1
    @James: it can also mean to abstract "too much" becond the specs of version 1.0, since you believe you know of a new requirement in the spec of a later version, an therefore already implement v1.0 in a more general way than necessary. Somethings, it is good to envision what might happen in a later version, but there is also a certain risk of overengineering. – Doc Brown Feb 4 at 13:24
  • @DocBrown I would call that premature concretion. The act of abstraction is to remove pieces of information, not add them. If you are adding more to the abstraction than is necessary then you are assuming something about lower levels of abstraction. I'd like to differentiate between the two as I think calling that "abstraction" doesn't make much sense as the definition of abstraction is "the process of extracting the underlying essence" Where as concretion is "characterized by or belonging to immediate experience of actual things or events". – James Feb 8 at 3:08
6

It's important to remember that abstraction is a means to an end. You use abstraction to uniform behavior across your program and make the addition of new classes straightforward in cases where you'd expect new classes to be added but only when the abstraction is needed in that very moment.

You wouldn't add abstraction simply because you might need it (with no real basis for thinking you'll need to add new classes in the future). A proper metaphor here might be plumbing. Hopefully you'll agree that a 6-directional pipe which allows water to flow up/down, east/west, north/south would be the most flexible type of pipe you could have. You could theoretically use a 6-directional pipe anywhere where a pipe is required, and block off the unneeded directions, right?

If you tried to fix a problem with a leak under your sink and found all the sections of pipe were 6-directional pipe pieces, you'd want to pull your hair out in frustration at the guy who designed it that way. Not only do you not know where the problem is, but it would almost certainly be more straightforward to simply start from scratch done in a proper way.

Of course coding isn't plumbing, but the metaphor still stands. Abstraction is like using those 6-directional pipe pieces. Use them when you honestly believe you may one day in the near future need to connect pipes from all 6 directions. Otherwise abstraction is simply complication, not much different than using a pattern where none is required or using a god class which attempts to do everything. If it is not being used, and will not likely ever be used, you're ultimately adding an additional class for nothing.

Admittedly, the art of writing programs is very very abstract conceptually. It's simply worth mentioning that abstractions don't exist for the sake of being an abstraction but because they're practical in some real way. If you feel the need to use abstraction, so be it, but don't ask me to check your plumbing afterwards. ;)

  • You seem a bit class-centric… Even if there are many classes, abstraction is not limited to be used in / benefitting them. – Deduplicator Feb 8 at 11:47
  • 1
    @Deduplicator Fair enough, but that doesn't change my point. If abstraction is used, it should be immediately beneficial or it should be a planned change in the near future. It matters not that we're referring to classes or functional programming. – Neil Feb 8 at 12:59
3

When I learned about Object Oriented Analysis and Design, many years ago, we would start with a plain English description of the system the customer needed.

Looking through that, any noun (or noun phrase) would be considered as a possible class. Any verbs (or verb phrases) would be potential methods on classes. So "bagging robot" could be a class BaggingRobot. "Open a bag" might become method OpenBag.

After a few iterations, this would turn into a class diagram.

At this point, there are no abstract classes. The customer doesn't want an abstract concept of a bagging robot. They want a robot that puts things in bags. All the classes are concrete and have a set of methods.

Abstract classes are only introduced when it becomes clear that:

  • There are several similar classes that could form a hierarchy.
  • They actually share something in common, so that a base class performs a useful purpose.

To me, "premature abstraction" is assuming that any BaggingRobot must inherit from some BaseRobot, and, worse, trying to develop a set of methods for BaseRobot before you even know what is common to all robots.

  • Abstraction isn't synonymous with abstract classes. An interface is an abstraction. I'm talking about abstractions in general, not just abstract classes. Perhaps I could have made this clearer by using an interface. If you develop your system on the same abstraction layer as your client then it will likely be the case that you would end up with some abstract classes that need to be extended from as language is naturally abstract. Working lower than that is what I call "premature concretion" where you naively assume that the clients ideas of how the system will work, are the same as yours. – James Feb 8 at 2:54
  • A BaggingRobot object that puts Item objects into Bag objects is already an abstraction of a real bagging robot that puts things in bags. – immibis May 9 at 2:11
1

It sounds a bit like you want to solve a problem that does not yet exist, and that you have no good reason to assume will occur, unless you just believe the client is wrong in what they're requesting. If that's the case, I would recommend more communication over implementing a guess solution, abstract or otherwise. While it might seem innocuous to just slap "abstract" on the class definition and feel safe knowing you can extend it later, you might find that you never need to, or you may find yourself extending it in ways that over-complicate the design down the line, by abstracting the wrong things.

Going with your example, do you imagine that it is the robot itself, the items being bagged or the method of bagging that will change down the line?

Premature abstraction really just means you have no good reason to perform the abstraction and since abstractions are far from free in many languages you may be incurring overhead without justification.

Edit To answer some points from OP in the comments, I'm updating this to clarify and respond in a way that doesn't require a long comment chain while addressing points (1) and (2) more specifically.

(1) To me this sounds like the programmer isn't being pragmatic enough, they have made assumptions that things would exist in the final program that doesnt, so they are working with to low of a level of abstraction, the problem isn't premature abstraction, it's premature concretion.

(Emphasis mine). Assuming things would exist in the final program that doesn't is exactly what I see in your example. The customer asked for an item-bagging robot. This is a very specific request, if somewhat vague in its language. The customer wants a robot that bags items, so you produce a pair of objects

public class Item {
/* Relevant item attributes as specified by customer */
}
public class BaggingRobot {
  private Collection<Item> items;

  public void bag(Item item);
}

Your model is simple and precise, it follows the requirements set by the customer without adding any complexity to the solution and without making an assumption that the customer wanted more than they asked for. If, when presented with this, they clarify the need for the robot to have interchangeable bagging mechanics, only then do you have enough information to justify creating an interface or other method of abstraction, since you can now point to specific value that is added to the system through the abstraction.

On the contrary, if you start with abstraction from pure pragmatism, you either spend time creating a separate interface and implementing it in a concretion, or create an abstract class, or whatever manner of abstraction you like. Should it then turn out that the customer is satisfied with this, and no further extension is necessary, you have spent time and resources in vain, and/or introduced overhead and complexity into the system for no gain.

In regards to (2), I agree that omitting things of importance is not on its face a hallmark of premature abstraction. Abstraction or not, you can omit something of importance and unless its found far down the line of a chain of abstraction, it will not be harder or easier to sort that out either way.

Instead, I would interpret that as meaning that any abstraction runs the risk of obfuscating your domain model. Incorrect abstraction can make a system very difficult to reason about, since you are creating relationships that can drastically affect the further growth of the domain model and domain understanding. You run the risk of omitting not important information about the thing being abstracted, but about the system as a whole, by taking your model down the wrong rabbit hole.

  • "It sounds a bit like you want to solve a problem that does not yet exist, and that you have no good reason to assume will occur, unless you just believe the client is wrong in what they're requesting." - This is not at all what I said, in fact I made it clear that I was only building based off of the words the client used. Its the act of not wanting to do this what drove me to using abstractions. – James Feb 8 at 2:34
  • "I would recommend more communication over implementing a guess solution" - You mean like I suggested? To quote what I said in OP " the solution to this isn't to come up with your own concretion and waste resources when you find out you guessed wrong, it's to get more information from the client." – James Feb 8 at 2:38
  • "While it might seem innocuous to just slap 'abstract' on the class definition and feel safe knowing you can extend it later" - When I say "abstraction" I mean "abstraction" I don't mean "abstract key word". Interfaces could be abstractions, abstractions are fuzzy logic, that's what this entire post is talking about. The client is communicating in terms of abstractions so we should program in abstract terms until we know more about the domain. – James Feb 8 at 2:41
  • 1
    "you might find that you never need to" - This doesn't make any sense. A client says "I want a robot that bags items", you create an abstraction that meets that criteria. Any further information you get from the client is going to be detailed information about how they want the bagging to be done. This doesn't invalidated your abstraction, the client doesn't suddenly change their mind about a core idea behind what they are buying. The robot is still going to bag items. The abstraction you created will still apply. – James Feb 8 at 2:44
  • "or you may find yourself extending it in ways that over-complicate the design down the line, by abstracting the wrong things."... Did you honestly read what I said or just the title? Seriously? Read point (1) & (2) – James Feb 8 at 2:47

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.