3

I know my question sounds a bit weird so I will try to explain my issue with examples.

In my application, I have two types of business objects : Shops and Customers for example.

Both of them can be associated with multiple Tags, with the exact same properties : let's say Id and Name.

So a customer "John" can be tagged "wealthy" and "annoying", and a "Shop" would be "made of bricks" and "located in a mall".

Since those two type of tags can never be interchanged (John cannot be made of bricks), should I create two child-classes with all properties deriving from a base Tag class like ShopTag and CustomerTag or keep a common Tag class despite it has no business sense?

I know it's a meta question and there is no Yes/No answer, but I'd like to hear good practices from seasoned programmers.

PRO

  • Does not refer to the same business reality
  • Prevents other developpers to mix them up later on
  • Improves readability
  • Comes from different databases, I have no control on that point (should I take that in consideration?)

CON

  • Leads to a more rigid structure
  • Looks a bit weird in the code to get pure inherited classes (c# example hereafter)

Thanks for your hints

   public abstract class TagBase
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class ShopTag : TagBase
    {

   }

    public class CustomerTag : TagBase
    {

    }

  • 1
    What benefit does the base class give if they are separate business concepts? If they are different but it turns out they share some behavior, favor composition over inheritance. – Rik D Jun 20 at 21:00
  • @RikD: Just an example of how subtypes can be used like this: class TaggedObject<TTagType> where TTagType : TagBase { public IEnumerable<TTagType> Tags; } and class Shop : TaggedObject<ShopTag> { }. I'm not saying OP needs it here, I'm just showing an example use case for that base class. – Flater Jun 21 at 0:22
3

There's a better option.

You want to separate shop tags from customer tags and you struggle with the fact that they are both just tags. Then why not add another property to tag for differentiation?

In addition to id and name you could introduce type, which is of type TagType. Then create an enum TagType with members Shop and Customer.

| improve this answer | |
  • Yes you are completely right, but that won't prevent future developper from misuing it : they could still have a Shop with a Customer-Typed Tag associated. I can create functions to check for that case but that's a bit of overhead. Anyway, thanks for your hint! – XavierAM Jun 20 at 16:06
  • 1
    @XavierAM A developer would have to be aware of it of course but it is not likely any developer will provide the option to add shop tags to a customer. Users will ultimately assign the tags and the type property will allow you (or the next developer) to limit the labels that can be assigned to the appropriate type. – Martin Maat Jun 20 at 16:57
  • 1
    @XavierAM: "I can create functions to check for that case but that's a bit of overhead" And your branching tag type classes (and all the ensuing generic code that uses the base class) somehow aren't overhead? – Flater Jun 21 at 0:36
  • If you do this, you are defeating the point of typed, object oriented languages. – RibaldEddie Jun 21 at 4:05
  • 2
    @RibaldEddie: Typed object oriented languages don't outright force you to make all possible distinctions using types. What you're suggesting is the equivalent of forcing the use of a Man and Woman class instead of giving your Person class a gender property. And then, by extension, LivingMan, DeadMan, LivingWoman and DeadWoman when your Person class also needs to track if they have died or not. This is not how types should be used in a typed object oriented language. – Flater Jun 22 at 8:05
3

or keep a common Tag class despite it has no business sense?

Does not refer to the same business reality

Not all code directly translates to or stems from a business need. If a common class makes sense for a particular code design, then there's nothing wrong with using it. Assuming the design itself is appropriate of course.

Since those two type of tags can never be interchanged (John cannot be made of bricks)

But can you state for a fact that there will never be a tag which may overlap? It's easy to gloss over possible future tags that overlap if your current ones simply happen to not overlap.

should I create two child-classes with all properties deriving from a base Tag class like ShopTag and CustomerTag or keep a common Tag

It very much depends on several points:

  • Is there a direct need to enforce validation here instead of relying on the tag creators to do the right thing?

Take StackExchange for an example. I can tag my question however I like, the sytem does not block it in any way. But there is an implicit expectation that the users will sanitize any bad entries that may occur.

Trying to write an algorithm to do so takes a lot of effort and there can be a lot of false positives and negatives, so the effort is not worth it. The same can apply to your case, you haven't really shown any reason to want to actively enforce this other than "I could do it".

  • Are you going to expect tag creators to always know in advance which entity a tag describes?

Even though it makes sense to you, it might not make sense to end users to categorize tags like this. You're still going to rely on your tag creators to categorize tags correctly - so why can't you then just rely on these users to apply tags with the same level of correctness?

  • Will there never be any overlap between entities that a tag might apply to?
    • or if there is, how do you expect to handle that case?

If you need to start handling overlap, it's going to get really hard, because you're going to have to design a validation system that is really complex to develop, and really complex to wield by your tag creators.

That being said, there is a range of options you could take here:

  • Let the users manage their own content
  • Create an enum type (TagType) and give the Tag class a property of that enum type.
  • Make specific Tag classes for each type and use inheritance of a generic base class to specify which tag type a certain object uses.
  • Store these tags in different tables and give each entity a FK (albeit with a cross table due to many-to-many) to their specific tag table.

You haven't really shown why you need to have this enforced/validated. Without a direct need, I suggest you err on the side of not writing something you don't know you need yet.

Some direct feedback to your question:

Prevents other developpers to mix them up later on

Wait, is it developers who are using specific tags, or end users? Because tags tend to be a sort of "free form categories" used by end users to sort their data.

If specific tags are a developer concern, then these specific tags are effectively a constant in the codebase? I mean that regardless of where the data is stored, your code expects certain tags to exist - these cannot be created or deleted by end users after release?

Improves readability

That's highly arguable. More complexity detracts from readability. Since you haven't expressed why you need this, it's unclear whether it's going to add or detract from the readability.

Comes from different databases, I have no control on that point (should I take that in consideration?)

Do these databases live independent lives? If one database changes its tags (let's say tag names now come as a list of translations), does that mean the other might not receive the same update?

If they are deployed and versioned independently, then you're always going to need separate classes, even if these tags could be freely mixed.

Whether you then still use a base class very much depends on whether you expect to have code that can operate on either entity without needing to know its specific type (nor the specific type of its tags).

| improve this answer | |
2

This seems like a good use case for a generic type. You can define a type Tag<T>, and then where relevant code that deals with tags depend specifically on Tag<Shop> or Tag<Customer>, without you needing to create those two separate classes.

| improve this answer | |
  • That would be an odd application of generics. There will be no instances of T in objects of Tag<T> which makes it pointless. I don't see how this could be used meaningfully in code either. – Martin Maat Jun 20 at 17:11
  • 1
    I think it would be an example of a phantom type. The use is to make sure that a tag meant for a customer isn't meant where one for a shop is required, and vice-versa. – bdsl Jun 20 at 17:27
  • OK... And then Shop should have a property of type List<Tag<Shop>> for its labels? – Martin Maat Jun 20 at 17:43
  • @MartinMaat Yes. – bdsl Jun 20 at 18:34
  • Clever! This ensured that developers can’t misuse a tag class for the wrong purpose, at least not by accident. But doesn’t this loose some of the flexibility of runtime polymorphism that you’d have with 1 class and and 2 subclasses? – Christophe Jun 21 at 8:16
1

It's easier to lump splits than to split lumps. There are at least two different possibilities for the future evolution of this code:

  1. The tags stay split apart forever
  2. The tags need to be merged.

If they are separate, then in case 1, you're happy since nothing needs to be done. In case 2, you can just rename ont of the two tags, say ShopTag to Tag, remove the declaration of the other one (CustomerTag( and replace the usages of them with Tag.

If they are joined together then while you are happy in case 2, in case 1 you will have to examine every place the single tag type is being used to figure out which tag type it should be in that case.

If the code for a tag is simple enough, I would suggest actually having two versions that do not depend on each other, (say if they just both held a string). If there is enough code you become worried about the two types going out of sync, then you could pull out shared static methods that operate on their members, which each type will delegate to.

Static methods are just the simplest option. If there are enough members you get tired of passing them all to static methods, you can wrap those members in an inner class, which could resemble the abstract base class in your initial example. If you find your self passing these members to many different complicated methods that don't make sense to be static, then it may make sense to have an ITag interface that both types can implement, so the methods can be non-static.

| improve this answer | |
1

Never say never. Unless it’s impossible.

I could very easily imagine that your system, may one day manage Purchase of those customers in those shops. What bout the new PurchaseTag? Should it be yet another independent transaction related tag entered by the user? Or could it be automatically assigned by the system for example based on the most expensive product chosen, or the most used shop tag searched by the user online in the month preceding the purchase? In the latter case some overlaps would be needed, without putting in question the overall design

A never-assumption very often appears to be a not-now-statement. When you make important design decisions you should therefore carefully ask “what if”, just to be sure that you won’t regret that decision later.

Keep your design open and future friendly: Don’t close doors if you don’t have to. Your abstract class is in this regard a wise choice:

  • in the end, a tag is a tag (this is not likely to change)
  • at the same time, it allows some differences/specialization (open for extension)
  • and generic code written for the tag parent will (at least if LSP is ensured) work with all future kind of tags not yet invented.

But what about behavior and separation of concerns?

The only thing that bothers me is that we did not mention at all any behaviors. A class is not just a wild aggregation of values.

The question is therefore to know if the shop and customer tags only differ by the values they can take (value space) or also by their behavior.

If it’s only about the value-space, I’d consider another approach:

  • Keep the tags general: Use Martin’s approach to implement a qualified tag: each tag is associated with an identifier telling what the tag is about.
  • specialize the context where the tag is used and in particular its containers.

In my view, this approach would implement a better separation of concerns: the tag class is focused on the tags themselves, and the responsibility for using the right tags is moved to another class.

This would allow you a higher degree of freedom to innovate by specializing tags according to their own nature (e.g. single language tags as now, multilingual tags, graphical tags, voice tags, ...) and at the same time ensure through the tag context (for example using dependency injection for the factory and selection of the right subset of tags in the tag repository) and the tag containers, or a tag policy, that no mistakes can be made in assignments.

| improve this answer | |

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.