12 ms·
ValueObject
- GuiA 10y ago> In many situations using references rather than values makes sense. If I'm loading and manipulating a bunch of sales orders, it makes sense to load each order into a single place. If I then need to see if the Alice's latest order is in the next delivery, I can take the memory reference, or identity, of Alice's order and see if that reference is in the list of orders in the delivery. Presumably, an order would consist of much more than a list of items (order #, delivery address, etc.), including a unique one (order #), so comparing by value here would still work? If you're properly building your hierarchies, I'm not sure I see cases where comparing by reference would logically be desirable (of course, in the real world you probably want to still compare by reference in cases where performance might matter, or you're doing lower level work). I guess this isn't disagreeing with the author's main point - that Value Objects are desirable - but pushing it further: Value Objects are desirable the vast majority of the time. The second part, regarding immutability, I'm fully onboard with.
- hprotagonist 10y agothis sounds suspiciously like "labeled products are good", which seems fairly intuitive. easy-ish to do in statically typed languages, but something like attrs (https://attrs.readthedocs.io/en/stable/ https://attrs.readthedocs.io/en/stable/ ) or just namedtuples will do this in python.
- maxxxxx 10y agoDoes anybody get anything from his writing? I have seen several posts of his here and they all are either trivial and/or give a fancy name to some standard coding construct. I think what he is describing here is the advantages of immutability which anyone who has heard only a little about FP knows already.
- mattmanser 10y agoHis book on refactoring was one of the most revelatory programming books I've ever read, and was a major influence on how I program. I'm sure if I re-read it today I'd raise a few eyebrows at sections, but it's 17 years old now. I've no idea how good it is now, given how much we've moved on, but it was very good back in the day. He's had some real hits. But I must admit, I find this article a bit silly. He's had some stinkers too. Worse still, this is an obvious problem because the solution's been in other languages for decades, overloading equivalence, it's just not in javascript. It's just not a common programming problem outside of certain domains. And, he knows it. So I really do not understand the point of that post. There was a time when every single one of the "learn .Net 1.0 in 28 days" books would have at least a paragraph about overloading Equals and GetHashCode. This is from 2005, for example, and even uses the same example: https://msdn.microsoft.com/ru-ru/library/ms173147(v=vs.80).aspx https://msdn.microsoft.com/ru-ru/library/ms173147(v=vs.80).a... The blog post seems a decade too late.
- wwwigham 10y agoIMO, The issue with this article is that it conflated a desire for immutable record types (as you'd see in a functional language) with something which requires equivalence overloading in a language which does not support them (as you'd find in a heavy OO language). I think while he presented _A_ solution to the specific issue he encountered (wanting to compare algebraic types as cohesive units in Java), it is not the _best_ general solution to the issue - in fact, it's really not the best solution in any language without built-in record types, as it can lead to costly hidden comparison times and unneeded memory growth (which most design patterns I would say should discourage this kind of hidden performance issue). This is a data structure problem which should be attacked much like how a compiler implementer would attack it - by being lazy and doing at little work as possible. Instead of making similar objects appear equivalent - make identical objects be references to the same object! Since we already decided to let records be immutable, it actually turns out quite nicely in JS, as I have written elsewhere - and I'm confident that the same cached-reference/object interning strategy could be used in Java, too.
- jowiar 10y agoKey points in here: - Thinking differently about Values vs. References (which you may or may not be used to depending on your language) - Mutable "values" are dangerous (and it might be worth thinking about what constitutes a "value") - Translating incoming low-level numbers and strings into proper "values" helps avoid errors.
- swanson 10y agoA concise explanation of a pattern that developers of newly every level can understand and link to? Nope, no value there! /s
- maxxxxx 10y agoIt just seems that this has been discussed to death already. The only new thing is that he calls it "ValueObject". I already dread people talking about "ValueObjects" and I will have no idea what this is. I thought "immutable objects" is the standard term.
- nightski 10y agoIt makes more sense in the context of domain driven design (ddd) but I don't believe he references this at all in the article. But based on his other articles I think that is what he is getting at.
- macspoofing 10y ago>The only new thing is that he calls it "ValueObject". That's not a new name. It's been around forever.
- jdlshore 10y agoThis isn't about immutability, this is about the power of introducing value objects to a system. (Immutability is just a property of good value objects.) Most code I read does not use value objects. It's more common for me to see primitive obsession [1], data clumps [2], arbitrary structs, or the "struct + service" antipattern (looking at you, Angular 1). Granted, I'm not working with FP languages. When you migrate code to use value objects, then follow up by moving associated behavior into the value object, there are significant knock-on benefits to your design. Value objects may be simple, but they're not obvious. If they were, more code would use them. [1] http://www.jamesshore.com/Blog/PrimitiveObsession.html http://www.jamesshore.com/Blog/PrimitiveObsession.html [2] http://www.martinfowler.com/bliki/DataClump.html http://www.martinfowler.com/bliki/DataClump.html
- Silhouette 10y agoValue objects may be simple, but they're not obvious. If they were, more code would use them. To the extent that the concept is relevant, the difference is obvious unless you only ever use languages that are ambiguous about reference/value semantics. If you use a language like C or C++ or even assembly, where pointers and taking an address are explicit and fundamental concepts, there's mostly no problem. Your data is a value by default, and if you want a reference (pointer) instead, you just make one, explicitly. The exception is arrays, and in not entirely unrelated news, array semantics in C and C++ are a significant source of bugs. If you use a functional programming language, again data is typically immutable unless you explicitly use some sort of reference type, so again no problem. I have long maintained that one of the worst of Java's many design flaws was using the exact same syntax to represent two distinct but related concepts, depending on whether you were referring to a primitive or a class-based type. At least with the static typing you can see which you've got, though. I can't fathom why you'd deliberately design a language to work like JS or Python, where not only can the exact same syntax mean two completely different things but which you get depends on dynamic typing information that you can't even see until runtime, or even on the exact values you're using rather than their types, in a case like Python's handling of small integers. I shudder to think how much productivity has been lost by now to bugs caused by mixing up .equals() with == in Java, == with === in JS, and == with `is` in Python.
- 10y ago
- hota_mazi 10y agoAgreed. This post is the kind of centithread we were having on Java mailing-lists fifteen years ago, talking about EJB's, DTO, the dangers of mutability, and the like. It's all established knowledge. Every programmer worth their salt understands the difference between value and reference equality and is proficient in how their language of choice supports these concepts. Also, for perceived thought leaders, all these people have completely failed to update their knowledge with modern technologies and languages. All you see from the Fowler and Uncle Bob of the world is Java and Javascript. How about some Rust, ELM, Kotlin, which encompass the more modern trends of programming and which address the kinds of problems described in this article in one liners? Also, I still chuckle at the fact that Fowler's web site (which he developed himself and called a "bliki", blog + wiki) is still incapable of supporting article titles with spaces in them, hence "ValueObject". That's not exactly a resounding endorsement of software engineering abilities.
- icebraining 10y agoAlso, I still chuckle at the fact that Fowler's web site (which he developed himself and called a "bliki", blog + wiki) is still incapable of supporting article titles with spaces in them, hence "ValueObject". That's not exactly a resounding endorsement of software engineering abilities. http://www.martinfowler.com/bliki/BimodalIT.html http://www.martinfowler.com/bliki/BimodalIT.html
- deleted 10y ago[deleted]
- gutnor 10y agoHe has been doing a bit of summarising both sides of an argument too, more as a sort of not to biased Software Development journalist. That can be useful, maybe not when you are in the thick of it, but for newcomer or people that have missed the great ideological battles of the last 6 months.
- peeters 10y agoSure, I find his articles are often "duh" to an experienced programmer. But on the other hand, it's a great resource to be able to point junior devs to. Instead of say explaining why value objects should usually be immutable, you can give a brief summary and then say "read this". I find it really similar to stuff in Effective Java. Sure there's nothing too revolutionary in that book, but in the aggregate it makes up my best coding practices, and when somebody questions one small piece of those coding practices, I can send them to a brief, to-the-point item in Effective Java.
- nickbauman 10y agoYou would be surprised how many people get ValueObject wrong. Most of the time the wrongness is due to how value objects are implemented as such. The concept of instances that are consistent with equals (immutability) comes later in most people's career. It doesn't help that hugely popular languages like Java seem to push people away from immutability.
- hughw 10y agoAs someone told me in college as we watched some Saturday morning cartoons: They don't need to make new cartoons because they keep making new kids.
- bshimmin 10y agoI felt like this quite strongly about his "Collection Pipeline" article (http://martinfowler.com/articles/collection-pipeline/ http://martinfowler.com/articles/collection-pipeline/) - he's definitely guilty of fancy name syndrome in that one (I think "Value Object" is a fairly well-known term, though - certainly one I was using a decade ago). On the other hand, both this article and the "Collection Pipeline" one contain a lot of useful information, concisely and informatively expressed, which would be beneficial to a junior developer as a general introduction to the subject.
- ronreiter 10y agoReminds me of Python articles from 2005
- wwwigham 10y agoLet me attempt to improve upon Fowler's ValueObject pattern in JS, and tell you the upsides: function Point(x, y) { const hash = Point.__hash(x, y); const cached = Point.__cache.get(hash); if (cached) return cached; const newPoint = {get x() { return x; }, get y() { return y; } }; Point.__cache.set(hash, newPoint); return newPoint; } Point.__hash = function(ptx, pty) { return `${ptx}|${pty}`; } Point.__cache = new WeakMap(); This does what you would expect a ValueObject (or an algebraic type or struct depending on the languages you normally use) to do in another non-JS language - it interns all equivalent objects so that simple reference equality is sufficient to determine equality - rendering the nonstandard "equals" method unneeded. This also solves all the issues with ".includes" and so forth, again, because the built in reference equality is sufficient to determine equality. This also has much better memory characteristics in a system where many equivalent objects are created, as only one copy is ever stored in memory. The only drawback is the small overhead of the WeakMap used to cache all the references and the overhead of "hashing" at object creation time - neither of which should be noticeable in most applications, and the memory benefits should outweigh these concerns in most performance applications regardless.
- phpnode 10y agoUnfortunately your example won't work. You can't use a primitive value as a key in a weak map, which negates many of their possible use cases.
- wwwigham 10y agoYou're right, I hadn't realized that the spec forbade primitives as WeakMap keys - so while to approach should actually work in other languages, in JS it would leak memory as you'd be forced to use a normal map (or clean up after yourself - ich).
- tylorr 10y agoYeah, weak map have weak references to the keys, not the values.
- jstimpfle 10y ago
- deleted 10y ago[deleted]
- kazinator 10y agoTXR Lisp: This is the TXR Lisp interactive listener of TXR 157. Use the :quit command or type Ctrl-D on empty line to exit. 1> (defstruct point nil x y (:method equal (me) (list me.x me.y))) #<struct-type point> 2> (equal (new point x 0 y 0) (new point x 1 y 1)) nil 3> (equal (new point x 0 y 0) (new point x 0 y 0)) t 4> (hash-equal (new point x 0 y 0)) 536869888 5> (hash-equal (new point x 0 y 0)) 536869888 6> (hash-equal (list 0 0)) 536869888 See what I did there? When designing this object system, I realized that rather than a binary equal method which takes this object and that, what we need is a unary equal method. When an object must be compared or hashed for equality under the equal function, and it has the equal method, then that method is called to retrieve a representative object. That object is then used in place of the original object. The method just has to return something which supports equal directly. This feature is called equality substitution. Doc link: http://www.nongnu.org/txr/txr-manpage.html#N-00790C76 http://www.nongnu.org/txr/txr-manpage.html#N-00790C76
- jstimpfle 10y agoThat will be terribly inefficient for asserting inequality of large objects.
- kazinator 10y agoSo: don't write huge objects---it's a code smelly anti-pattern anyway; or don't make every single member of a large object participate in equality; or else, cache the equality substitution and re-compute it if the object is modified. I'm adding a dirty flag to the object system in the next release. If any slot is modified, the object will be marked dirty; an API will be provided for testing and clearing the dirty flag. This will make it a cinch to write the above point class so that it returns the same list if the object is clean. Also, when the object is dirty and the representative list must be recomputed, the cons cells of the old list can be re-used; we don't have to allocate a new list. Most of the time, you don't want that anyway; objects should be immutable as much as possible. If you have objects in a hash table, you don't want to fiddle with them in ways that affect their equality. Speaking of which, this is one of the ways in which the equality substitution is used in real code. Certain slots of the objects are treated as immutable and the equality substitution is based on those slots. The objects are put into hash tables based on that equality. Yet they have other slots treated as mutable. Those don't count toward equality. If they did, the hashing would be in trouble, obviously.
- jstimpfle 10y agoFairly long post with a simple message: Data is the truth, it is useful to think about the values that live in the program, and about their relations. It is useful to think about what things are "objects" and thus need identities. This is not obvious to OOP people. In OOP everything (each object) has implied identity: their address in memory. This is brutally wrong for most use cases in most programming domains. What usecase justifies new Integer(3) != new Integer(3)? Most languages support overriding an equality method as a partial fix, but that's far from elegant. On the other hand it should be fairly obvious to people who like relational databases.
- hughw 10y agoWell put and spot on. The trouble with OO is it's not a data model. Objects aren't rows of a database table. Programmers mutate data fields of objects and enforce the consistency logic manually. As a thought experiment I've considered how you'd write, say, a window system in a logic language like SQL or Prolog. Which pixels are visible and which hidden by other windows? The answer is a join over entities like Window, Pixel, and maybe a ZOrder... something like that, you get the idea. All state transitions bounded by transactions. No way to throw your program into an inconsistent state.
- lacampbell 10y ago> This is not obvious to OOP people. Citation needed!! > In OOP everything (each object) has implied identity: their address in memory. That's not an implied identity at all. That's a default identity. You're meant to override it. What sane default would you propose? Objects are not structs or records. > This is brutally wrong for most use cases in most programming domains. What usecase justifies new Integer(3) != new Integer(3)? What OOP language provides numeric literals that don't have a built in integer class, with an equality method that makes sense!? Complete strawman. > Most languages support overriding an equality method as a partial fix, but that's far from elegant. A partial fix? It is how you solve the problem with objects. I am not sure what other method you would propose.
- jstimpfle 10y agoIt is an identity you can't get rid of, and which is often misused, or accidentally used, to create a lot of complexity. No, you can't override '==' in Java and other OO languages. You can override .equals() which defaults to ==. > A partial fix? It is how you solve the problem with objects. I am not sure what other method you would propose. You mentioned it yourself: structs or records, a.k.a value semantics. Here is a resource from people with more authority (esp. Guy Steele) http://cr.openjdk.java.net/~jrose/values/values-0.html http://cr.openjdk.java.net/~jrose/values/values-0.html
- verytrivial 10y agoI have always used "value" vs "identity" to distinguish these classes. e.g. "Two men called John" (value, equal, no identity) "That man called John, and that man called John." (with identity, so "non-identical", buy may have the same value is some aspect). Every comparison must first be distinguished by whether you are comparing identity or some value of the object. With that distinction made, the arguments about referential integrity (and when it matters) is easier to digest. Pretty obvious I guess, but coming from C++, it is amazing how many people fumble through without having this distinction clear.
- jstimpfle 10y agoI've realized that there's seldom an obvious "identity", because identity is never absolute, but needs context ("semantics"). Mathematically identity is just a function (of some key data to more data) but in practice part of the key is implied (in the program, runtime, database connection, external policies, whatever). For example, conventional URLs are often frowned upon because they don't "exactly" identify a document. Instead URLs involving hashsums, GUIDs etc. are proposed. I think that's a bad idea because while the hash sum is a quite precise identity, it's actually often too precise. I want to be able to edit a web document without having to create a new identity. When someone surfs the URL and gets the latest "version" that's fine. We could consider all versions "identical" for our purposes. Or alternatively we could say that some of the identity was implied in the context, not the URL (the "get the newest" part).
- mbrock 10y agoURLs are like mutable cells in a namespace. Content-based address systems usually have separate solutions for named references, like branches in Git or the various naming schemes for IPFS etc.
- deleted 10y ago[deleted]
- verytrivial 10y agoHmm .. I think you might have that the wrong way around. URLs do exactly identify a document in the sense that the document that is returned is the document in question. That you might get a different document each time is a different matter and relates, again, to value, not identity. (Then there's idempotency which is a useful concept that only appears to causes eyeballs to roll. Even the top in in Google is snarky.) And identity and value are both arbitrary -- John today is John tomorrow, but even that will also cause philosophers to twitch. I guess it not that surprising that programming languages and the like encode the same confusion!
- dmalvarado 10y ago> const p1 = {x: 2, y: 3}; > const p2 = {x: 2, y: 3}; > assert.notEqual(p1,p2); // NOT what I want > Sadly that test passes. It does so because JavaScript tests equality for js objects by looking at their references, ignoring the values they contain. What? Has 'assert' been standardized? Shouldn't it read: "It does so because my assert function tests equality for js objects by looking at their references, ignoring the values they contain."
- mcbits 10y agoI think his point would be the same (and maybe clearer) if he said assert.isFalse(p1 == p2). I.e. he's not concerned about the testing semantics, but JavaScript's equality semantics which we're expected to assume the test function is using.
- deleted 10y ago[deleted]
- edejong 10y agoEquality is an often overlooked complexity in denotational semantics. Dijkstra wrote an EWD [1] on the subject, which should give insight into the difficulties of this presumably simple relation. In most languages, I prefer to think of equality as a property outside of structure or class. Languages such as Java bind equals() strongly with the object, but this is a mistake. First of all, it promotes ordering where there is none. Equality is reflexive and symmetric. Second of all, it does not allow for multiple forms of equality. Lastly, since it is defined in Object, it forces the equality relation to all underlying types, even when there is no equality defined. [1] https://www.cs.utexas.edu/users/EWD/transcriptions/EWD10xx/EWD1073.html https://www.cs.utexas.edu/users/EWD/transcriptions/EWD10xx/E...
- jstimpfle 10y agoMultiple forms of equality: I think there should be just one "equality": the obvious structural one, which can be inferred by a compiler. What examples are there of cases where there is no obvious equality but still the official "equality" tag is needed? EDIT: got downvoted, why? It's a serious question. I've never had any use for any equality besides the structural one. Honestly want to see if anyone can come up with a valid case.
- mcbits 10y agonew Rational(2, 3) == new Rational(4, 6) You could implement Rationals to be structurally equal by simplifying them immediately on construction, but that may be a lot of unnecessary computation in some cases.
- chrismorgan 10y agoI like Rust. `==` is implemented by types as they desire and as makes sense, via the PartialEq trait (and the type can mark whether it’s a partial equality or a total equality by whether Eq is also implemented). Because of Rust’s strong ownership model, the whole referential equality matter actually becomes comparatively irrelevant, though if you actually need it you can cast to raw pointers as compare those (`x as * const _ == y as *const _`). This strong ownership model also means that the whole aliasing problem becomes irrelevant also: it’s obvious from the code where you alias things, and you can only do so in a memory-safe way. Really, this whole article becomes delightfully irrelevant for Rust.
- Traubenfuchs 10y agoPlease avoid hurting Javas reputation by using outdated code. 1. Since Java 8 there are LocalDateTime/ZonedDateTime/etc. classes. They are immutable. LocalDateTime even works with Hibernate. 2. date.setDate(int date) is deprecated for nearly 20 years. The methods Javadoc clearly states that you are changing the object. The method even returns void. Don't use it. Why are you still using it? 3. If you still want to use the less comfortable Date and Calendar, they work fine if you use them as you are supposed to.
- talideon 10y agoHe does actually refer to this, but he could be more explicit on point (2) rather than referring to it obliquely.
- bertan 10y agoOne of the reasons I love Golang is that comparison of structs is among their values, not their references. To do so, compiler complains if you have circular struct type dependencies, but I think it is OK.
- sly010 10y agoOther good examples of value objects are: - EmailAddress - PhoneNumber - FullName - Address Perhaps it's just my domain, but I find myself very often defining equality to various degrees for these concepts. In fact I create value classes for any value that can have multiple valid encodings (e.g. keys and hashes in crypto, dollar value, etc). It makes it very easy to have all encoding/decoding code in one obvious place (in or near the object code). Encoding and decoding can then happen at the edges, and the core of the logic becomes much more readable ... but this gets into the Domain Object territory.