Hacker News new | ask | show | jobs
by enduku 1755 days ago
1. Creating a struct with desired state data

2. Creating functions that take a pointer to the struct as the first parameter

3. Declaring a variable with that struct type to create an instance of the object

4. Declaring another variable with that struct type for another object instance

1 comments

... is not, in fact, OO at all, by any meaningful definition. 1..4 is just programming. People have done it since long before there was any "OO" buzzword.

There are formal definitions of OO. The above satisfy exactly none of them.

There's usually a formal definition of something and a common definition of something, and most languages that get traction follow the common definition.

For example, the formal definition of the Liskov Substitution Principal:

> Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.

This doesn't allow any change in behavior when subclassing. Not even the addition of logging, which makes the overriding of methods generally useless. Yet languages still provide this feature (perhaps to their detriment, but they still do).

There's also a common definition of the Liskov Substitution Principal:

> Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

This is, generally speaking, what people are talking about when the mention the Liskov Substitution Principal, unless they're actively writing an academic paper on the topic.

Bringing up the academic definition when someone is using the common definition is 1. not relevant and 2. usually not helpful.

> This doesn't allow any change in behavior when subclassing.

Generally correct, but that's why you should only be "subclassing" abstract interfaces in the first place. Subclassing concrete object methods is a footgun, precisely because you have no way of enforcing which of these "properties" any code will actually be relying on, either at any given point in time or in the future. (Including code that's itself part of the base objects hierarchy and calling possibly-overridden methods, which means subclassing also breaks encapsulation!)

Concrete inheritance is very hard to make sense of semantically in the fully general case; if it's viable at all it is as a kind of mere specialization, and that's what the LSP is trying to get at.

I don't disagree at all. I tend to advocate for very shallow hierarchies in the 5-10% of code that I think benefits from an object oriented style.

But the point of the comment is the formal/common divide rather than the specific example.

When there are, as in the case of OO, multiple formal definitions, it is because there is no consistent "common definition" to compare any given example against, and relying on anybody's "common definition" means there is no basis for discussion.

Failing to match any of the formal definitions, as here, is a sure way to fail to reach a level where discussion is worthwhile. So, it is not, and I leave it here.

The above pattern using opaque pointers resembles objects in C++. A construct() and destruct() function returning opaque pointers (handles for the struct really) can act as custom constructors and destructors. The opaque pointer ensures a simplified API, protects internal struct data, and the defining code of struct can change without impacting the remaining source code.

This is in fact, by any meaningful definition of OOP, is a very reasonable pattern. With regards to formal definitions of OO, message passing aspect ought to be recognised rather than overemphasizing the object aspect.