8 ms·
Please don't do this! All you have accomplished really is to move the descriptive name from the variable's name to the variable's type, and at what cost? You n
by jokerx 8y ago
Please don't do this!
All you have accomplished really is to move the descriptive name from the variable's name to the variable's type, and at what cost? You now have to create a whole new datatype. The programmer must look up that datatype and see oh, it's just an int. And now you need conversion routines or worse yet casting to convert that type to a simple int for interop reasons (database, UI).
Experience has shown that it is very productive to have a small number of generally useful datatypes which are augmented by custom datatypes.
Creating a new datatype for something as simple as an int or String defeats that and makes it more cumbersome to work with. (It's especially unwarranted for a variable that is an immutable variable serving as a simple id.)
- scarface74 8y agoYou haven’t just moved the descriptive name from the variable name to the type. If you are using a strongly typed language, you can just right click on the accountId type and find everywhere in your codebase where the “accountId” is being used. The “accountId” is not “just an int”. An accountId has semantics that would be different than an int. An accountId is not the same as a “customerId” that may also be represented as an int. The accountId also has different uses than an int. You’re not going to take the sum, average, etc of an accountId. An accountId that happens to have a value of 1 is semantically not equivalent of a customerId of 1. A method that expects a list of accountIds that are just ints will just as happily take a list of customerIds. But a method that takes as a parameter List<AccountId> will cause a compile time error if you pass in a List<CustomerId>. Why use a strongly typed language and then ruin one of the benefits of it if you don’t use domain specific types?
- cessor 8y agoI agree with @scarface74 and would go even further in that I would probably create a custom type for List<AccountId> to convey the purpose of what that thing is now. And as for the "type shyness": > Creating a new datatype for something as simple as an int or String defeats that and makes it more cumbersome to work with. It is never "just an int". Soon you will ask questions about the int, and in a sufficiently large application you will forget the answers to the quesions easily, and scatter the same question (i.e., the same boolean logic) all over the place. Consider an application where you need to filter something by year. A year, you know? As in 1974, 2018, etc. An int is bad, because an int could be -5, which does not make sense. Ok, a uint then? Still bad, because then there is a year 0 and - if your application deals with birthdays of real people, 1800 would be an invalid birthday. The semantics (or pragmatics, linguistically speaking) emerge from how you interpret the integer, and this interpretation can happily live in that one file with that one class that encapsulates "just" that integer. The AccountId probably shouldn't be < 0, and a specific type could be the one place where you make sure that this is always the case. What if you start with a 32bit integer and suddenly you realize that your weird Internet-Of-Things-Sensor reading database has grown and the 32bit integer is getting too small? You could just use 64bits. With "just an int", you now get to change every single function that expects an int for the purpose of a SensorReadingId (or an account id or whatever) and change it to size_t, uint, int64 or whatever. OR, you just tell the object that its internal representation uses 64bits now, because the AccountId class is the one single place that deals with account ids. The type provides a consistent interface that allows you to infer the semantics of its use. An int doesn't do that. This idea predates java by some ~20 years [1]. An int is not an account id. An int is not a year. An email is not a string. The result of working like that is considered a code smell [2]. Still, coming back to the example I made: It was primarily about commenting. I was taught to always comment, and StyleCop enforced comments on each and every field of a class, leading to noisy, superfluous comments. /// Gets or Sets the Name string Name { get; set; } does not add anything. As for returning an AccountID: I'd like to point out that the Account is a strong and independent object, who can make his own decisions and does not need to return anything. Returning its id like that is actually breaking encapsulation. Still if you do it, it probably shouldn't be an integer. Why are people scared of creating types and objects? [1] https://link.springer.com/chapter/10.1007%2F978-1-4612-6315-9_22 https://link.springer.com/chapter/10.1007%2F978-1-4612-6315-... [2] https://sourcemaking.com/refactoring/smells/primitive-obsession https://sourcemaking.com/refactoring/smells/primitive-obsess...
- jokerx 8y agoI take it you don't write a lot of CRUD apps? You deal with a lot of data that is of only a few simple types, and as I said in my post, interoperability means you can't get too crazy with your class definitions. They need to stay simple or the SYSTEM will become more complicated. As for a variable never staying a simple type, again, see my example in my previous post. A string id is going to stay a string id and its data and behavior is dictated by being an id to remain supersimple. I am not against defining new types; I am against defining a new type for every single variable "just because."
- scarface74 8y agoFor interoperability, you would want to keep the models that are sent out over the wire separate from the models you use in your program. You want the freedom of being able to change your database properties and fields, your domain models which could be some type of crazy aggregation of separate database tables are even information from other sources, without changing the structure that you expose to external clients.
- jokerx 8y agoYour example of needing to define a type because "int" is not good enough is very C/C++ centric. Most languages don't have the plethora of integer types that C has. Java, for example, has just has 2 in general use: int (signed 32 bit) and long (signed 64 bit). (Nobody considers byte a general integer type.) In C/C++, you have to define so many things about the types you use because so much is "implementation dependent." So for C/C++, you may be correct. Most other languages define their types more stringently and include a smaller number of general purpose types.
- scarface74 8y agoC# has nine integral types. https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/integral-types-table https://docs.microsoft.com/en-us/dotnet/csharp/language-refe... But even then, a year is an int, but it would have certain semantics you would want to enforce outside of integers.
- jokerx 8y agoI understand your reasoning, but in your reply to my critique, you did not address any of the drawbacks I listed except for the first comment I made. If you can explicitly refute those, then your position would be stronger.
- scarface74 8y agoThe programmer must look up that datatype and see oh, it's just an int. And now you need conversion routines or worse yet casting to convert that type to a simple int for interop reasons (database, UI). Why should the developer care what the underlying type is? It should be an opaque type. When you serialize it, your serializer should call your ToString() method. When you deserialize it, your deserializer would call the constructor that takes an int. But why are you saving an id to a database - which would never be used as int - as an int? But even in most CRUD apps, your domain model with rich types would be different than your view model which would probably also be different than your DB model. You are going to be mapping back and forth regardless - hopefully using a tool like Automapper.