http://qs321.pair.com?node_id=222257

Hi All, I am currently working out the object model for what is basically a ticket tracking system. The idea is to facilitate discussion between authors, editors and supervisors regarding documents. We have the tools already in place to create the documents and even different versions of documents, but what I'm developing now is a flexible object model to express a decision tree in such a way that if the policies of the group change down the road, altering the system to reflect the new procedures is as painless as swapping out a single module. This leads me to consider the Strategy design pattern.

Components:

  • groups: every individual belongs to a group.
  • user: a single person.
  • workspace: an listing of active "tickets" under someone's care. May belong either to a group or to an individual.
  • statuses: i.e. 'pending approval', 'approved', 'under editorial review', etc.

Here's an example decision tree:

Someone creates a new document | V The document goes a particular supervisor determined by the topic of the document. Status: 'pending approval'. / \ / \ The document gets rejected The document gets approved and returns to the workspace and moves to the editors' of the author for more work. group workspace. Status: 'pending approval'. Status: 'approved'. / / Document moves from editors' group workspace to individual's workspace. Status: 'under editorial review'. And several more options below, but you get the idea I hope.

Now it seems to me that I just need a single object to encapsulate where in the system a given document is, and what its status is. But I want the interface for this thing to be simple, so to the user it just seems like an object moving along a track. Something like:

Ticket::new(String group, String username, String documentID); # other + values will come from the db Ticket::save; Ticket::move(String direction, int steps); # e.g., forward if okayed, # back if not okay, sometim +es even # back to the beginning.

The hard part is that I want to be able to describe the decision tree, or track of possibilities, in a module that can be swapped at will. So I've got an idea to do something like:

package Track; our %switch = (); # to be filled in by subclasses. sub new { my ($class) = @_; # don't have any args in mind yet. my $this = bless({}, $class); return $this->init(); } sub move { AbstractMethod->throw("Not implemented in this class\n"); } + # raise exception unless # +implemented by a subclass package BasicTrack; use base Track; sub init { my ($this) = @_; # Add anonymous subroutines to the hash of possible combinations # of status and location. I.e., if the status is 'pending # approval' while the document is sitting in the workspace of a # supervisor, and 'forward' is the command issued to Track::move, # then the subroutine ought to set the status to 'approved', # change group to 'editors'. Other subclasses can inherit # or override these as necessary, to avoid duplication of behavior +. $Track::switch{case1} = sub { # do the stuff }; } sub move { my ($this, $arg) = @_; # complicated set of switch statements to figure # out which subroutine in %Track::switch to call. # This would be called from Ticket::move most likely. }

Right now I'm thinking about having each Ticket object contain a reference to a Track object, although this could be implemented as class methods in Track as well. I'm not strongly attached to either approach, although the latter would make this more of a classic Model-View-Controller arrangement than an application of Strategy. Anyhow, in the event of a policy change, a subclass of BasicTrack, or an entirely new subclass of Track could be implemented, without altering the other components.

Is this sensible or nuts? Shortcomings and pitfalls? Many thanks in advance! fever