5 ms·
On the other hand, the Law of Demeter says that you should almost always be passing in and fetching primitive types. At least in an API. Does it not? Though I'
by nessus42 14y ago
On the other hand, the Law of Demeter says that you should almost always be passing in and fetching primitive types. At least in an API. Does it not?
Though I'm guessing that "primitive types" would include algebraic data types, which serve the same purpose here as little objects like IDObject. And since Java doesn't have algebraic data types.... All the more reason to switch to Scala.
Edit: I should have written "built-in types" above, rather than "primitive types".
- peeters 14y agoHmm, I've never seen Demeter interpreted to mean that you should favour primitives over rich types. Can you explain your interpretation?
- nessus42 14y agoI've heard the Law of Demeter at times stated explicitly as that you should usually return only primitive data types from an API. Rich Hickey certainly claims this too, although he doesn't refer to it as the Law of Demeter. The Law of Demeter is also sometimes summarized by "One dot: good. Two dots: bad." Or from Wikipidia: In particular, an object should avoid invoking methods of a member object returned by another method. 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 a.b.Method() breaks the law where a.Method() does not. Edit: I should have written "built-in types" above, rather than "primitive types".
- deleted 14y ago[deleted]
- hkolek 14y agoThat has nothing to do with primitive types. Otherwise in Java it would mean you shouldn't return a String ever because it's not primitive! It has nothing to do with primitive types whatsoever. It's about encapsulation, i.e. you should not depend on the internal structure of an object. As a simple example, when one wants to walk a dog, one would not command the dog's legs to walk directly; instead one commands the dog which then commands its own legs. i.e. this is violating the law: dog.getLegs().walk() and should instead be written: dog.walk() with this implementation: class Dog { void walk() { legs.walk(); } }
- nessus42 14y agoBy "primitive data types" I didn't mean to exclude built-in collections of primitive data types. Point taken, though: I probably should have said "built-in data types", or some such. I didn't even mean to exclude algebraic data types, as I explicitly mentioned. Though I think that the question of whether algebraic datatypes should be allowed is an interesting one. Also, clearly allowed would be any objects provided by the standard library, since returning those would not couple your code to the code of the API. As Rich Hickey expresses it, I believe, you should only return from an API data that you could send over the wire without sharing any of the API's code. If you stick to this rule, then, for instance, it is much easier to make your application distributed. (Though I'm not sure to what degree Hickey and The Law of Demeter would agree on everything. E.g., returning some hairy nested dictionary of dictionaries of dictionaries of strings to represent a book, or what have you. I don't know what Rich Hickey would say about that.)
- dhimes 14y agoThe Law of Demeter isn't about data encapsulation. It's trying to solve the problem of tightly-coupled code methods. It is a close cousin of "Don't get data from objects and operate on them- ask the contain object to do the operation for you." The Law of Demeter says, "Don't call a method on an object that was returned by a method of a different object." When you do this you are creating a tightly-woven daisy-chain which will lead to inter-dependency hell should something change in the future. Instead, you should either (1) ask the object you are calling to call the other object for you, or (2) call the third object method directly, without relying on the intermediary to get it. In other words, with objects X, Y and Z. Assume that X has Y. You want to run method fz on Z. Don't do: Y.getZ().fz Do do: Y.runFz() // calls Z.fz or instantiate a copy of Z in X and simply Z.fz The data encapsulation principle is also about keeping things loosely coupled. By passing data around as an object, the interested parties need to care far less about the details (that is, their signatures don't change EDIT:TYPO _as_ the code changes). This is a really good thing to do in the "build early and try it" style of building projects as adding data (parameters) to the signature is relatively painless.
- nessus42 14y agoIf I understand you correctly about the Law of Demeter, and we were to use an `Id` object to represent an ID, then the following would be verboten: val id: Id = db.findUserBySsNumber(ssNumber) println("id=" + id.toString()) // Violates Demeter! Instead we should do something like val id: Id = db.findUserBySsNumber(ssNumber) println("id=" + db.idToString(id)) // Demeter is happy. Boy, it would be a lot easier if `id` were just a string to begin with! Also, now we're physically dependent on db's data structures (i.e., `Id`). If we were to want to move `db` to be on a server, we would have to share at least some of the server's code. If `id` were just a string, we would be less tightly coupled to `db`.
- dhimes 14y agoIf I knew what your code was trying to do I might be able to write to it directly. But these guidelines would suggest that if you are going to call methods on the id object, it's best, if possible, to put the methods in the id object itself.