views:

62

answers:

2

This is more of a design question and my main intention to post here is to get diverse inputs on the problem's solution.

What I want to create is a counter. A counter maybe single valued (single char / digit) or multivalued. So I've designed an hierarchy like this (read Key = Counter) :

                              Key
                               |
      -----------------------------------------------
      |                 |             |              |
SingleValueKey    MultiValueKey   NumericKey   AlphaNumericKey
  (there could be further mix and match between these types)

Now while designing the MultiValueKey, I thought I could simply use a collection of n-SingleValueKey to create a n-character counter. For e.g. a 2 digit (multivalue) counter will use an array of size 2 of SingleValue Numeric counters. What I'm eventually planning is a variable length alpha-numeric key set, base 64, for short urls. The numeric context is just a simple example.

What I'm stuck at now is the "rolling" or "wrapping" of lower order counters into incrementing higher order counters. Foe e.g. for a 2 digit counter, once the units place reaches 9, the next number will be when the units places wraps around to 0 and the tens place is incremented by 1.

I have a few areas I need your feedback on how this should be done :

  1. The single value counters should throw an exception when its reached its max value? Or should it automatically wrap around? Or should it allow the user to specify which of these approaches should be taken?
  2. In case the single counter should throw an exception (as I think), there should be a "reset" method to reset the counter to the start - the caller should handle the exception and call reset (before / after it goes on to increment higher order counters). Would this be a good design?
  3. When the counter is just initialized - new SingleValueNumericKey() - what should be the value of the counter? Should it be ready to use, with no value, OR should it be the first value in its value set?
  4. Similar to the prev question, when the "reset" method has been called, what should be the value of the counter?

Please help me with your valuable inputs here. If you have suggestions on the design itself, its more than welcome! If I take away something from this thread, I'm going to refer to it in my commits - so you will have the credit for the suggestion :)

Thanks,
Madhur Tanwani

EDIT: Added my final use case to clarify Jason's questions for all.

A: 

Do yourself a favor and create some use cases, so it's clear what you need. If you're just talking about N-digit numerical counters, what you have seems like overkill. But maybe not.

Jason S
What I'm eventually planning is a alpha-numeric key set, base 64, for short urls. The numeric context is just a simple example.
madhurtanwani
+1  A: 

Design each counter to know about the higher order counters, so you only have to interact with the counter in the ones place. You can use a constructor with a specified number of digits to create the higher order counter with one less digit. When you increment the counter, you can compare this to the base being used (number of values per digit) and reset to zero and increment the higher counter. Getting the value can also be done recursively.

It's good to do this design without exceptions. Exceptions should be used when there is an exceptional circumstance, beyond what is normally expected. For example, when a file read fails, or a network socket won't connect. You shouldn't use exceptions for normal events like a counter rolling.

The reset method should set all counters back to zero, and this is the default value they should have when they are initialized. In fact, the reset method can use the same linking so that when you call reset on a counter, it will reset its value to zero and call reset on its higher counter, if set.

You can build on this same design to abstract away concepts like the specific characters used to represent each value. You can also make a function to get the whole counter set as a string, recursively, using an abstract function for the character for each value so that you can subclass it like you designed.

public class Key {
  private Key higherKey;
  private int base;
  private int value;

  public Key(int base, int numberOfDigits) {
    this.base = base;
    if (numberOFDigits > 1)
      this.higherKey = new Key(base, numberOfDigits - 1);
    this.value = 0;
  }

  public int getNumericValue() {
    int value = this.value;
    if (this.higherKey != null)
      value += (this.higherKey.getValue() * this.base);
    return value;
  }

  public void increment() {
    this.value++;
    if (this.value >= this.base) {
      this.value++;
      if (this.higherKey != null)
        this.higherKey.increment();
    }
  }
}
Erick Robertson
Thanks for your inputs on exceptions and reset. The reason I believed an exception should be thrown is that a single digit/char counter should not really know what should be done once its max limit is reached (unless specified explicitly - which would be the other design).But I agree - what you suggest about exceptions usage does make sense. Thanks!
madhurtanwani
In that case you can use the observer pattern to notify the higher order counters of the overflow event.
Peter Tillemans
However, I'm not sure the "linking" model is a good one. A single digit counter knows more than it should to function.Once suggestion from a colleague is to use a Mediator pattern - have a guy on top of the n-SingleValueKey objects. Whenever the single valued counter rolls, it should intimate the mediator (director as in GoF). The mediator should then decide what to do next - what do you think??
madhurtanwani
Agree Peter - I just posted a Mediator pattern suggestion from my colleague. Yup the observer pattern could be used to talk to the mediator. Thanks!
madhurtanwani
Don't get too caught up in patterns. Instead, do what works. Where you need the ability to have a subclass change some functionality in the future, you can always adjust the design to expose the method you need to override. Don't worry so much about all the possible scenarios. Instead, code to what you know you need. The design can always be expanded later.
Erick Robertson