3

Let's say I have a class called ValueSpec with these immutable instance fields:

min_value: int
max_value: int

description: string  

Each instance of ValueSpec is uniquely named. This unique name is its lookup/primary key in any kind of lookup mechanism we're talking about (database, dictionary, etc.)

When should this unique name be put in ValueSpec as an instance field, and when should it be decoupled? I am generating a dictionary of these ValueSpecs either way to achieve optimized lookup, but I'm not sure whether this unique name is considered part of the ValueSpec conceptually.

Advantages of decoupling this unique name from ValueSpec:

  1. ValueSpec's interface is slimmer for any client, especially when its unique name is only used for lookup, and not anything involving the processing of ValueSpec.
  2. It is possible to instantiate ValueSpec before its ID can be determined (unless the field was made mutable).

Advantages of coupling this unique name with ValueSpec (by adding it as a name: string field):

  1. In the event that this unique name is needed later on (let's say logging of the use of this ValueSpec), it will need to be passed along with ValueSpec anyway.
  2. It is impossible to instantiate ValueSpec without naming it.

In terms of sample Python code, I'm asking when to prefer decoupling ValueSpec and its unique name like this:

value_specs = {
    "unique_name_1": ValueSpec(0, 10, "description_1"),
    "unique_name_2": ValueSpec(1, 11, "description_2")
}

and when to prefer coupling it with ValueSpec like this:

value_specs = {value_spec.name: value_spec for value_spec in (
    ValueSpec("unique_name_1", 0, 10, "description_1"),
    ValueSpec("unique_name_2", 1, 11, "description_2")
)}
1
  • I added a bounty cause there a lot of objects in my code that make me wonder about whether their ID should be decoupled or not. Just looking for some pointers about when to couple vs decouple. Aug 31, 2020 at 0:44

4 Answers 4

2
+50

When the unique ID is only used for retrieval purpose and has no meaning for the object itself, you should keep the ID out. Why?

  • the ID only matters to the object’s context and we could as well imagine that the same object could be used in different contexts (e.g. with one unique id in one context, and another unique id in another context).
  • moreover if the object would be a value object, the id would de facto give it an identity that could contradict the idea of value semantic. If this sounds too abstract, a value object is an anonymous object that is only defined by its values; now imagine you would make a new object with different values but an existing ID: would this mean that it’s the same object because of the ID or a different object because of the different combination of values? Leaving the ID out removes this contradiction

When the ID matters for the object itself, i.e. if it is part of what makes the object’s identity regardless of the context, then, store the ID as attribute. This is typically the case if the object must be aware of its identity or if it represents a unique entity.

2
  • Amen, Brother. e.g.: I've seen the "ID signifying nothing" paradigm ignore otherwise valid compound keys. Subsequently for 1 or more tables, de-facto uniqueness and some table relationship(s) were enshrined in class' code not the DB. Over time (i.e. new coders, experience leaves, code fixes, code releases) table integrity was compromised. If there is no natural key, that's OK. This is not a hole in the data's soul needing absolution with false idols.
    – radarbob
    Aug 31, 2020 at 16:33
  • @radarbob I’m glad you bring that up. in fact, the compound key is here, hidden in the text: if the ID is only meaningful for the context, storing it in the object (or elsewhere) makes only sense if you also store something allowing the identification of the relevant context, i.e. in a database perspective, the other component of the composed key. In an UML perspective the, the id of the owning composite. I didn’t want to bring it up in the answer in order to keep it simple and not to distract OP with answer elents not directly related to the question ;-)
    – Christophe
    Aug 31, 2020 at 18:59
2

A filename only helps you find an object in a file system.
A primary key only helps you find an object in a database.
A reference only helps you find an object in memory.

An ID helps you find an object regardless of what it's currently stored in.

If you have the object now, you only have it because of how you found it. If you need to find it later, how you found it now only helps you later if it'll still be in the same kind of place. Use an ID and you don't care what it's in.

Given that, do you want to manage ID's by making seperate ID maps every time your objects wander into a different place or would you rather bake the ID into the object once and be done with it?

It is possible to instantiate ValueSpec before its ID can be determined (unless the field was made mutable).

You know, you can do both. Leave ValueSpec with no ID as a value object. Hold it with a ValueSpecEntity that has an ID. Don't pretend you know the ID when you don't. Having a valid ID means you can do certain things. When the things you can do change it means your type changed.

2

An ID can be used to indicate two things.

  1. It can be used to uniquely identify an object (primary).
  2. It can be used to identify a relation between two objects (foreign). (could be a relation between an object and a grouping)

Lets consider Book and Library as an example.

The ISBN of a Book represents an unique ID. Of all the books in the world there is only one with the ISBN 978-0-19-955240-5. There might be many copies of that book, many instances, but given the ISBN you can identify the Book.

Now think about the relationship between Books and a Library. The catalogue of books available in a Library is a type of relationship, the book is associated with the library. The Library has the Book available, probably more than one copy of the Book. Library therefore has an instance ID (CatalogueId) of a Book ID (ISBN).

This is what your talking about in your examples.

This is an a way to identify an object:

value_specs = {value_spec.name: value_spec for value_spec in (
    ValueSpec("unique_name_1", 0, 10, "description_1"),
    ValueSpec("unique_name_2", 1, 11, "description_2")
)}

This is a way to identify a relationship, between value_specs collection and ValueSpec() items:

value_specs = {
    "unique_name_1": ValueSpec(0, 10, "description_1"),
    "unique_name_2": ValueSpec(1, 11, "description_2")
}

library = {
    "unique_id_1": Book(0, 10, "description_1"),
    "unique_id_2": Book(0, 10, "description_1"),
    "unique_id_3": Book(1, 11, "description_2")
}

If you think of a collection as a way to group objects of a certain conceptual type together, like a library is a way to group books, then adding an item to a collection is really setting a relationship between the item to the group. That always needs an unique identifier, whether it it is a dictionary key or an array index, it doesn't matter.

So, if you are looking for some pointers about when to couple vs decouple.

What are you identifying? An object, or a relationship between an object and the group/collection it belongs too?

Hope that helps

1

The main questions I ask myself when I face this kind of situation is:

Is it worth it to build the entire model so I can work easily and quicly on my data or should I just do the job?

In SQL, index are only built once at the creating of the table and automaticaly maintained after that: As long as the memory usage doesn't block me, storing some id to help with indexing and sorting is likely good.

If your python program is not persistent and have to rebuild its indexes, just iterating the data to solve your problem may be easier, faster and maybe cleaner as your code will just be about crunching your data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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