oopdesign-patternsreusabilityresponsibility

Ownership responsibility and reusability


I had a disscusion with a collegue about approaches to these topics in our project. Im making a Progress System, in short a ProgressMonitor class and a Task class, ProgressMonitor handles an array of tasks. All pretty straight forward stuff. But when it came to the tasks ui, we had to opinions. Each task has his own UI. We agree we should have an HUD class for handling UI. The difference in opinion is, he wants the HUD to handle each specific task's UI and so the HUD class would grow quite big and the task would have near to no logic about its UI. My opinion, The HUD would only handle the logic that is the same for all things in our project that need UI's, and all specific logic is handle by the objects that need UI. Advantage of his is some ownership (objects that need to acces some ui info can easily get it from HUD) My advantages, HUD stay light clean and reusable.

What would be more correct acording to OOP?


Solution

  • I'll start with questions that will affect the implementation:

    If the answer to Question 1 is Yes and separating the two responsibilies is hard, adding the UI logic to the Task can simply things a lot.

    If the answer to Question 2 is Yes then using the classic model/view separation will make writing new tasks easier as you only need to add the task logic to the Task class.

    You can have a TasksHUD class who's responsibility will be to handle a list of TaskUI class. Eeach TaskUI class will have a Task associated with it and handle the rendering of UI and presentation logic for this specific task.

    This way your TasksHUD class will manage how the list is shown to the user, while each list entry will be handled by the TaskUI class. The task presentation code will be reused.

    Task will only have the responsibilities of executing, changing it's status and other things that a task must do in your app (if you give a more details I can a more detaildd and probably more accurate description), while the responsibility of presenting a task will be given to a TaskUI.

    This way if you need to change the logic how a task is rendered, you have to change only the TaskUI class.

    You can write as many tasks as you wan't without having the need to write a UI related code for those tasks.

    If you need to change the Task class you may or may not have to change TaskUI class as the dependecy goes from TaskUI to Task. Some changes will affect the UI some won't.

    If the answer to Question 3 is Yes then:

    You can add the responsibility of handling it's UI to the Task. This way it would be easier to change them as they are in the same class. One problem here is that the Task class can grow having multiple responsibilies. Also sharing rendering code for similar tasks etc.

    Event in this case you can decouple the Task from it's UI in two classes Task and TaskUI but you will need a mechanism in your app that will associate a specific Task class with it's TaskUI class. This can lead to more classes and a complexity that may not wan't to manage. In the long run it may save you time (mostly from chaising bugs).

    Here is a pseudo code example:

    interface TaskObserver {
      void onTaskChanged(t);
    }
    
    interface TaskUI {
      void render();
    }
    
    interface TaskUIFactory {
    
      register(TaskUIFactoryRegistry registry);
    
      TaskUI create(Task task);
    }
    
    interface Task {
    
      TaskStatus status;
    
      execute();
    
      addEventListener(TaskObserver o);
    }
    
    class TaskA : Task {
      // implementation.....
    }
    
    class TaskA_UI : TaskUI, TaskObserver {
    
      TaskA mTask;
    
      TaskA_UI(TaskA task) {
    
        mTask = task;
        mTask.addEventListener(this);
      }
    
      render() {
        // rendering goes here
      }
    
      onTaskChanged(t) {
        // raise an event or signal to the TaskHUD that a refresh is needed
      }
    }
    
    class TaskA_UIFactory : TaskUIFactory {
    
      void register(TaskUIFactoryRegistry registry) {
        registry.Register(typeof(TaskA), this);
      }
    
      TaskUI createUI(Task task) { 
          return new TaskA_UI((TaskA)task);
        }
      }
    

    When a task is added your TasksHUD can use the TaskUIFactoryRegistry to get a TaskUIFactory that will create a TaskUI.

    Here are some resources that you can check that discuss these kind of issues: