Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I wrote a little app to manage an arbitrary series of tasks (e.g., call a SQL sproc and capture out-vars, run another app, run an SSIS package) with dependencies between tasks. Each task has a status (waiting to start, failed and waiting to retry, failed and abandoned, succeeded, etc.). Internally, each Task object has a field of the TaskStatus enum type. There's a TaskManager object which starts tasks and monitors their successes and failures, and a Dashboard WinForm which displays progress.

Should I expose each Task's status as an enum throughout the app, or only to TaskManager, or always expose only the string value? My approach has been to expose the status as an enum, but this means that not only TaskManager but also Dashboard has a dependency on the possible values for TaskStatus.

I almost posted this question on code review, but on balance I think it fits better here, sense it's mostly a matter of principle.

Dashboard only interacts with Task.Status in a two places. First, its value is displayed in a DataGridView which summarizes all tasks, and this really should use a text value instead, if only to allow for prettier values. Second, status values are compared when the Status column is sorted, and obviously this will work either way.

TaskManager is the iffy part. For example, a TaskManager has props NumTasksRunning, NumTasksComplete, and NumTasksWaiting. Originally, with Tasks exposing their statuses directly, I put this logic in TaskManager:

public int  NumTasksRunning   { get { return _tasks.Count(t => t.IsEnabled && (t.Status == TaskStatus.Running || t.Status == TaskStatus.Aborting || t.Status == TaskStatus.FailedAwaitingRetry)); } }
public int  NumTasksComplete  { get { return _tasks.Count(t => t.IsEnabled && (t.Status == TaskStatus.Succeeded || t.Status == TaskStatus.FailedAbandoned)); } }
public int  NumTasksWaiting   { get { return _tasks.Count(t => t.IsEnabled && (t.Status == TaskStatus.Waiting)); } }

I could move this logic to properties in Task, protecting TaskManager from needing to know about the enum:

public bool  IsRunning   { get { return _status == TaskStatus.Running || _status == TaskStatus.Aborting || _status == TaskStatus.FailedAwaitingRetry; } }
public bool  IsComplete  { get { return _status == TaskStatus.Succeeded || _status == TaskStatus.FailedAbandoned; } }
public bool  IsWaiting   { get { return _status == TaskStatus.Waiting; } }

However, there are other places where the code is much simpler if TaskManager can see tasks' statuses directly:

public void  Abort(Exception ex)
    {
    State = TaskManagerState.Aborting;

    foreach (Task task in _tasks)
        if (task.Status == TaskStatus.Running && task.CanAbort)
            task.Abort(ex);
    }

Of course I could add a new property to Task, maybe called CanAbortNow (CanAbort means the task can be aborted in principle, separate), but this is just one of several touch points. If I end up with an "IsX" property for every possible value of TaskStatus, have I really made the code any easier to maintain?

This app is for my personal use, so the choice really only matters in that I'd like to know the "right" way - if there is any consensus.

share|improve this question
add comment

1 Answer

I'm not sure you'll find a consensus, but:

In my opinion, it's pretty okay to reuse the enum; better than supplying arbitrary strings anyways... If the dependency is harder than you'd like then have an intermediary adapter layer. Let the UI have its own status enum, and the glue layer adapts one enum to the other to help decouple the change impact between the UI and the task layer.

I'd tend to favor having the adapter layer, but for small or infrequently changing apps it is perhaps an unnecessary complexity.

share|improve this answer
    
Thanks for the quick response. The TaskManager class is the intermediary, and it mostly separates tasks from the UI, but this is a remaining connection I could snip. –  Jon of All Trades Jul 23 '12 at 19:07
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.