Recently I re-read The Pragmatic Programmer mostly while on a break in Mexico. I also did my fair share of sitting in the sun, eating and event drinking but I read a LOT as well.
And I keep learning, or at least reminding myself, of several things I think are really useful to all of us.
In terms of writing OOP code the Law Of Demeter is one that is really useful. Basically in this Law you should only access :
– items (properties, methods, constants, etc) of the of the object you are part of
– items (properties, methods, constants, etc) you are passed as parameters
– items (properties, methods, constants, etc) of objects you create in your code
This limits the knowledge your code needs of the internals of other objects.
Suppose we have a set up something like :
Global Dim someGlobalVar as integer
Class FooBar
Dim i as integer
Sub fubaz() as string
End Sub
End Class
Class Bar
Dim fubar as FooBar
Sub Constructor()
fubar = new FooBar
End Sub
Sub Baz()
End Sub
End Class
Class Foo
Dim BarInstance as Bar
Sub Constructor
self.BarInstance = new Bar
End Sub
Function HasInstance() as Boolean
// this one is OK !!!!!
return (BarInstance is nil) = false
End Function
Sub CallBazOfBar()
// this one is OK !!!!!
BarInstance.Baz()
End function
Function CallBazOfPassedInBar(b as Bar)
// this one is OK !!!!!
b.Baz()
End Function
Function CreateAndCallBazOfBar()
// this one is OK !!!!!
dim b as new Bar
b.Baz()
End Function
Function ReachesThroughBarToFooBar() as integer
return BarInstance.fubar.i
End function
Function ReliesOnGlobal() as integer
return someGlobalVar
End function
End Class
Then for us to adhere to this principle the following new methods on our Class Foo would NOT be ok :
Function ReachesThroughBarToFooBar() as integer
return BarInstance.fubar.i
End function
This one is NOT ok because it requires the Foo class to know more about the internals and implementation of the Bar class AND FooBar class than it should. Instead the Bar class should have a method on it that can be invoked to get details of its properties or classes it owns.
Function ReliesOnGlobal() as integer
return someGlobalVar
End function
This one is NOT ok because it requires the Foo class to rely on a global variable that is outside the scope of the instance and the method using it.
Methods in our Foo class should ONLY use :
- methods in the instance itself (HasInstance is OK)
- methods of any parameters passed to the methods like CallBazOfPassedInBar(b as Bar)
- Any objects created/instantiated within the method like CreateAndCallBazOfBar
- methods in the instances properties (in this case methods of Bar like CallBazOfBar)
- A global variable, accessible by the instance, that is in the scope of the method (this last one is harder to explain)
I like the Wikipedia summary which is
For many modern object oriented languages that use a dot as field identifier, the law can be stated simply as “use only one dot”. That is, the code
https://en.wikipedia.org/wiki/Law_of_Demetera.b.Method()
breaks the law wherea.Method()
does not.