Usage of self subclassResponsibility
Asked Answered
W

1

6

I'm new to Pharo and I'm having trouble grasping some concepts, specifically subclassResponsibilty.

I have the following objecttree:

AbstractDictionary
--TreeBasedDictionary (not abstract)
--AbstractArrayDictionary
----SimpleDictionary (not abstract)
----FastDictionary (not abstract)

All 3 implement the operations at:put: and at: (and more). I understood from class today that I can pull up at:put: to the AbstractDictionary with the following implementation:

at: thisKey put: thisValue
"insert new key / value pair"
^self 
    at: thisKey
    put: thisValue
    duplicate: [self error:'duplicate key']

Then I implemented a metod at:put:duplicate: in TreeBasedDictionary and AbstractArrayDictionary (because the implementation is the same for the latter's subclasses). Then, when I call at:put: on an object it won't find it in the instance of either TreeBasedDictionary , SimpleDictionary or FastDictionary but when looking up the tree it finds this method in the superclass (AbstractDictionary). This will then call self with at:put:duplicate: . This method is implemented by the 3 afore mentioned classes, and then the self which is pointing to the instance of the class that made the initial call is sent the message to execute at:put:duplicate:.

Now, AbstractArrayDictionary,SimpleDictionary and FastDictionary all have a method named size. Both classes at the bottom of the inheritence tree implement this method. AbstractArrayDictionary implements this method as follows

size
"dictionary size"
self subclassResponsibility 

Suppose I implemented this method for my TreeBasedDictionary, I suppose I should implement the size method in my AbstractDictionary class as shown above.

My questions: Why do I need to do it like that, and not like this:

size
"dictionary size"
^self size

And if so, do I remove the method from AbstractArrayDictionary and why?

Whittier answered 21/10, 2013 at 21:8 Comment(2)
You are talking about put:at: and then use at:put: in the example. Also you have both at:put:duplicate: and put:at:duplicate:. Are you sure that you haven't done any mistakes?Northerner
Oh, that's a mistake indeed. Both are talking about the same method. I will correct this now.Whittier
N
10

Ok, look.

First of all if you use

size
  ^ self size

you'll end up in the infinite recursion. You ask size of object and the size method asks size of the object itself and so on.

Now in general about what you should know.

  1. You should avoid duplication, that's why you are moving similar methods to the parent nodes in hierarchy. If you have a same method in all subclasses, move it into the superclass without hesitation.
  2. You can just implement methods where they should work. E.i. not write methods with self subclassResponsibility at all. Objects should respond to messages that they can understand. Can AbstractDictionary put elements in it? No. Then there can be no method.
  3. It's not nice to do things that I've written in #2 part. Because
    • when you browse AbstractDictionary class you get an idea that it should support at:put:duplicate:, but the functionality depends on the implementation (aka subclass).
    • when someone will be implementing another subclass of your dictionary, for example MagicBagDictionary and will forget to implement at:put:duplicate:, then during execution he will be gently reminded that this is a subclass responsibility and the method should be implemented. Otherwise he will get an error "message not understood by MagicBagDictionary".

Concerning the size method again: if you know at some point where you can get the size - do it. For example if AbstractDictionary has instance variable container that will hold all the element and should be able to tell the size of them - you can implement

size
  ^ container size

in the AbstractDictionary. But if you have no idea ho the element will be stored and how to calculate their size at that point, you should use:

size
  "dictionary size"
  self subclassResponsibility

This way AbstractDictionary is saying:

yes, you can get my size, but I have no idea how to calculate it because I'm not complete. But my subclasses should know that, so don't hesitate to ask.

Northerner answered 22/10, 2013 at 6:39 Comment(2)
Your explanation was nice and clear and made me understand this, thank you for that! One remark though. When I put size ^self size in the superclass, it actually does call the method in the leaf class that made the initial call.Whittier
@ChristopheDeTroyer, this means that you use super size form non size method of subclass. And it's a bad practice to call different method of superclass. Also if you think about this a bit more you can easily understand that this kind of implementation will generate a lot of WTFs per minute ;) I agree that we can make this work, but is this out goal?Northerner

© 2022 - 2024 — McMap. All rights reserved.