views:

31

answers:

2

This is a design question better explained with a Stack Overflow analogy:

Users can earn Badges. Users, Badges and Earned Badges are stored in the database. A Badge’s logic is run by a Badge Condition Strategy. I would prefer not to have to store Badge Condition Strategies in the database, because they are complex tree structure objects.

How do I associate a Badge stored in the database with its Badge Condition Strategy? I can only think of workaround solutions. For example: create 1 class per badge and use a SINGLE_TABLE inheritance strategy. Or get the badge from the database, and then programmatically lookup and inject the correct Badge Condition Strategy.

Thanks for suggesting a better design.

+1  A: 

I don't see why you should store a strategy in DB - strategy is mostly expressed in code (possibly with some configuration parameters which, in turn, can be stored in DB, but that is a different issue to me).

OTOH I would keep the Badge and its condition strategy in one class, which eliminates your lookup problem. In Java, a good domain model would be to represent badges as an enum, with an overridable method to determine whether a given user has earned that specific badge.

Update here is an example:

enum Badge {
  EPIC() {
    public boolean isEligible(User user) {
      // determine whether this user is eligible for the Epic badge
    }
  },
  CRITIC() {
    public boolean isEligible(User user) {
      // determine whether this user is eligible for the Critic badge
    }
  },
  ...
  ;

  public abstract boolean isEligible(User user);
}

But if you really want them separated, then in the constructor of e.g. LegendaryBadge you say this.strategy = new LegendaryBadgeConditionStrategy();

Péter Török
Thanks Peter. I don't want to store the strategy in DB. I am doing what you're suggesting, but I still don't see how a Badge is going to be associated to its Badge Condition without a hack. Imagine that the Badge has a BadgeCondition private field set by constructor or setter, but the BadgeCondition is transient.
Francois
@Francois, see my update.
Péter Török
@Francois, one further note: using a Strategy separate from the Badge would only make sense to me if the strategies for a specific badge could vary depending on external or internal factors. For badges this is not the case - the conditions for getting a specific badge are the same for all users at any given point in time. They may change over time, but quite rarely, which to me does not fully justify using the Strategy pattern here.
Péter Török
+1  A: 

How about a BadgeType enum with types that correspond to the Badges in the database? The enum can have a getBadgeConditionStrategy() method that returns the correct strategy for each enum value:

public enum BadgeType {
     SMARTNESS( new SmartnessBadgeConditionStrategy() ),
     WISDOM( new WisdomBadgeConditionStrategy(),
     ...;

     private BadgeConditionStrategy badgeConditionStrategy;
     BadgeType(BadgeConditionStrategy badgeConditionStrategy) {
          this.badgeConditionStrategy = badgeConditionStrategy;
     }

     public getBadgeConditionStrategy() {
         return badgeConditionStrategy;
     }
 }
JacobM