2

Over the years i have seen multiple cases where data is accessed and/or manipulated using a representative value which is internally resolved to the right object/data-field/algorithm.

Some examples:

// 1. Provide same logger globally using it's name
auto myLogger = LoggingService.getLogger("myLogger")

// 2. Get person object using a unique id
auto person = PersonCatalog.getPerson("12345")

// 3. Get the left child of a B-Tree node
auto lnode = BTreeNode.getNode("left")

// 4. Make player character do something based off a command
playerCharacter.performAction("move_north")

I understand, why one would use this approach for the logger, as you don't have to pass around concrete objects to a Logger instance throughout the whole program. I can also see the merit in the second example, as it could provide an interface to a dictionary or a database lookup.

But the third and fourth example in contrast smell of design flaws.

Does this kind of design have a name and is it a viable strategy for more than just a couple of special scenarios?

  • 1
    Key addressing? – Basilevs Nov 11 '15 at 4:56
1

I agree, the latter two appear to be code smells, and the first one also. But I believe the contrast posed in this question has more to do with choosing the appropriate data structures than with specific patterns. I'll go over each one, starting with the one that seems fine.

The #2 above looks okay (assuming the magic string is just for brevity) and uses the concept of an identifier. It's less a pattern and more of a basic concept used long before computers existed. In this case, the chosen identifier as a number string.

The #1 is a smell because it's unclear what should be passed in to get the right logger. Is "myLogger" an internal name, or a class to instantiate, or a connection string? I can't tell from this call, and I've seen all of those options used before. If I again assume the magic string is for brevity, it could be alright if the variable name and/or a comment clarifies it.

The #3 is a smell because it's using a very fluid and easy-to-get-wrong structure (a string identifier) to represent a very concrete concept. In fact, there are 2 or so correct strings you can pass in and virtually infinite incorrect strings. This is concrete enough that it should ideally be represented by a design-time validated structure like a class. Then when you get a member name wrong, the code won't even compile or will give a syntax error. The structure itself is important enough that the compiler/interpreter shouldn't even run it if incorrect.

The #4 is a smell in a similar way to #3 (no validated structure) and #1 (lack of clarity), but less specific than B-tree code in #3. (Assuming you have reasons for not representing actions using methods/functions, f.ex. client/server.) You could represent this with a pattern or two. You could use messaging or even the GOF command pattern, depending on the specific application. Having messages as concrete classes would both give your inputs a validated structure and give your callers a well-defined means to communicate with you.

In summary, most of these examples highlight the need to choose the appropriate data structure. Using strings to activate code branches is not nearly specific enough for well-known algorithms like b-trees. But it might be okay for a database connection. Choose wisely.

  • Nice write-up! Interestingly, i took Nr. 1 from the Python standard library - but i agree that it's confusing. When i first saw the getLogger() call i also was not quite sure what it does. I would love to upvote your answer, but unfortunately this is a new account and i dont have the rep yet. So, take my thought +1 ;-) – Luca Fülbier Nov 11 '15 at 18:36
  • Much appreciated. :) – Kasey Speakman Nov 11 '15 at 19:29

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.