4 ms·
I guess I don’t think that way. Having classes where values are “restricted” doesn’t make many sense in my mind. It’s a flaw in your type system if invalid va
by ninkendo 9y ago
I guess I don’t think that way.
Having classes where values are “restricted” doesn’t make many sense in my mind. It’s a flaw in your type system if invalid values are possible... proper algebraic typing with pattern matching would do an infinitely better job at this.
And linked values are more smell to me... it’s a sign you have a bad abstraction if two properties have to be set in tandem or your object becomes invalid. Can you think of a good example for purposes of discussion?
- acdha 9y agoA somewhat contrived example but say you had a user address with state and zip code values and you wanted to prevent setting a zip code outside of that state. Or with any kind of state change - e.g. certain statuses are only valid for paid accounts, you can only archive verified files, etc. That’s not saying you couldn’t have an API which is set, set, set, validate() but there’s an aesthetic argument for early enforcement so invalid values can never be set. Type systems can’t solve everything but I’d agree that it’s not surprising that you see these design patterns most in languages like Java which didn’t have a better way.
- ninkendo 9y agoI love this response because it's helping me formulate my objections a little better. I don't think a User class should be directly validating the zip lives inside the state in some sort of setter method... which one do I set first to make it work? It looks like once I change one I can't change the other any more... I'd need to have a method that changes both at once. And this is a perfect example of your data not matching your encapsulation: your encapsulation would want to express that you need to provide a zip/state pair via some setLocation(zip, state) method, which does not correspond to any private property you actually have. But I definitely would prefer the set,set,set,validate approach more, and I would even say the validate() belongs in a different class altogether, ideally as a side-effect-free standalone method. (Basically, I should be able to set an invalid state and zip property in a vacuum, it's only when I go to validate it that it returns invalid.) Going further I would implement any form submission system as taking some Validatable interface, where the Validatable object in this case would store your Address object via composition, and would represent the specific logic for validation for these purposes... but it would all live separately from a User struct, which would just be a dumb struct.
- acdha 9y ago> I don't think a User class should be directly validating the zip lives inside the state in some sort of setter method... which one do I set first to make it work? It looks like once I change one I can't change the other any more... I'd need to have a method that changes both at once. The idea I had in mind for that admittedly contrived example was roughly that you'd have an address class and some logic along the lines of setting a field automatically clears the lower-level fields, but that's not really a practical design as much as an illustration of the pattern of having complex logic in a setter. I should note that this isn't my preference, either – either set,set,set,validate or updateLotsOfFields(dataStructure) – but I've seen people who felt otherwise and had reasonable arguments for this kind of behaviour.
- Someone 9y ago”A somewhat contrived example but say you had a user address with state and zip code values and you wanted to prevent setting a zip code outside of that state.” So, you’re thinking of an address class that says “I know what zip codes go with what states” _and_ “if you don’t know what zip codes are valid with what states, I’ll throw an exception”? That’s cruel. Changing a user’s address would be quite the challenge, if (s)he moved to a different state (you have to change the state and the zip code in one go) A better design has some kind of oracle that, given possibly incomplete address info, returns an ‘address’ that you then can atomically set on a user: user.address = streetMap.find(“123 Main Street, USA”)
- acdha 9y agoNote that it was described as “a somewhat contrived example”. I was just trying to get the idea of a relationship where some people would want to prevent you from setting incompatible values and others might have a separate step. I tend to prefer the latter approach but I've worked with people who felt differently for reasons which I'm unwilling to entirely reject. Both can be made to work[1] and there's enough variation between languages, projects, and environments that I think the choice is at least understandable. 1. To answer your last point: something along the lines of obj.setZip() raising an exception but obj.setState() automatically cleared the related value on a change.