Why is the OpenGL object creation/destruction model the way it is?

In OOP, we create an object and access its attributes directly or through a set/get method.

In OpenGL, we create an object ID as a handle to the created object, bind the object it to a target via the handle, call functions on the target, bind other objects, … With textures we do the same thing with additional elements in play: texture units.

Is there a reason the object creation/destruction workflow is this way? Is it a programming paradigm different from OOP?

OpenGL is a procedural API, not an OO API: Procedural programming - Wikipedia

The main reason for this is historical; when OpenGL was first specified C++ did exist but had not yet been standardized. C was the “common language” of computing, C is a procedural language, and therefore OpenGL became a procedural API. It’s worth noting that even Direct3D is also a procedural/C API despite the fact that it may not look so at first glance.

The “bind an object” paradigm is, however, another matter entirely and one that many people feel to have been in error. The first version of OpenGL didn’t actually have any objects - you got a single texture to play with and that was all.

Objects (and binding) were first introduced by the GL_EXT_texture_object extension: https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_object.txt

Even though it’s not explicitly stated, it seems obvious that the reason for binding to make a texture object current for all future operations was because it was the least intrusive option; the existing API could otherwise be retained unmodified.

From that point onward future evolutions of OpenGL followed the same model as texture objects, even when specifying new object types that didn’t previously exist. It was well understood and well accepted that this contributed to what was once a clean and elegant API becoming clunky and awkward to use (not to mention error-prone); just look at all the requests for direct state access to go core in the “Suggestions” forum.

Interestingly, even the original extension recognised this flaw, but it would not become as critical a problem until after the introduction of multitexture and buffer objects, by which time the damage was already done:

Previous proposals overloaded the <target> parameter of many Tex commands with texture object names, as well as the original enumerated values. This proposal eliminated such overloading, choosing instead to require an application to bind a texture object, and then operate on it through the binding reference. If this constraint ultimately proves to be unacceptable, we can always extend the extension with additional binding points for editing and querying only, but if we expect to do this, we might choose to bite the bullet and overload the <target> parameters now.