I want to create a little todo app, with a "pedagogic focus" on clean software design. I spent some time reading about design patterns lately and I am trying to wrap my mind about which of them I could and should use.

Features I want my "todo items" to provide:

  1. Timing

    • has a due date
    • has a due date and due time
    • is "timeless"
  2. Completion

    • simple (you fulfill the task and cross it from the list)
    • cumulative
      • turns (you have to perform 3 sets of pullups to fulfill the task)
      • time (you need to gather 30 minutes in total of piano practicing to fulfill the task)
  3. Continuity (with given frequency, e.g. 3 days)

    • once (once done it's gone)
    • multiple (task is repeated n times with given frequency)
    • continuous (task is repeated indefinitely with given frequency)
  4. Optional reminder

I then tried to find design concepts to implement these features; here are my thoughts:

  1. Feature families 1-3 suggest three independent "properties" of a todo item, which could be three distinct strategy interfaces like TimingStrategy, CompletionStrategy and ContinuityStrategy.

  2. Yet, considering timing strategies, all three available concrete strategies, i.e. DueDateTimingStrategy, DueDatetimeTimingStrategy and TimelessTimingStrategy, would use different sets of parameters (time, datetime or no time fields) which can not be put into practicve using the standard strategy pattern.

  3. I could also introduce a TimingParameter class which is used by TimingStrategy to address this problem, but then again...: Is this too much in this case?

  4. For my understanding, a decorator pattern is not suitable here because I want to pick one behavior for the todo's "guts" and not "augment" behavior on the todo's "skin". Also using decorator would allow me to pick more than one timing behavior which should not be possible.

  5. The completion feature could actually be provided by using the strategy pattern, because one-interface-for-all is thinkable: The SimpleCompletionStrategy could be seen as a special case of CumulativeTurnsCompletionStrategy where the number of turns is 1.

  6. The continuity property could be implemented by using the strategy pattern

  7. The reminder feature could be part of the basic "todo" class, but what does this mean when picking the TimelessTimingStrategy? Such a todo can't have a reminder! If the todo class checks whether reminding is allowed this breaks separation of concenrns. Should the TimingStrategy interface provide a canReminderBeSet() method?

  8. If one of the feature families (or a new one) would allow for pick-n-out-of-m-options, this would demand a decorator pattern? Or an OptionManager class?

  9. Am I trying to force in patterns just because I read about them recently?

  10. Not using composition/patterns but inheritance would lead to class explosion DueDateSimpleOnceTodo, TimelessCumulativeTurnsContinuousTodo, ...

  11. Maybe one of the feature families could be implemented via inheritance (e.g. Todo base class with an isDue() method and strategies for completion and continuity and then child classes according to Timing, thus DueDateTodo, DueDatetimeTodo and TimelessTodo, for instance.

I'm very open to your suggestions and comments. Your propositions for an architecture and comments on specific bullets in my lists are both very welcome!

  • 2
    "Am I trying to force in patterns just because I read about them recently?" Yes - although you're by no means the only person to do this. Patterns are something which are a guide to good design, not a straightjacket in which you have to force your design. Apr 4 at 15:59
  • @PhilipKendall So, in this specific case: What do you propose? Having these features implemented in one class, setting the options via enums, for instance?
    – LCsa
    Apr 4 at 16:09
  • 2
    @LCsa the question is fairly too broad and on the edge of the offtopic. Reading your comments one may think that you are asking us for a whole consultancy and tech. analysis of your concrete problem, what would not lead to any meaninful answer for the rest of the community.
    – Laiv
    Apr 4 at 20:21
  • @Laiv It surely is broad in terms of word count :D The "off-topic" label I cannot understand, though. While I am definitely not intending to ask the community to post the code to my program, I am clearly struggling to pick a design path for the "todo class" for which I formulated some requirements. I was hoping for some reactions like "This differentiation seems to small to justify a full-blown strategy pattern implementation, try to go with enums and take it from there." or "These many properties usually show that you should split up your class." etc...
    – LCsa
    Apr 4 at 20:33

The first thing I would ask myself here is: is it really necessary to use any kind of inheritance at all?

So why not start by defining four types for Timing, Completion, Continuity and Reminder, then your ToDoItem is simply a class with four attributes of these types, plus a fifth string attribute "description". The four types may be implemented by simple classes and enums, but I don't see a compelling reason to start with inheritance here. For example, the Timing class could consist of

  • a date-time attribute

  • an attribute of an enum-type with 3 values (like Timing_date, Timing_datetime, Timing_none), which holds the information how to interpret the date-time attribute.

In reality, I would not even start with the five attributes. I would start with the most simple design for a TodoItem class I can think of - a class with just a "description" attribute. Then I would implement it - in code! No inheritance, no composition, no strategy, no decoration, no patterns. Next step would be to write a program around it to display, add, or delete these Todo items, and also wite some tests for it.

When adding more features to the program, one by one, the TodoItem class can be extended as required, as well as the tests. Whenever some part of the program becomes too unstructured, refactor, to avoid duplication, to keep the system comprehensible, maintainable and testable.

Maybe later, during this processs, when your program grows, Timing may require three different subclasses, and it will evolve into a strategy object (see Fowler's refactoring catalog, "Replace Type Code with Subclasses"). But do yourself a favor and don't start with that "just in case". Better ask yourself regularly "do I really need a pattern for this, or can keep things simpler by using no pattern"?

  • You even added some more input, thanks :)
    – LCsa
    Apr 11 at 14:38

I have grave misgivings about your approach.

Software patterns are not a "recipe book."

The purpose of software patterns is not to stitch together an application with them, like sewing a quilt. First of all, that won't work. Because Software Patterns are not a comprehensive catalog, there are large gaps in what you can do with them, so (if, in fact, you wish to call yourself a software engineer someday), you must still write code that solves novel problems.

Software patterns are well-known solutions to specific problems in software development. They're not a development strategy. Instead of asking "What software patterns should I use," you should be asking "Is the specific architectural/algorithmic problem I am looking at already solved by a well-known software pattern?

In other words, your approach should be problem/solution based. Unless you can identify a software pattern that solves the specific problem you're having, you don't need it.

  • I'm fully with you that patterns shouldn't be employed for their own sake - which I wanted to point out in 9) :) I think I stated my wishes for functionality - do you have recommendations on how to implement such a class (group of classes) if not by one of my own guesses? Can you point out, which of my thoughts could be a good way to go or which can not and why? How would you design a class/collection of classes to fulfill these requirements? So far I have no hints on how to go on... Thanks!
    – LCsa
    Apr 4 at 16:14
  • @LCsa: "How would you design a class/collection of classes to fulfill these requirements?" - How do you eat an elephant? One bite at a time. Start with the most simple TodoItem class you can think of (for example, just with a description and no time/date). Implement it - in code! No inheritance, no composition, no strategy, no decoaration, no patterns. Write a program around it to display, add, or delete these items. Write some tests for it. Now start to add more features to the program - one by one. Extend the item class as required, as well as the tests ...
    – Doc Brown
    Apr 5 at 9:50
  • .. Whenever some part of the program becomes too unstructured, refactor (and maybe some design pattern will become apparent or necessary when the program grows, to avoid duplication or keep the system testable.). Rinse and repeat. And that's it.
    – Doc Brown
    Apr 5 at 9:50
  • @DocBrown These are some useful tips to start with, thank you. Although I want to keep nagging a bit: I do already know that I will have these two "families of timings" for instance (date/datetime and timeless), so ignoring them for now will just postpone the same question for a couple of hours. Would you mind sharing some thoughts about how you would plan such different timing behaviors while avoiding class explosion due to other options/properties (such as their completion type for instance)? Thanks!
    – LCsa
    Apr 7 at 20:23
  • @LCsa: what do you mean by "class explosion", and why do you think it could happen in this situation?
    – Doc Brown
    Apr 7 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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