Live data from Hacker News

Premature Abstraction

arendjr.nl

81–90 of 116 posts

Re: Premature Abstraction

#81
post #12
post #4

Although I agree with the recommendations, I cringe at the definition of abstraction. In a sane world, abstraction doesn't mean defining classes so much as it means identifying important unifying concepts. DRYing your code by moving a method to a common base class isn't abstraction in any important way, it's just adding a level of indirection. In fact, I'd argue that this example is the opposite of abstraction: it's…

If by abstraction you mean identifying unifying concepts then I cant understand how you reasoned yourself into thinking that identifying a common method and sharing it between multiple classes by the means of the super class is not abstraction. You have identified a commonality - the common code, common method. By your definition it's abstraction.

Example of abstraction vs indirection:

If I said 'User', we both know exactly what that means. It's so semantically simple that laypeople know what it means. But our implementations could vary wildly. Someone who's just taken Java 101 will be thinking of a class with getName() and setName(). But someone who's just taken SQL 101 will think of a User as an INT or UUID, where features are added by referencing that user's id from different tables. User is abstract because it's understandable and not locked into any particular implementation.

I love Kafka. But it's a PITA to program against, at least in Java. I cannot code directly against it and always need to make my own wrapper classes to construct and poll it. I'll make ResumingKafkaReader and RewindingKafkaConsumerFactory, etc. These are not abstract, because they are very specific about what and how they do things. They are concrete behaviours wrapped with 1-2 levels of concrete indirection.

However, I might inject one of my Kafka indirections into a business logic class, interfaced as a Supplier, which makes it abstract. I can then unit test my class, safe in the knowledge that my class cannot know if a User came from Kafka or just a test stub.

So I push back on the thesis of the article, and double-down on doing things abstractly first and foremost. This is closely related to the dependency inversion principle. Write (and test) your business classes around Users and other abstract things. Once you've done it wrong a few times and eventually gotten it right, then you can start writing the indirections (e.g. AbstractKafkaFactory) which the article rightly claims slow you down in the beginning.

Re: Premature Abstraction

#82
post #47
post #17

I like to start with a fairly unambitious bit of procedural code and gradually introduce abstractions when it starts to get complicated or repetitious. Straight code becomes functions, occasionally a group of functions cry out to become a class. In C++ this is a huge effort to do - change hurts more there. In python it's much less painful and I end up with a program that is some imperfect composite of object oriented…

Yes, I'm doing that a lot, too. I'm often astounded how hostile some languages are to later changes - e.g. java always feels resistant to change, while dotnet and especially python are more amenable. I.e. I totally transformed a program from function to oo in python without much sweat - would have been a total pain in dotnet or java

Can you give examples for what you changed?

Re: Premature Abstraction

#83

Earlier quoted context omitted.

Agree 100%: static typing (for code completion) + method/data bundling is the major win in OO, and it rarely gets talked about for whatever reason. It's unfortunate that inheritance became such a major focus of practical OO languages. Would love to see a composition-first OO language. Might have its own problems, but would at least be interesting.

Go, Rust, Zig, etc all support static typing and method/data bundling without any explicit language support for implementation inheritance (interface inheritance in general and especially when structural rather than nominal is not nearly as much of an issue and doesn't create strict tree hierarchies). Rust has support for variance and subtupint so perhaps it's not as pure of an example, but it's pretty heavily restri…

Go's first class support for typed return tuples and Interfaces is a lovely replacement for inheritance (E.G. an Interface of type blah supports this signature). They function as an API contract, if a given class implements the requirements of the Interface, it can be cast to and used as that anywhere which accepts that interface.

Re: Premature Abstraction

#84
post #76

Though I agree about the point about not creating objects/instances where a pure function will get the job done, I disagree with the general stance against OOP. I think OOP is absolutely essential to simplicity. FP tends to lead to too many indirections with data traversing too many conceptual boundaries. FP (if used dogmatically) tends to encourage low cohesion. I want high cohesion and loose coupling. Some degree o…

Thanks! I would just like to clarify I’m actually not opposed to OOP at all, and at several points I tell people it’s fine to go in that direction for the problems where you need it. I do try to warn against it as a go-to solution before you’ve understood what are the problems that actually need fixing, which it sounds like we’re pretty aligned on. Indeed if you pass by value/use immutability where feasible, you alre…

My view on FP is that one of its main benefits in terms of avoiding bugs is that it forces you to turn all your 'movable state' into raw messages. The fact that it also abolishes 'unmovable state' is just a quirk of it, not necessarily a benefit.

My main point in support of OOP is that OOP doesn't prevent you from making all your 'movable state' into raw messages too. Properly encapsulated instance state is not dangerous. What I like about OOP is that it offers some additional benefits in terms of high cohesion and loose coupling because co-locating logic with related state helps to write high cohesion, loosely coupled modules. It reduces the amount and complexity of state that needs to be transferred between modules. It allows my messages and function/method signatures to be even leaner than FP allows.

If you want to catch a taxi to the airport, FP still requires that you bring a jerrycan of fuel and steering wheel to give to your taxi driver, the only restriction is that he cannot alter them (no mutations). The benefit of this is that you can fully trust that the integrity of your jerrycan and steering wheel has been maintained after the trip and you can then confidently re-use them for your airplane as well... Anyway, as elegant as that seems in theory, it's not quite how the world works.

Re: Premature Abstraction

#86
post #47

Earlier quoted context omitted.

Yes, I'm doing that a lot, too. I'm often astounded how hostile some languages are to later changes - e.g. java always feels resistant to change, while dotnet and especially python are more amenable. I.e. I totally transformed a program from function to oo in python without much sweat - would have been a total pain in dotnet or java

Can you give examples for what you changed?

Difficult on mobile and I'm without access to k PC ATM.

Basically, my predecessor hat build a python program which had N modules which where applied to a data structure - maybe think of correction steps. First spell checking, than turn manual headlines into actual headlines etc. Originally, they operated on a global data structure, and every new module required calling it on the global structure, so extension was difficult. Worse, every module hat internal state, e.g. the number of spelling mistakes. Reporting these things at the end was cumbersome. There where "required" functions in each module, but it was difficult to newcomers to discover who they where. We changed it so that every step was a class which inherited from a base class, so adding a new step was as easy as inheriting from the base class. Furthermore, we added Auto discovery, so just adding the class in a module was sufficient to get it executed.

Re: Premature Abstraction

#87
post #21

Earlier quoted context omitted.

OO code for domain modeling might be, to date, the single greatest source of disillusionment in my career. There are absolutely use cases where it works very well. GUI toolkits come to mind. But for general line-of-business domain modeling, I keep noticing two big mismatches between the OO paradigm and the problem at hand. First and foremost, allowing subtyping into your business domain model is a trap. The problem i…

> dynamic dispatch makes it harder for newcomers to figure out the business logic by reading the code This is definitely a potential problem, but I note that you can also get into this mess without OO in any language that lets you put a (reference to) function in a variable. Or, god help you, operator overloading.

Operator overloading isn't to blame.

If I overload shift-left `That said, unless you are writing a math library or some container, there's not many good uses for operator overloading.

Re: Premature Abstraction

#88
post #21

Earlier quoted context omitted.

> dynamic dispatch makes it harder for newcomers to figure out the business logic by reading the code This is definitely a potential problem, but I note that you can also get into this mess without OO in any language that lets you put a (reference to) function in a variable. Or, god help you, operator overloading.

Operator overloading isn't to blame. If I overload shift-left ` That said, unless you are writing a math library or some container, there's not many good uses for operator overloading.

> If I overload shift-left `Yours and C++'s.

Re: Premature Abstraction

#89
I think there's plenty of good advice in this post, though the OP doesn't talk as much about the evils of premature abstraction as one might like. Still, they do talk about how to avoid it using reasonable programming guidelines.

In the talk about data structures, I was reminded of Fred Brooks quote from MMM: "Show me your flowcharts, and conceal your table, and I shall continue to be mystified; show me your tables and and I won't usually need your flowchart; it'll be obvious." Several people have translated it to something like "Show me your code and conceal your data structures, and I shall continue to be mystified. Show me your data structures, and I won't usually need your code; it'll be obvious," for a modern audience.

Several years ago I was happy to work with several people with an interest in philosophical history. We whiled away the hours thinking about whether these quotes represented something of the tension between Hericlitus (You cannot step into the same river twice) and Plato (everything is form and substance.) So... I think the observation about the alternating utility of form and function is an old one.

Re: Premature Abstraction

#90

My general take (and w/ the caveat that every system is different) is as follows: - procedural code to enter into the system (and perhaps that's all you need) - object oriented code for domain modeling - functional code for data structure transformations & some light custom control flow implementation (but not too much) I like the imperative shell, functional core pattern quite a bit, and focusing on data structures…

OO code for domain modeling might be, to date, the single greatest source of disillusionment in my career. There are absolutely use cases where it works very well. GUI toolkits come to mind. But for general line-of-business domain modeling, I keep noticing two big mismatches between the OO paradigm and the problem at hand. First and foremost, allowing subtyping into your business domain model is a trap. The problem i…

FWIW - OO has the same (or more) problems in GUI toolkits.
Post reply on HN