1

Let's assume that I have an attribute called Ticket_Case that has a predefined value (for the sake of arguments, say 1 to 20) that model different real-life use cases. They are an attribute to an ticket itself.

In a different context of the system, I need to generate different messages based on the ticket case. These specific messages are used in this one place, and nowhere else.

The pragmatic solution would be to create a huge switch / case function that maps a case to a message. The less pragmatic, but also less error-prone solution would be to directly attach the message into the Ticket_Case object in some way. Okay, sure that works. It's muddling two different contexts a bit, but that's ok.

But now assume we have a growing number of contexts. Each of them don't really have a lot of similarity, so code reuse is not an option. We could group them all together in the specific Ticket_Case object, but that puts a lot of responsibility into that object. But the other alternative would be the huge switch / case method in every context boundary to translate the Ticket_Case to the corresponding object. This alone screams for many easy-to-miss issues if a new Ticket-Case were ever introduced.

So my question is: What is an acceptable way to deal with these "huge enums" that have correct, yet different uses in different places of the system, without having them either grow out of control internally or creating a lot of brittle mapping code?

1
  • 1
    Don’t have the time for a proper answer right now but this sounds like a perfect opportunity for the visitor pattern. Mar 16, 2020 at 17:39

4 Answers 4

4

One could introduce the concept of a two-dimensional message table, where the dimensions are the use case number and something like a context number. This "table" is just a helper class with one object, and the object could be initialized once at startup time (or you could implement it with a huge switch-case internally, if you think that is easier, but this is just an implementation detail).

Then, provide a public method getMessage(ContextId id) in the Ticket_Case class which returns the correct message depending on the use case number of the ticket and a context parameter. This method works just as a facade to the message table.

This makes the Ticket_Case look like as if it was responsible for the messages, from outside, but internally the responsibilities are split up between the two classes.

4
  • Is there an standard for creating said tables? I swore I heard about a file format for resource tables before that's supported by all major IDE's but I can't remember...
    – Joe
    Mar 17, 2020 at 9:15
  • @Joe: you are overinterpreting what I mean by "table". I am talking simply of a thin capsule around a two-dimensional array for the messages, with some convenience methods to access it by indexes representing the "use case number" and "context id". How you access this internally (utiliizing lists of lists, or dictionaries, or whatever), is just a minor implementation detail.
    – Doc Brown
    Mar 17, 2020 at 10:30
  • 2
    @Joe: If any of your coworker are familiar with software localisation (modifying software to make them display strings from multilingual translations chosen at run-time), they will be able to guide you through the approaches and solutions. Even though your situation isn't related to localisation, the techniques and design methodologies used to solve these two problems are similar.
    – rwong
    Mar 17, 2020 at 15:58
  • @rwong that's exactly the thing I remembered, that it sounds quite similar to localisation. Gonna do some research there, thank you!
    – Joe
    Mar 17, 2020 at 22:57
2

Similar to Docs answer, but instead of a table use a dictionary

TicketCaseMessager : IMessageGenerator
{
    private Dictionary<string,string> messageMap;

    public string GetMessage(Ticket t)
    {
         return messageMap[t.Case] ?? "Unknown Case";
    }    
}

This allows you to switch out the mappings in an OOP way and avoid tables if required.

You could add unit tests to ensure all TicketCases are dealt with

0
1

Answers seem to be additive this time: as an extension to Docs or Ewans answer, add a unit test, which goes through all TicketCases and all Contexts, and fails if the message is the default unkown message ("Unknown Case" or null or whatever).

This way you make sure that nothing is accidentally left out.

1
  • Arguably this could also be done at runtime in a static block (at least in Java). Coming from a place where testing is rare...
    – Joe
    Mar 17, 2020 at 14:26
1

An alternative to Doc's and Ewan's answer is to use a different object in each context, and let that object have whatever functionality is proper for that context.

Objects shouldn't be defined by the data they have, but what behavior they provide. That means, if in one context the behavior for TicketCase is different than for another, then those are two different objects, even if they refer to the same 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.