Is “avoid the yo-yo problem” a reason to allow the “primitive obsession”?












21















According to When is primitive obsession not a code smell?, I should create a ZipCode object to represent a zip code instead of a String object.



However, in my experience, I prefer to see



public class Address{
public String zipCode;
}


instead of



public class Address{
public ZipCode zipCode;
}


because I think the latter one requires me to move to the ZipCode class to understand the program.



And I believe I need to move between many classes to see the definition if every primitive data fields were replaced by a class, which feels as if suffering from the yo-yo problem (an anti-pattern).



So I would like to move the ZipCode methods into a new class, for example:



Old:



public class ZipCode{
public boolean validate(String zipCode){
}
}


New:



public class ZipCodeHelper{
public static boolean validate(String zipCode){
}
}


so that only the one who needs to validate the zip code would depend on the ZipCodeHelper class. And I found another "benefit" of keeping the primitive obsession: it keeps the class looks like its serialized form, if any, for example: an address table with string column zipCode.



My question is, is "avoiding the yo-yo problem" (move between class definitions) a reason to allow the "primitive obsession"?










share|improve this question




















  • 2





    A nice read concerning adresses: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Make sure you understand what you need and if a zipcode that is part of an address is really what you need and want to model.

    – Polygnome
    yesterday






  • 1





    "I should create a ZipCode object to represent zip code instead of a String object." ...Why? Zip codes would have no additional functionality to attach, and the most common thing you'll do with them is render them as part of a full address. Maybe you'll pass them to a geocoding service, but a custom object makes that harder. The Address class itself can validate (which shouldn't be more than just correct length and optional parts to avoid restricting valid inputs unknown to you at delivery time). I just don't see any practical advantage to having a zip code object; it's just boilerplate.

    – jpmc26
    19 hours ago








  • 4





    @jpmc26 Then you would be shocked to see how complex our zip code object is -- not saying it's right, but it does exist

    – Jared Goguen
    15 hours ago






  • 3





    @jpmc26, I fail to see how you get from "complex" to "badly-designed." Complex code is often the result of simple code coming into contact with the complexity of the real world rather than the ideal world we might wish existed. "Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes."

    – Kyralessa
    9 hours ago






  • 4





    @jpmc26 - the point of wrapping objects like ZipCode is type safety. Zip code is not a string, it's a zip code. If a function expects a zip code, you should only be able to pass a zip code, not a string.

    – Davor Ždralo
    6 hours ago
















21















According to When is primitive obsession not a code smell?, I should create a ZipCode object to represent a zip code instead of a String object.



However, in my experience, I prefer to see



public class Address{
public String zipCode;
}


instead of



public class Address{
public ZipCode zipCode;
}


because I think the latter one requires me to move to the ZipCode class to understand the program.



And I believe I need to move between many classes to see the definition if every primitive data fields were replaced by a class, which feels as if suffering from the yo-yo problem (an anti-pattern).



So I would like to move the ZipCode methods into a new class, for example:



Old:



public class ZipCode{
public boolean validate(String zipCode){
}
}


New:



public class ZipCodeHelper{
public static boolean validate(String zipCode){
}
}


so that only the one who needs to validate the zip code would depend on the ZipCodeHelper class. And I found another "benefit" of keeping the primitive obsession: it keeps the class looks like its serialized form, if any, for example: an address table with string column zipCode.



My question is, is "avoiding the yo-yo problem" (move between class definitions) a reason to allow the "primitive obsession"?










share|improve this question




















  • 2





    A nice read concerning adresses: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Make sure you understand what you need and if a zipcode that is part of an address is really what you need and want to model.

    – Polygnome
    yesterday






  • 1





    "I should create a ZipCode object to represent zip code instead of a String object." ...Why? Zip codes would have no additional functionality to attach, and the most common thing you'll do with them is render them as part of a full address. Maybe you'll pass them to a geocoding service, but a custom object makes that harder. The Address class itself can validate (which shouldn't be more than just correct length and optional parts to avoid restricting valid inputs unknown to you at delivery time). I just don't see any practical advantage to having a zip code object; it's just boilerplate.

    – jpmc26
    19 hours ago








  • 4





    @jpmc26 Then you would be shocked to see how complex our zip code object is -- not saying it's right, but it does exist

    – Jared Goguen
    15 hours ago






  • 3





    @jpmc26, I fail to see how you get from "complex" to "badly-designed." Complex code is often the result of simple code coming into contact with the complexity of the real world rather than the ideal world we might wish existed. "Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes."

    – Kyralessa
    9 hours ago






  • 4





    @jpmc26 - the point of wrapping objects like ZipCode is type safety. Zip code is not a string, it's a zip code. If a function expects a zip code, you should only be able to pass a zip code, not a string.

    – Davor Ždralo
    6 hours ago














21












21








21


7






According to When is primitive obsession not a code smell?, I should create a ZipCode object to represent a zip code instead of a String object.



However, in my experience, I prefer to see



public class Address{
public String zipCode;
}


instead of



public class Address{
public ZipCode zipCode;
}


because I think the latter one requires me to move to the ZipCode class to understand the program.



And I believe I need to move between many classes to see the definition if every primitive data fields were replaced by a class, which feels as if suffering from the yo-yo problem (an anti-pattern).



So I would like to move the ZipCode methods into a new class, for example:



Old:



public class ZipCode{
public boolean validate(String zipCode){
}
}


New:



public class ZipCodeHelper{
public static boolean validate(String zipCode){
}
}


so that only the one who needs to validate the zip code would depend on the ZipCodeHelper class. And I found another "benefit" of keeping the primitive obsession: it keeps the class looks like its serialized form, if any, for example: an address table with string column zipCode.



My question is, is "avoiding the yo-yo problem" (move between class definitions) a reason to allow the "primitive obsession"?










share|improve this question
















According to When is primitive obsession not a code smell?, I should create a ZipCode object to represent a zip code instead of a String object.



However, in my experience, I prefer to see



public class Address{
public String zipCode;
}


instead of



public class Address{
public ZipCode zipCode;
}


because I think the latter one requires me to move to the ZipCode class to understand the program.



And I believe I need to move between many classes to see the definition if every primitive data fields were replaced by a class, which feels as if suffering from the yo-yo problem (an anti-pattern).



So I would like to move the ZipCode methods into a new class, for example:



Old:



public class ZipCode{
public boolean validate(String zipCode){
}
}


New:



public class ZipCodeHelper{
public static boolean validate(String zipCode){
}
}


so that only the one who needs to validate the zip code would depend on the ZipCodeHelper class. And I found another "benefit" of keeping the primitive obsession: it keeps the class looks like its serialized form, if any, for example: an address table with string column zipCode.



My question is, is "avoiding the yo-yo problem" (move between class definitions) a reason to allow the "primitive obsession"?







object-oriented coding-style






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 5 hours ago









Peter Mortensen

1,11521114




1,11521114










asked yesterday









mmmaaammmaaa

2,30931421




2,30931421








  • 2





    A nice read concerning adresses: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Make sure you understand what you need and if a zipcode that is part of an address is really what you need and want to model.

    – Polygnome
    yesterday






  • 1





    "I should create a ZipCode object to represent zip code instead of a String object." ...Why? Zip codes would have no additional functionality to attach, and the most common thing you'll do with them is render them as part of a full address. Maybe you'll pass them to a geocoding service, but a custom object makes that harder. The Address class itself can validate (which shouldn't be more than just correct length and optional parts to avoid restricting valid inputs unknown to you at delivery time). I just don't see any practical advantage to having a zip code object; it's just boilerplate.

    – jpmc26
    19 hours ago








  • 4





    @jpmc26 Then you would be shocked to see how complex our zip code object is -- not saying it's right, but it does exist

    – Jared Goguen
    15 hours ago






  • 3





    @jpmc26, I fail to see how you get from "complex" to "badly-designed." Complex code is often the result of simple code coming into contact with the complexity of the real world rather than the ideal world we might wish existed. "Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes."

    – Kyralessa
    9 hours ago






  • 4





    @jpmc26 - the point of wrapping objects like ZipCode is type safety. Zip code is not a string, it's a zip code. If a function expects a zip code, you should only be able to pass a zip code, not a string.

    – Davor Ždralo
    6 hours ago














  • 2





    A nice read concerning adresses: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Make sure you understand what you need and if a zipcode that is part of an address is really what you need and want to model.

    – Polygnome
    yesterday






  • 1





    "I should create a ZipCode object to represent zip code instead of a String object." ...Why? Zip codes would have no additional functionality to attach, and the most common thing you'll do with them is render them as part of a full address. Maybe you'll pass them to a geocoding service, but a custom object makes that harder. The Address class itself can validate (which shouldn't be more than just correct length and optional parts to avoid restricting valid inputs unknown to you at delivery time). I just don't see any practical advantage to having a zip code object; it's just boilerplate.

    – jpmc26
    19 hours ago








  • 4





    @jpmc26 Then you would be shocked to see how complex our zip code object is -- not saying it's right, but it does exist

    – Jared Goguen
    15 hours ago






  • 3





    @jpmc26, I fail to see how you get from "complex" to "badly-designed." Complex code is often the result of simple code coming into contact with the complexity of the real world rather than the ideal world we might wish existed. "Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes."

    – Kyralessa
    9 hours ago






  • 4





    @jpmc26 - the point of wrapping objects like ZipCode is type safety. Zip code is not a string, it's a zip code. If a function expects a zip code, you should only be able to pass a zip code, not a string.

    – Davor Ždralo
    6 hours ago








2




2





A nice read concerning adresses: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Make sure you understand what you need and if a zipcode that is part of an address is really what you need and want to model.

– Polygnome
yesterday





A nice read concerning adresses: mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses Make sure you understand what you need and if a zipcode that is part of an address is really what you need and want to model.

– Polygnome
yesterday




1




1





"I should create a ZipCode object to represent zip code instead of a String object." ...Why? Zip codes would have no additional functionality to attach, and the most common thing you'll do with them is render them as part of a full address. Maybe you'll pass them to a geocoding service, but a custom object makes that harder. The Address class itself can validate (which shouldn't be more than just correct length and optional parts to avoid restricting valid inputs unknown to you at delivery time). I just don't see any practical advantage to having a zip code object; it's just boilerplate.

– jpmc26
19 hours ago







"I should create a ZipCode object to represent zip code instead of a String object." ...Why? Zip codes would have no additional functionality to attach, and the most common thing you'll do with them is render them as part of a full address. Maybe you'll pass them to a geocoding service, but a custom object makes that harder. The Address class itself can validate (which shouldn't be more than just correct length and optional parts to avoid restricting valid inputs unknown to you at delivery time). I just don't see any practical advantage to having a zip code object; it's just boilerplate.

– jpmc26
19 hours ago






4




4





@jpmc26 Then you would be shocked to see how complex our zip code object is -- not saying it's right, but it does exist

– Jared Goguen
15 hours ago





@jpmc26 Then you would be shocked to see how complex our zip code object is -- not saying it's right, but it does exist

– Jared Goguen
15 hours ago




3




3





@jpmc26, I fail to see how you get from "complex" to "badly-designed." Complex code is often the result of simple code coming into contact with the complexity of the real world rather than the ideal world we might wish existed. "Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes."

– Kyralessa
9 hours ago





@jpmc26, I fail to see how you get from "complex" to "badly-designed." Complex code is often the result of simple code coming into contact with the complexity of the real world rather than the ideal world we might wish existed. "Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes."

– Kyralessa
9 hours ago




4




4





@jpmc26 - the point of wrapping objects like ZipCode is type safety. Zip code is not a string, it's a zip code. If a function expects a zip code, you should only be able to pass a zip code, not a string.

– Davor Ždralo
6 hours ago





@jpmc26 - the point of wrapping objects like ZipCode is type safety. Zip code is not a string, it's a zip code. If a function expects a zip code, you should only be able to pass a zip code, not a string.

– Davor Ždralo
6 hours ago










6 Answers
6






active

oldest

votes


















82














The assumption is that you don't need to yo-yo to the ZipCode class to understand the Address class. If ZipCode is well-designed it should be obvious what it does just by reading the Address class.



Programs are not read end-to-end - typically programs are far too complex to make this possible. You cannot keep all the code in a program in your mind at the same time. So we use abstractions and encapsulations to "chunk" the program into meaningful units, so you can look at one part of the program (say the Address class) without having to read all code it depends on.



For example I'm sure you don't yo-yo into reading the source code for String every time you encounter String in code.



Renaming the class from ZipCode to ZipCodeHelper suggest there now is two separate concepts: a zip code and a zip code helper. So twice as complex. And now the type system cannot help you distinguish between an arbitrary string and a valid zip code since they have the same type. This is where "obsession" is appropriate: You are suggesting a more complex and less safe alternative just because you want to avoid a simple wrapper type around a primitive.



Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. But as soon as you add any logic, it is much simpler if this logic is encapsulated with the type.



As for serialization I think it sounds like a limitation in the framework you are using. Surely you should be able to serialize a ZipCode to a string or map it to a column in a database.






share|improve this answer





















  • 1





    I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

    – R. Schmitz
    yesterday








  • 10





    @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

    – JacquesB
    yesterday











  • I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

    – R. Schmitz
    yesterday






  • 5





    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

    – Matthieu M.
    23 hours ago











  • @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

    – TKK
    23 hours ago





















40














If can do:



new ZipCode("totally invalid zip code");


And the constructor for ZipCode does:



ZipCodeHelper.validate("totally invalid zip code");


Then you've broken encapsulation, and added a pretty silly dependency to the ZipCode class. If the constructor doesn't call ZipCodeHelper.validate(...) then you have isolated logic in its own island without actually enforcing it. You can create invalid zip codes.



The validate method should be a static method on the ZipCode class. Now the knowledge of a "valid" zip code is bundled together with the ZipCode class. Given that your code examples look like Java, the constructor of ZipCode should throw an exception if an incorrect format is given:



public class ZipCode {
private String zipCode;

public ZipCode(string zipCode) {
if (!validate(zipCode))
throw new IllegalFormatException("Invalid zip code");

this.zipCode = zipCode;
}

public static bool validate(String zipCode) {
// logic to check format
}

@Override
public String toString() {
return zipCode;
}
}


The constructor checks the format and throws an exception, thereby preventing invalid zip codes from being created, and the static validate method is available to other code so the logic of checking the format is encapsulated in the ZipCode class.



There is no "yo-yo" in this variant of the ZipCode class. It's just called proper Object Oriented Programming.





We are also going to ignore internationalization here, which may necessitate another class called ZipCodeFormat or PostalService (e.g. PostalService.isValidPostalCode(...), PostalService.parsePostalCode(...), etc.).






share|improve this answer



















  • 13





    Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

    – Some Guy
    23 hours ago











  • @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

    – R. Schmitz
    9 hours ago








  • 1





    @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

    – Greg Burghardt
    7 hours ago






  • 3





    @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

    – Some Guy
    6 hours ago








  • 3





    A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

    – Some Guy
    6 hours ago



















5














If you wrestle a lot with this question, perhaps the language you use is not the right tool for the job?
This kind of "domain-typed primitives" are trivially easy to express in, for example, F#.



There you could, for example, write:



type ZipCode = string
type Town = string

type Adress = {
zipCode: ZipCode
town: Town
//etc
}

let adress1 = {
zipCode = ZipCode "90210"
town = Town "Beverly Hills"
}

let faultyAdress = {
zipCode = "12345" // <-Compiler error
town = adress1.zipCode // <- Compiler error
}


This is really useful for avoiding common mistakes, like comparing id's of different entities. And since these typed primitives are much more lightweight than a C# or Java-class, you'll end up actually use them.






share|improve this answer
























  • Interesting - how would it look like if you wanted to enforce validation of ZipCode?

    – Hulk
    11 hours ago






  • 2





    @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

    – Guran
    11 hours ago



















2














The ZipCode abstraction could only make sense if your Address class did not also have a TownName property. Otherwise, you have half an abstraction: the zip code designates the town, but these two related bits of information are found in different classes. It doesn't quite make sense.



However, even then, it's still not a correct application (or rather solution to) primitive obsession; which, as I understand it, mainly focuses on two things:




  1. Using primitives as the input (or even output) values of a method, especially when a collection of primitives is needed.

  2. Classes that grow extra properties over time without ever reconsidering whether some of these should be grouped into a subclass of their own.


Your case is neither. An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country, ...). There is little to no reason to break up this data as it has a single responsibility: designate a location on Earth. An address requires all of these fields in order to be meaningful. Half an address is pointless.



This is how you know that you don't need to subdivide any further: breaking it down any further would detract from the functional intention of the Address class. Similarly, you don't need a Name subclass to be used in the Personclass, unless Name (without a person attached) is a meaningful concept in your domain. Which it (usually) isn't. Names are used for identifying people, they usually have no value on their own.






share|improve this answer





















  • 1





    @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

    – Flater
    yesterday








  • 4





    "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

    – R. Schmitz
    yesterday








  • 1





    @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

    – Flater
    yesterday








  • 3





    @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

    – R. Schmitz
    yesterday






  • 7





    @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

    – JimmyJames
    yesterday



















1














The answer depends entirely on what you actually want to do with the ZIP codes. Here are two extreme possibilities:



(1) All addresses are guaranteed to be in a single country. No exceptions at all. (E.g. no foreign customers, or no employees whose private address is abroad while they are working for a foreign customer.) This country has ZIP codes and they can be expected to never be seriously problematic (i.e. they don't require free-form input such as "currently D4B 6N2, but this changes every 2 weeks"). The ZIP codes are used not just for addressing, but for validation of payment information or similar purposes. - Under these circumstances, a ZIP code class makes a lot of sense.



(2) Addresses can be in almost every country, so dozens or hundreds of addressing schemes with or without ZIP codes (and with thousands of weird exceptions and special cases) are relevant. A "ZIP" code is really only asked for to remind people from countries where ZIP codes are used not to forget to provide theirs. The addresses are only used so that if someone loses access to their account and they can prove their name and address, access will be restored. - Under these circumstances, ZIP code classes for all relevant countries would be an enormous effort. Fortunately they are not needed at all.






share|improve this answer































    0














    The other answers have talked about OO domain modelling and using a richer type to represent your value.



    I don't disagree, especially given the example code you posted.



    But I also wonder if that actually answers the title of your question.



    Consider the following scenario (pulled from an actual project I'm working on):



    You have a remote application on a field device that talks to your central server. One of the DB fields for the device entry is a zip code for the address that the field device is at. You don't care about the zip code (or any of the rest of the address for that matter). All of the people who care about it are on the other side of an HTTP boundary: you just happen to be the single source of truth for the data. It has no place in your domain modeling. You just record it, validate it, store it, and on request shuffle it off in a JSON blob to points elsewhere.



    In this scenario, doing much of anything beyond validating the insert with an SQL regex constraint (or its ORM equivalent) is probably overkill of the YAGNI variety.






    share|improve this answer



















    • 5





      Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

      – IMSoP
      yesterday











    • @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

      – Jared Smith
      yesterday











    • You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

      – IMSoP
      yesterday











    • You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

      – Miral
      16 hours ago











    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "131"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: false,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f386042%2fis-avoid-the-yo-yo-problem-a-reason-to-allow-the-primitive-obsession%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown




















    StackExchange.ready(function () {
    $("#show-editor-button input, #show-editor-button button").click(function () {
    var showEditor = function() {
    $("#show-editor-button").hide();
    $("#post-form").removeClass("dno");
    StackExchange.editor.finallyInit();
    };

    var useFancy = $(this).data('confirm-use-fancy');
    if(useFancy == 'True') {
    var popupTitle = $(this).data('confirm-fancy-title');
    var popupBody = $(this).data('confirm-fancy-body');
    var popupAccept = $(this).data('confirm-fancy-accept-button');

    $(this).loadPopup({
    url: '/post/self-answer-popup',
    loaded: function(popup) {
    var pTitle = $(popup).find('h2');
    var pBody = $(popup).find('.popup-body');
    var pSubmit = $(popup).find('.popup-submit');

    pTitle.text(popupTitle);
    pBody.html(popupBody);
    pSubmit.val(popupAccept).click(showEditor);
    }
    })
    } else{
    var confirmText = $(this).data('confirm-text');
    if (confirmText ? confirm(confirmText) : true) {
    showEditor();
    }
    }
    });
    });






    6 Answers
    6






    active

    oldest

    votes








    6 Answers
    6






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    82














    The assumption is that you don't need to yo-yo to the ZipCode class to understand the Address class. If ZipCode is well-designed it should be obvious what it does just by reading the Address class.



    Programs are not read end-to-end - typically programs are far too complex to make this possible. You cannot keep all the code in a program in your mind at the same time. So we use abstractions and encapsulations to "chunk" the program into meaningful units, so you can look at one part of the program (say the Address class) without having to read all code it depends on.



    For example I'm sure you don't yo-yo into reading the source code for String every time you encounter String in code.



    Renaming the class from ZipCode to ZipCodeHelper suggest there now is two separate concepts: a zip code and a zip code helper. So twice as complex. And now the type system cannot help you distinguish between an arbitrary string and a valid zip code since they have the same type. This is where "obsession" is appropriate: You are suggesting a more complex and less safe alternative just because you want to avoid a simple wrapper type around a primitive.



    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. But as soon as you add any logic, it is much simpler if this logic is encapsulated with the type.



    As for serialization I think it sounds like a limitation in the framework you are using. Surely you should be able to serialize a ZipCode to a string or map it to a column in a database.






    share|improve this answer





















    • 1





      I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

      – R. Schmitz
      yesterday








    • 10





      @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

      – JacquesB
      yesterday











    • I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

      – R. Schmitz
      yesterday






    • 5





      Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

      – Matthieu M.
      23 hours ago











    • @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

      – TKK
      23 hours ago


















    82














    The assumption is that you don't need to yo-yo to the ZipCode class to understand the Address class. If ZipCode is well-designed it should be obvious what it does just by reading the Address class.



    Programs are not read end-to-end - typically programs are far too complex to make this possible. You cannot keep all the code in a program in your mind at the same time. So we use abstractions and encapsulations to "chunk" the program into meaningful units, so you can look at one part of the program (say the Address class) without having to read all code it depends on.



    For example I'm sure you don't yo-yo into reading the source code for String every time you encounter String in code.



    Renaming the class from ZipCode to ZipCodeHelper suggest there now is two separate concepts: a zip code and a zip code helper. So twice as complex. And now the type system cannot help you distinguish between an arbitrary string and a valid zip code since they have the same type. This is where "obsession" is appropriate: You are suggesting a more complex and less safe alternative just because you want to avoid a simple wrapper type around a primitive.



    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. But as soon as you add any logic, it is much simpler if this logic is encapsulated with the type.



    As for serialization I think it sounds like a limitation in the framework you are using. Surely you should be able to serialize a ZipCode to a string or map it to a column in a database.






    share|improve this answer





















    • 1





      I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

      – R. Schmitz
      yesterday








    • 10





      @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

      – JacquesB
      yesterday











    • I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

      – R. Schmitz
      yesterday






    • 5





      Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

      – Matthieu M.
      23 hours ago











    • @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

      – TKK
      23 hours ago
















    82












    82








    82







    The assumption is that you don't need to yo-yo to the ZipCode class to understand the Address class. If ZipCode is well-designed it should be obvious what it does just by reading the Address class.



    Programs are not read end-to-end - typically programs are far too complex to make this possible. You cannot keep all the code in a program in your mind at the same time. So we use abstractions and encapsulations to "chunk" the program into meaningful units, so you can look at one part of the program (say the Address class) without having to read all code it depends on.



    For example I'm sure you don't yo-yo into reading the source code for String every time you encounter String in code.



    Renaming the class from ZipCode to ZipCodeHelper suggest there now is two separate concepts: a zip code and a zip code helper. So twice as complex. And now the type system cannot help you distinguish between an arbitrary string and a valid zip code since they have the same type. This is where "obsession" is appropriate: You are suggesting a more complex and less safe alternative just because you want to avoid a simple wrapper type around a primitive.



    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. But as soon as you add any logic, it is much simpler if this logic is encapsulated with the type.



    As for serialization I think it sounds like a limitation in the framework you are using. Surely you should be able to serialize a ZipCode to a string or map it to a column in a database.






    share|improve this answer















    The assumption is that you don't need to yo-yo to the ZipCode class to understand the Address class. If ZipCode is well-designed it should be obvious what it does just by reading the Address class.



    Programs are not read end-to-end - typically programs are far too complex to make this possible. You cannot keep all the code in a program in your mind at the same time. So we use abstractions and encapsulations to "chunk" the program into meaningful units, so you can look at one part of the program (say the Address class) without having to read all code it depends on.



    For example I'm sure you don't yo-yo into reading the source code for String every time you encounter String in code.



    Renaming the class from ZipCode to ZipCodeHelper suggest there now is two separate concepts: a zip code and a zip code helper. So twice as complex. And now the type system cannot help you distinguish between an arbitrary string and a valid zip code since they have the same type. This is where "obsession" is appropriate: You are suggesting a more complex and less safe alternative just because you want to avoid a simple wrapper type around a primitive.



    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. But as soon as you add any logic, it is much simpler if this logic is encapsulated with the type.



    As for serialization I think it sounds like a limitation in the framework you are using. Surely you should be able to serialize a ZipCode to a string or map it to a column in a database.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited yesterday









    Rik D

    16115




    16115










    answered yesterday









    JacquesBJacquesB

    42.3k1688124




    42.3k1688124








    • 1





      I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

      – R. Schmitz
      yesterday








    • 10





      @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

      – JacquesB
      yesterday











    • I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

      – R. Schmitz
      yesterday






    • 5





      Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

      – Matthieu M.
      23 hours ago











    • @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

      – TKK
      23 hours ago
















    • 1





      I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

      – R. Schmitz
      yesterday








    • 10





      @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

      – JacquesB
      yesterday











    • I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

      – R. Schmitz
      yesterday






    • 5





      Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

      – Matthieu M.
      23 hours ago











    • @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

      – TKK
      23 hours ago










    1




    1





    I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

    – R. Schmitz
    yesterday







    I agree with the "meaningful units" (main-) part, but not so much that a zip code and zip code validation are the same concept. ZipCodeHelper (which I would rather call ZipCodeValidator) might very well establish a connection to a web service to do it's job. That would not be part of the single responsibility "hold the zip code data". Making the type system disallow invalid zip codes can still be achieved by making the ZipCode constructor the equivalent of Java's package-private and calling that with a ZipCodeFactory which always calls the validator.

    – R. Schmitz
    yesterday






    10




    10





    @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

    – JacquesB
    yesterday





    @R.Schmitz: That is not what "responsibility" means in the sense of the single responsibility principle. But in any case, you should of course use as many classes as you need as long as you encapsulate the zip code and its validation. The OP suggest a helper instead of encapsulating the zip code, which is a bad idea.

    – JacquesB
    yesterday













    I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

    – R. Schmitz
    yesterday





    I want to respectfully disagree. SRP means a class should have "one, and only one, reason to be changed" (change in "what a zipcode consists of" vs. "how it is validated"). This specific case here is further elaborated on in the book Clean Code: "Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions." - ZipCode would be a "data structure" and ZipCodeHelper an "object' . In any case, I think we agree that we shouldn't have to pass web connections to the ZipCode constructor.

    – R. Schmitz
    yesterday




    5




    5





    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

    – Matthieu M.
    23 hours ago





    Using a primitive is IMHO justified in the cases where there is no validation or other logic depending on this particular type. => I disagree. Even if all values are valid, I would still favor conveying the semantics to the language rather than use primitives. If a function can be called on a primitive type which is nonsensical for its current semantic usage, then it should not be a primitive type, it should be a proper type with only the sensible functions defined. (As an example, using int as ID allows multiplying an ID by an ID...)

    – Matthieu M.
    23 hours ago













    @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

    – TKK
    23 hours ago







    @R.Schmitz I think ZIP codes are a poor example for the distinction you're making. Something that changes often might be a candidate for separate Foo and FooValidator classes. We could have a ZipCode class that validates the format and a ZipCodeValidator that hits some Web service to check that a correctly formatted ZipCode is actually current. We know that ZIP codes change. But practically, we're going to have a list of valid ZIP codes encapsulated in ZipCode, or in some local database.

    – TKK
    23 hours ago















    40














    If can do:



    new ZipCode("totally invalid zip code");


    And the constructor for ZipCode does:



    ZipCodeHelper.validate("totally invalid zip code");


    Then you've broken encapsulation, and added a pretty silly dependency to the ZipCode class. If the constructor doesn't call ZipCodeHelper.validate(...) then you have isolated logic in its own island without actually enforcing it. You can create invalid zip codes.



    The validate method should be a static method on the ZipCode class. Now the knowledge of a "valid" zip code is bundled together with the ZipCode class. Given that your code examples look like Java, the constructor of ZipCode should throw an exception if an incorrect format is given:



    public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
    if (!validate(zipCode))
    throw new IllegalFormatException("Invalid zip code");

    this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
    // logic to check format
    }

    @Override
    public String toString() {
    return zipCode;
    }
    }


    The constructor checks the format and throws an exception, thereby preventing invalid zip codes from being created, and the static validate method is available to other code so the logic of checking the format is encapsulated in the ZipCode class.



    There is no "yo-yo" in this variant of the ZipCode class. It's just called proper Object Oriented Programming.





    We are also going to ignore internationalization here, which may necessitate another class called ZipCodeFormat or PostalService (e.g. PostalService.isValidPostalCode(...), PostalService.parsePostalCode(...), etc.).






    share|improve this answer



















    • 13





      Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

      – Some Guy
      23 hours ago











    • @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

      – R. Schmitz
      9 hours ago








    • 1





      @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

      – Greg Burghardt
      7 hours ago






    • 3





      @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

      – Some Guy
      6 hours ago








    • 3





      A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

      – Some Guy
      6 hours ago
















    40














    If can do:



    new ZipCode("totally invalid zip code");


    And the constructor for ZipCode does:



    ZipCodeHelper.validate("totally invalid zip code");


    Then you've broken encapsulation, and added a pretty silly dependency to the ZipCode class. If the constructor doesn't call ZipCodeHelper.validate(...) then you have isolated logic in its own island without actually enforcing it. You can create invalid zip codes.



    The validate method should be a static method on the ZipCode class. Now the knowledge of a "valid" zip code is bundled together with the ZipCode class. Given that your code examples look like Java, the constructor of ZipCode should throw an exception if an incorrect format is given:



    public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
    if (!validate(zipCode))
    throw new IllegalFormatException("Invalid zip code");

    this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
    // logic to check format
    }

    @Override
    public String toString() {
    return zipCode;
    }
    }


    The constructor checks the format and throws an exception, thereby preventing invalid zip codes from being created, and the static validate method is available to other code so the logic of checking the format is encapsulated in the ZipCode class.



    There is no "yo-yo" in this variant of the ZipCode class. It's just called proper Object Oriented Programming.





    We are also going to ignore internationalization here, which may necessitate another class called ZipCodeFormat or PostalService (e.g. PostalService.isValidPostalCode(...), PostalService.parsePostalCode(...), etc.).






    share|improve this answer



















    • 13





      Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

      – Some Guy
      23 hours ago











    • @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

      – R. Schmitz
      9 hours ago








    • 1





      @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

      – Greg Burghardt
      7 hours ago






    • 3





      @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

      – Some Guy
      6 hours ago








    • 3





      A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

      – Some Guy
      6 hours ago














    40












    40








    40







    If can do:



    new ZipCode("totally invalid zip code");


    And the constructor for ZipCode does:



    ZipCodeHelper.validate("totally invalid zip code");


    Then you've broken encapsulation, and added a pretty silly dependency to the ZipCode class. If the constructor doesn't call ZipCodeHelper.validate(...) then you have isolated logic in its own island without actually enforcing it. You can create invalid zip codes.



    The validate method should be a static method on the ZipCode class. Now the knowledge of a "valid" zip code is bundled together with the ZipCode class. Given that your code examples look like Java, the constructor of ZipCode should throw an exception if an incorrect format is given:



    public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
    if (!validate(zipCode))
    throw new IllegalFormatException("Invalid zip code");

    this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
    // logic to check format
    }

    @Override
    public String toString() {
    return zipCode;
    }
    }


    The constructor checks the format and throws an exception, thereby preventing invalid zip codes from being created, and the static validate method is available to other code so the logic of checking the format is encapsulated in the ZipCode class.



    There is no "yo-yo" in this variant of the ZipCode class. It's just called proper Object Oriented Programming.





    We are also going to ignore internationalization here, which may necessitate another class called ZipCodeFormat or PostalService (e.g. PostalService.isValidPostalCode(...), PostalService.parsePostalCode(...), etc.).






    share|improve this answer













    If can do:



    new ZipCode("totally invalid zip code");


    And the constructor for ZipCode does:



    ZipCodeHelper.validate("totally invalid zip code");


    Then you've broken encapsulation, and added a pretty silly dependency to the ZipCode class. If the constructor doesn't call ZipCodeHelper.validate(...) then you have isolated logic in its own island without actually enforcing it. You can create invalid zip codes.



    The validate method should be a static method on the ZipCode class. Now the knowledge of a "valid" zip code is bundled together with the ZipCode class. Given that your code examples look like Java, the constructor of ZipCode should throw an exception if an incorrect format is given:



    public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
    if (!validate(zipCode))
    throw new IllegalFormatException("Invalid zip code");

    this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
    // logic to check format
    }

    @Override
    public String toString() {
    return zipCode;
    }
    }


    The constructor checks the format and throws an exception, thereby preventing invalid zip codes from being created, and the static validate method is available to other code so the logic of checking the format is encapsulated in the ZipCode class.



    There is no "yo-yo" in this variant of the ZipCode class. It's just called proper Object Oriented Programming.





    We are also going to ignore internationalization here, which may necessitate another class called ZipCodeFormat or PostalService (e.g. PostalService.isValidPostalCode(...), PostalService.parsePostalCode(...), etc.).







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered yesterday









    Greg BurghardtGreg Burghardt

    12.7k42957




    12.7k42957








    • 13





      Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

      – Some Guy
      23 hours ago











    • @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

      – R. Schmitz
      9 hours ago








    • 1





      @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

      – Greg Burghardt
      7 hours ago






    • 3





      @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

      – Some Guy
      6 hours ago








    • 3





      A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

      – Some Guy
      6 hours ago














    • 13





      Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

      – Some Guy
      23 hours ago











    • @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

      – R. Schmitz
      9 hours ago








    • 1





      @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

      – Greg Burghardt
      7 hours ago






    • 3





      @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

      – Some Guy
      6 hours ago








    • 3





      A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

      – Some Guy
      6 hours ago








    13




    13





    Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

    – Some Guy
    23 hours ago





    Note: The main advantage with @Greg Burkhardt's approach here is that if someone gives you a ZipCode object, you can trust that it contains a valid string without having to check it again, since its type and the fact that it was successfully constructed gives you that guarantee. If you instead passed strings around, you might feel a need to "assert validate(zipCode)" at various places in your code just to be sure that you had a valid zip code, but with a successfully constructed ZipCode object, you can trust that its contents are valid without having to check them again.

    – Some Guy
    23 hours ago













    @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

    – R. Schmitz
    9 hours ago







    @SomeGuy While the disadvantage is that you're having a "vexing" exception here. Either you surround every ZipCode creation with a try-catch, or you always check it twice. However, as long as your validation code is minor, this should pose no issue for internal, embedded, game and throwaway apps.

    – R. Schmitz
    9 hours ago






    1




    1





    @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

    – Greg Burghardt
    7 hours ago





    @R.Schmitz: The ZipCode.validate method is the pre-check that can be performed before invoking a constructor that throws an exception.

    – Greg Burghardt
    7 hours ago




    3




    3





    @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

    – Some Guy
    6 hours ago







    @R.Schmitz: If you are concerned about a vexing exception, an alternate approach to construction is to make the ZipCode constructor private, and provide a public static factory function (Zipcode.create?) that performs validation of the passed-in parameters, returns null if unsuccessful, and otherwise constructs a ZipCode object and returns it. The caller will always have to check for a null return value, of course. On the other hand, if you are in the habit, for instance, of always validating (regex? validate? etc.) before constructing a ZipCode, the exception may not be so vexing in practice.

    – Some Guy
    6 hours ago






    3




    3





    A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

    – Some Guy
    6 hours ago





    A factory function that returns an Optional<ZipCode> is also a possibility. Then the caller has no choice but to explicitly handle possible failure of the factory function. Regardless, in either case, the error will be discovered somewhere near where it was created rather than possibly much later, by client code far from the original problem.

    – Some Guy
    6 hours ago











    5














    If you wrestle a lot with this question, perhaps the language you use is not the right tool for the job?
    This kind of "domain-typed primitives" are trivially easy to express in, for example, F#.



    There you could, for example, write:



    type ZipCode = string
    type Town = string

    type Adress = {
    zipCode: ZipCode
    town: Town
    //etc
    }

    let adress1 = {
    zipCode = ZipCode "90210"
    town = Town "Beverly Hills"
    }

    let faultyAdress = {
    zipCode = "12345" // <-Compiler error
    town = adress1.zipCode // <- Compiler error
    }


    This is really useful for avoiding common mistakes, like comparing id's of different entities. And since these typed primitives are much more lightweight than a C# or Java-class, you'll end up actually use them.






    share|improve this answer
























    • Interesting - how would it look like if you wanted to enforce validation of ZipCode?

      – Hulk
      11 hours ago






    • 2





      @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

      – Guran
      11 hours ago
















    5














    If you wrestle a lot with this question, perhaps the language you use is not the right tool for the job?
    This kind of "domain-typed primitives" are trivially easy to express in, for example, F#.



    There you could, for example, write:



    type ZipCode = string
    type Town = string

    type Adress = {
    zipCode: ZipCode
    town: Town
    //etc
    }

    let adress1 = {
    zipCode = ZipCode "90210"
    town = Town "Beverly Hills"
    }

    let faultyAdress = {
    zipCode = "12345" // <-Compiler error
    town = adress1.zipCode // <- Compiler error
    }


    This is really useful for avoiding common mistakes, like comparing id's of different entities. And since these typed primitives are much more lightweight than a C# or Java-class, you'll end up actually use them.






    share|improve this answer
























    • Interesting - how would it look like if you wanted to enforce validation of ZipCode?

      – Hulk
      11 hours ago






    • 2





      @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

      – Guran
      11 hours ago














    5












    5








    5







    If you wrestle a lot with this question, perhaps the language you use is not the right tool for the job?
    This kind of "domain-typed primitives" are trivially easy to express in, for example, F#.



    There you could, for example, write:



    type ZipCode = string
    type Town = string

    type Adress = {
    zipCode: ZipCode
    town: Town
    //etc
    }

    let adress1 = {
    zipCode = ZipCode "90210"
    town = Town "Beverly Hills"
    }

    let faultyAdress = {
    zipCode = "12345" // <-Compiler error
    town = adress1.zipCode // <- Compiler error
    }


    This is really useful for avoiding common mistakes, like comparing id's of different entities. And since these typed primitives are much more lightweight than a C# or Java-class, you'll end up actually use them.






    share|improve this answer













    If you wrestle a lot with this question, perhaps the language you use is not the right tool for the job?
    This kind of "domain-typed primitives" are trivially easy to express in, for example, F#.



    There you could, for example, write:



    type ZipCode = string
    type Town = string

    type Adress = {
    zipCode: ZipCode
    town: Town
    //etc
    }

    let adress1 = {
    zipCode = ZipCode "90210"
    town = Town "Beverly Hills"
    }

    let faultyAdress = {
    zipCode = "12345" // <-Compiler error
    town = adress1.zipCode // <- Compiler error
    }


    This is really useful for avoiding common mistakes, like comparing id's of different entities. And since these typed primitives are much more lightweight than a C# or Java-class, you'll end up actually use them.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered 11 hours ago









    GuranGuran

    1866




    1866













    • Interesting - how would it look like if you wanted to enforce validation of ZipCode?

      – Hulk
      11 hours ago






    • 2





      @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

      – Guran
      11 hours ago



















    • Interesting - how would it look like if you wanted to enforce validation of ZipCode?

      – Hulk
      11 hours ago






    • 2





      @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

      – Guran
      11 hours ago

















    Interesting - how would it look like if you wanted to enforce validation of ZipCode?

    – Hulk
    11 hours ago





    Interesting - how would it look like if you wanted to enforce validation of ZipCode?

    – Hulk
    11 hours ago




    2




    2





    @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

    – Guran
    11 hours ago





    @Hulk You can write OO-style in F# and make the types into classes. However, I prefer functional style, declaring the type with type ZipCode = private ZipCode of string and adding a ZipCode module with a create function. There are some examples here: gist.github.com/swlaschin/54cfff886669ccab895a

    – Guran
    11 hours ago











    2














    The ZipCode abstraction could only make sense if your Address class did not also have a TownName property. Otherwise, you have half an abstraction: the zip code designates the town, but these two related bits of information are found in different classes. It doesn't quite make sense.



    However, even then, it's still not a correct application (or rather solution to) primitive obsession; which, as I understand it, mainly focuses on two things:




    1. Using primitives as the input (or even output) values of a method, especially when a collection of primitives is needed.

    2. Classes that grow extra properties over time without ever reconsidering whether some of these should be grouped into a subclass of their own.


    Your case is neither. An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country, ...). There is little to no reason to break up this data as it has a single responsibility: designate a location on Earth. An address requires all of these fields in order to be meaningful. Half an address is pointless.



    This is how you know that you don't need to subdivide any further: breaking it down any further would detract from the functional intention of the Address class. Similarly, you don't need a Name subclass to be used in the Personclass, unless Name (without a person attached) is a meaningful concept in your domain. Which it (usually) isn't. Names are used for identifying people, they usually have no value on their own.






    share|improve this answer





















    • 1





      @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

      – Flater
      yesterday








    • 4





      "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

      – R. Schmitz
      yesterday








    • 1





      @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

      – Flater
      yesterday








    • 3





      @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

      – R. Schmitz
      yesterday






    • 7





      @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

      – JimmyJames
      yesterday
















    2














    The ZipCode abstraction could only make sense if your Address class did not also have a TownName property. Otherwise, you have half an abstraction: the zip code designates the town, but these two related bits of information are found in different classes. It doesn't quite make sense.



    However, even then, it's still not a correct application (or rather solution to) primitive obsession; which, as I understand it, mainly focuses on two things:




    1. Using primitives as the input (or even output) values of a method, especially when a collection of primitives is needed.

    2. Classes that grow extra properties over time without ever reconsidering whether some of these should be grouped into a subclass of their own.


    Your case is neither. An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country, ...). There is little to no reason to break up this data as it has a single responsibility: designate a location on Earth. An address requires all of these fields in order to be meaningful. Half an address is pointless.



    This is how you know that you don't need to subdivide any further: breaking it down any further would detract from the functional intention of the Address class. Similarly, you don't need a Name subclass to be used in the Personclass, unless Name (without a person attached) is a meaningful concept in your domain. Which it (usually) isn't. Names are used for identifying people, they usually have no value on their own.






    share|improve this answer





















    • 1





      @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

      – Flater
      yesterday








    • 4





      "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

      – R. Schmitz
      yesterday








    • 1





      @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

      – Flater
      yesterday








    • 3





      @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

      – R. Schmitz
      yesterday






    • 7





      @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

      – JimmyJames
      yesterday














    2












    2








    2







    The ZipCode abstraction could only make sense if your Address class did not also have a TownName property. Otherwise, you have half an abstraction: the zip code designates the town, but these two related bits of information are found in different classes. It doesn't quite make sense.



    However, even then, it's still not a correct application (or rather solution to) primitive obsession; which, as I understand it, mainly focuses on two things:




    1. Using primitives as the input (or even output) values of a method, especially when a collection of primitives is needed.

    2. Classes that grow extra properties over time without ever reconsidering whether some of these should be grouped into a subclass of their own.


    Your case is neither. An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country, ...). There is little to no reason to break up this data as it has a single responsibility: designate a location on Earth. An address requires all of these fields in order to be meaningful. Half an address is pointless.



    This is how you know that you don't need to subdivide any further: breaking it down any further would detract from the functional intention of the Address class. Similarly, you don't need a Name subclass to be used in the Personclass, unless Name (without a person attached) is a meaningful concept in your domain. Which it (usually) isn't. Names are used for identifying people, they usually have no value on their own.






    share|improve this answer















    The ZipCode abstraction could only make sense if your Address class did not also have a TownName property. Otherwise, you have half an abstraction: the zip code designates the town, but these two related bits of information are found in different classes. It doesn't quite make sense.



    However, even then, it's still not a correct application (or rather solution to) primitive obsession; which, as I understand it, mainly focuses on two things:




    1. Using primitives as the input (or even output) values of a method, especially when a collection of primitives is needed.

    2. Classes that grow extra properties over time without ever reconsidering whether some of these should be grouped into a subclass of their own.


    Your case is neither. An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country, ...). There is little to no reason to break up this data as it has a single responsibility: designate a location on Earth. An address requires all of these fields in order to be meaningful. Half an address is pointless.



    This is how you know that you don't need to subdivide any further: breaking it down any further would detract from the functional intention of the Address class. Similarly, you don't need a Name subclass to be used in the Personclass, unless Name (without a person attached) is a meaningful concept in your domain. Which it (usually) isn't. Names are used for identifying people, they usually have no value on their own.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited yesterday

























    answered yesterday









    FlaterFlater

    6,97621323




    6,97621323








    • 1





      @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

      – Flater
      yesterday








    • 4





      "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

      – R. Schmitz
      yesterday








    • 1





      @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

      – Flater
      yesterday








    • 3





      @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

      – R. Schmitz
      yesterday






    • 7





      @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

      – JimmyJames
      yesterday














    • 1





      @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

      – Flater
      yesterday








    • 4





      "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

      – R. Schmitz
      yesterday








    • 1





      @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

      – Flater
      yesterday








    • 3





      @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

      – R. Schmitz
      yesterday






    • 7





      @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

      – JimmyJames
      yesterday








    1




    1





    @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

    – Flater
    yesterday







    @RikD: From the answer: "you don't need a Name subclass to be used in the Person class, unless Name (without a person attached) is a meaningful concept in your domain." When you have custom validation for the names, the name has then become a meaningful concept in your domain; which I explicitly mentioned as a valid use case for using a subtype. Secondly, for the zipcode validation, you're introducing additional assumptions, such as zip codes needing to follow a given country's format. You're broaching a topic that's much broader than the intent of OP's question.

    – Flater
    yesterday






    4




    4





    "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

    – R. Schmitz
    yesterday







    "An address is a well-defined concept with clearly necessary properties (street, number, zip, town, state, country)." - Well that's just plain wrong. For a good way to deal with this, look at Amazon's address form.

    – R. Schmitz
    yesterday






    1




    1





    @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

    – Flater
    yesterday







    @R.Schmitz The format of an address is different from the properties it has. Yes, culturally it can be formatted different ways, but the property declaration is not responsible for making that decision. It only defines which fields are possible. My answere did not address address validation, which your linked page is mostly about (that and formats, also not my focus). Additionally, not every culture has these wildly varying address formats (Belgium and the Netherlands have a rather fixed format for all listed cases). Additionally, not every application is built to be used internationally.

    – Flater
    yesterday






    3




    3





    @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

    – R. Schmitz
    yesterday





    @Flater Well I won't blame you for not reading the full list of falsehoods, because it's quite long, but it literally contains "Addresses will have a street", "An address require both a city and a country", "An address will have a postcode" etc., which is contrary to what the quoted sentence says.

    – R. Schmitz
    yesterday




    7




    7





    @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

    – JimmyJames
    yesterday





    @GregBurghardt "Zip code assumes United States Postal Service, and you can derive the name of the town from the zip code. Cities can have multiple zip codes, but each zip code is only tied to 1 city." This is not correct in general. I have a zipcode that is used mainly for a neighboring city but my residence is not located there. Zipcodes do not always align with governmental boundaries. For example 42223 contains counties from both TN and KY.

    – JimmyJames
    yesterday











    1














    The answer depends entirely on what you actually want to do with the ZIP codes. Here are two extreme possibilities:



    (1) All addresses are guaranteed to be in a single country. No exceptions at all. (E.g. no foreign customers, or no employees whose private address is abroad while they are working for a foreign customer.) This country has ZIP codes and they can be expected to never be seriously problematic (i.e. they don't require free-form input such as "currently D4B 6N2, but this changes every 2 weeks"). The ZIP codes are used not just for addressing, but for validation of payment information or similar purposes. - Under these circumstances, a ZIP code class makes a lot of sense.



    (2) Addresses can be in almost every country, so dozens or hundreds of addressing schemes with or without ZIP codes (and with thousands of weird exceptions and special cases) are relevant. A "ZIP" code is really only asked for to remind people from countries where ZIP codes are used not to forget to provide theirs. The addresses are only used so that if someone loses access to their account and they can prove their name and address, access will be restored. - Under these circumstances, ZIP code classes for all relevant countries would be an enormous effort. Fortunately they are not needed at all.






    share|improve this answer




























      1














      The answer depends entirely on what you actually want to do with the ZIP codes. Here are two extreme possibilities:



      (1) All addresses are guaranteed to be in a single country. No exceptions at all. (E.g. no foreign customers, or no employees whose private address is abroad while they are working for a foreign customer.) This country has ZIP codes and they can be expected to never be seriously problematic (i.e. they don't require free-form input such as "currently D4B 6N2, but this changes every 2 weeks"). The ZIP codes are used not just for addressing, but for validation of payment information or similar purposes. - Under these circumstances, a ZIP code class makes a lot of sense.



      (2) Addresses can be in almost every country, so dozens or hundreds of addressing schemes with or without ZIP codes (and with thousands of weird exceptions and special cases) are relevant. A "ZIP" code is really only asked for to remind people from countries where ZIP codes are used not to forget to provide theirs. The addresses are only used so that if someone loses access to their account and they can prove their name and address, access will be restored. - Under these circumstances, ZIP code classes for all relevant countries would be an enormous effort. Fortunately they are not needed at all.






      share|improve this answer


























        1












        1








        1







        The answer depends entirely on what you actually want to do with the ZIP codes. Here are two extreme possibilities:



        (1) All addresses are guaranteed to be in a single country. No exceptions at all. (E.g. no foreign customers, or no employees whose private address is abroad while they are working for a foreign customer.) This country has ZIP codes and they can be expected to never be seriously problematic (i.e. they don't require free-form input such as "currently D4B 6N2, but this changes every 2 weeks"). The ZIP codes are used not just for addressing, but for validation of payment information or similar purposes. - Under these circumstances, a ZIP code class makes a lot of sense.



        (2) Addresses can be in almost every country, so dozens or hundreds of addressing schemes with or without ZIP codes (and with thousands of weird exceptions and special cases) are relevant. A "ZIP" code is really only asked for to remind people from countries where ZIP codes are used not to forget to provide theirs. The addresses are only used so that if someone loses access to their account and they can prove their name and address, access will be restored. - Under these circumstances, ZIP code classes for all relevant countries would be an enormous effort. Fortunately they are not needed at all.






        share|improve this answer













        The answer depends entirely on what you actually want to do with the ZIP codes. Here are two extreme possibilities:



        (1) All addresses are guaranteed to be in a single country. No exceptions at all. (E.g. no foreign customers, or no employees whose private address is abroad while they are working for a foreign customer.) This country has ZIP codes and they can be expected to never be seriously problematic (i.e. they don't require free-form input such as "currently D4B 6N2, but this changes every 2 weeks"). The ZIP codes are used not just for addressing, but for validation of payment information or similar purposes. - Under these circumstances, a ZIP code class makes a lot of sense.



        (2) Addresses can be in almost every country, so dozens or hundreds of addressing schemes with or without ZIP codes (and with thousands of weird exceptions and special cases) are relevant. A "ZIP" code is really only asked for to remind people from countries where ZIP codes are used not to forget to provide theirs. The addresses are only used so that if someone loses access to their account and they can prove their name and address, access will be restored. - Under these circumstances, ZIP code classes for all relevant countries would be an enormous effort. Fortunately they are not needed at all.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 1 hour ago









        Hans AdlerHans Adler

        18715




        18715























            0














            The other answers have talked about OO domain modelling and using a richer type to represent your value.



            I don't disagree, especially given the example code you posted.



            But I also wonder if that actually answers the title of your question.



            Consider the following scenario (pulled from an actual project I'm working on):



            You have a remote application on a field device that talks to your central server. One of the DB fields for the device entry is a zip code for the address that the field device is at. You don't care about the zip code (or any of the rest of the address for that matter). All of the people who care about it are on the other side of an HTTP boundary: you just happen to be the single source of truth for the data. It has no place in your domain modeling. You just record it, validate it, store it, and on request shuffle it off in a JSON blob to points elsewhere.



            In this scenario, doing much of anything beyond validating the insert with an SQL regex constraint (or its ORM equivalent) is probably overkill of the YAGNI variety.






            share|improve this answer



















            • 5





              Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

              – IMSoP
              yesterday











            • @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

              – Jared Smith
              yesterday











            • You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

              – IMSoP
              yesterday











            • You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

              – Miral
              16 hours ago
















            0














            The other answers have talked about OO domain modelling and using a richer type to represent your value.



            I don't disagree, especially given the example code you posted.



            But I also wonder if that actually answers the title of your question.



            Consider the following scenario (pulled from an actual project I'm working on):



            You have a remote application on a field device that talks to your central server. One of the DB fields for the device entry is a zip code for the address that the field device is at. You don't care about the zip code (or any of the rest of the address for that matter). All of the people who care about it are on the other side of an HTTP boundary: you just happen to be the single source of truth for the data. It has no place in your domain modeling. You just record it, validate it, store it, and on request shuffle it off in a JSON blob to points elsewhere.



            In this scenario, doing much of anything beyond validating the insert with an SQL regex constraint (or its ORM equivalent) is probably overkill of the YAGNI variety.






            share|improve this answer



















            • 5





              Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

              – IMSoP
              yesterday











            • @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

              – Jared Smith
              yesterday











            • You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

              – IMSoP
              yesterday











            • You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

              – Miral
              16 hours ago














            0












            0








            0







            The other answers have talked about OO domain modelling and using a richer type to represent your value.



            I don't disagree, especially given the example code you posted.



            But I also wonder if that actually answers the title of your question.



            Consider the following scenario (pulled from an actual project I'm working on):



            You have a remote application on a field device that talks to your central server. One of the DB fields for the device entry is a zip code for the address that the field device is at. You don't care about the zip code (or any of the rest of the address for that matter). All of the people who care about it are on the other side of an HTTP boundary: you just happen to be the single source of truth for the data. It has no place in your domain modeling. You just record it, validate it, store it, and on request shuffle it off in a JSON blob to points elsewhere.



            In this scenario, doing much of anything beyond validating the insert with an SQL regex constraint (or its ORM equivalent) is probably overkill of the YAGNI variety.






            share|improve this answer













            The other answers have talked about OO domain modelling and using a richer type to represent your value.



            I don't disagree, especially given the example code you posted.



            But I also wonder if that actually answers the title of your question.



            Consider the following scenario (pulled from an actual project I'm working on):



            You have a remote application on a field device that talks to your central server. One of the DB fields for the device entry is a zip code for the address that the field device is at. You don't care about the zip code (or any of the rest of the address for that matter). All of the people who care about it are on the other side of an HTTP boundary: you just happen to be the single source of truth for the data. It has no place in your domain modeling. You just record it, validate it, store it, and on request shuffle it off in a JSON blob to points elsewhere.



            In this scenario, doing much of anything beyond validating the insert with an SQL regex constraint (or its ORM equivalent) is probably overkill of the YAGNI variety.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered yesterday









            Jared SmithJared Smith

            1,217517




            1,217517








            • 5





              Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

              – IMSoP
              yesterday











            • @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

              – Jared Smith
              yesterday











            • You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

              – IMSoP
              yesterday











            • You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

              – Miral
              16 hours ago














            • 5





              Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

              – IMSoP
              yesterday











            • @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

              – Jared Smith
              yesterday











            • You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

              – IMSoP
              yesterday











            • You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

              – Miral
              16 hours ago








            5




            5





            Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

            – IMSoP
            yesterday





            Your SQL regex constraint could be viewed as a qualified type - within your database, the Zip code is not stored as "VarChar" but "VarChar constrained by this rule". In some DBMSes, you could easily give that type+constraint a name as a reusable "domain type", and we are back in the recommended place of giving the data a meaningful type. I agree with your answer in principle, but don't think that example matches; a better example would be if your data is "raw sensor data", and the most meaningful type is "byte array" because you have no idea what the data means.

            – IMSoP
            yesterday













            @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

            – Jared Smith
            yesterday





            @IMSoP interesting point. Not sure I agree though: you could validate a string zip code in Java (or any other language) with a regex but still be dealing with it as a string instead of a richer type. Depending on the domain logic, further manipulation might be required (for instance ensuring that the zip code matches the state, something that would be difficult/impossible to validate with regex).

            – Jared Smith
            yesterday













            You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

            – IMSoP
            yesterday





            You could, but as soon as you do so, you are ascribing it some domain-specific behaviour, and that is exactly what the quoted articles are saying should lead to the creation of a custom type. The question is not whether you can do it in one style or the other, but whether you should, assuming your programming language gives you the choice.

            – IMSoP
            yesterday













            You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

            – Miral
            16 hours ago





            You could model such a thing as a RegexValidatedString, containing the string itself and the regex used to validate it. But unless every instance has a unique regex (which is possible but unlikely) this seems a bit silly and wasteful of memory (and possibly regex compilation time). So you either put the regex into a separate table and leave behind a lookup key in each instance to find it (which is arguably worse due to indirection) or you find some way to store it once for each common type of value sharing that rule -- eg. a static field on a domain type, or equivalent method, as IMSoP said.

            – Miral
            16 hours ago


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Software Engineering Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f386042%2fis-avoid-the-yo-yo-problem-a-reason-to-allow-the-primitive-obsession%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown











            Popular posts from this blog

            What are all the squawk codes?

            What are differences between VBoxVGA, VMSVGA and VBoxSVGA in VirtualBox?

            Hudsonelva