2

Just a litle question about good practices in Object oriented programming.

Let's Image that I have a class like this (pseudo-code) :

class Activity{
   construct(duration){
           this.duration = duration
      }
}

Now I want to define 2 types of Activities : "workTask" and "freeTime". I can imagine 3 possibilities :

  1. Add a property to each instances of my class 'Activity'. Something like "Activity::type"
  2. Inherite twice of the class "Activity" without changing anything exept the type of those classes. One would be "WorkTask" and the other "FreeTime"
  3. Delegate the type assignement. The easier way might be by creating 2 arrays "workTasks" and "freeTimes" and store activities in those arrays.

I actually prefer the last choice but I don't know if it's the recommended way to do that stuff. Are those 3 patterns acceptable (even the second one that is in my opinion the weirder) ? Is there any other good ways to do it ?

Morgan
  • 589
  • 9
  • 20
  • Regarding the second option, note that some statically typed languages, like Haskell, allow type aliases for a similar purpose: [Type and newtype](https://wiki.haskell.org/Type#Type_and_newtype). – Alexey Feb 15 '20 at 12:27
  • @Alexey Thanks for the tip. I never seen that kind of action – Morgan Feb 15 '20 at 14:22

2 Answers2

2

Best practices calls for (1) "Add a property to each instances of my class 'Activity'. Something like 'Activity::type'"

This will allow you to:

  • Put all activities in the same array and still know which are which.
  • Change the activity type at runtime.
  • Separate an array of activities into two arrays.

Neither of the other two options is as flexible.

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • Thanks for the reply. Notice that if it's easier with that solution to get the type of an activity, it's harder to get activities of a type : probably means to add a command in the constructor to push each instances "Activity" in a global array or in a static property "Activity::instances" and iterate throw that array with a "if" statement to check the type. I wait before defining an answer as the "good one" while that question is more or less a question of opinion and I want to get more opinions as possible ^^. – Morgan Feb 15 '20 at 14:42
  • If you used inheritance you would also have to "iterate throw that array with a 'if' statement to check the type." in order to check the type and if you used the other option, you would have no way at all of checking the type once they were all in a single array. The first solution (Composition) is the most flexible and appropriate. – Daniel T. Feb 15 '20 at 14:51
  • "the other option, you would have no way at all of checking the type once they were all in a single array". That's true but I don't have to use flat arrays. I can just use instead an array of arrays. When I say that I want to get more opinions, it's more that I would like more "it depends" answers with that kind of structure : "In a case ~~~~~" - "Solution ~~~~~ is better" - "Because ~~~~~". I think you provides the best solution in most of cases. I also think it's interesting to get exceptions. – Morgan Feb 15 '20 at 15:15
1

The answer to this kind of question is always "It depends". There are many factors to consider when choosing which approach to use.

If you need to check the type of activities a lot, then 3 is a bad idea, as you need to loop through arrays in order to find out whether a particular activity is a WorkTask or FreeTime.

If WorkTask and FreeTime differs in behaviour/data (e.g. WorkTime could have an extra taskName field or something), then you should use 2. Also note that even if they are the same now, it doesn't mean it will stay this way forever.

Both 1 and 3 will allow you to accidentally assign WorkTasks to variables that are supposed to store FreeTime. This might not be such a big problem in a dynamically-typed language, since you can do this with 2 anyway.

Don't forget that there are a fourth way: Composition

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Composition isn't a "fourth" way. It is option 1. – Daniel T. Feb 15 '20 at 14:53
  • Thanks for the reply. For that kind of problem, I don't understant why composition would help. I mean it could help later in the code to store objects instead of string to define the type of the activity and use those objects to modify the behavior of "WorkTask" and "FreeTime". But in my case I was just asking about ways to separate instances according to categories, not defining several behaviors for each of those categories. So compositing it's more a better way to do 1 than a fourth solution. Might be wrong and miss an advantage of compositing. – Morgan Feb 15 '20 at 15:04
  • @Morgan As I said, that really depends on how likely you expect `WorkTask` and `FreeTime` to stay structurally the same (both only store a `duration`) as time goes on. With inheritance/composition, you can easily do so if you want to add a property to all `WorkTask`s, such as `taskName`. But if in your problem domain, such things are not going to happen, then it's also fine if you just use a `type` property to indicate the type. – Sweeper Feb 15 '20 at 15:39
  • @Morgan On second thought, are you specifically asking about the situation where there are two kinds of objects that are exactly the same structurally, but you want to differentiate between them anyway? I wrote my answer (and the last comment) thinking that you were asking for something more general. Have I been misunderstanding your intention? – Sweeper Feb 15 '20 at 15:43