digitalmars.D - The interaction of encapsulation and properties in D
- =?UTF-8?B?Ikx1w61z?= Marques" (125/125) Jul 10 2013 Maybe this has already been discussed (if so sorry), but I would
- =?UTF-8?B?Ikx1w61z?= Marques" (2/2) Jul 10 2013 (Of course I forgot to change the variable to private in the
- Jesse Phillips (9/15) Jul 10 2013 In general it seems people just use public fields. (I do, and
- Jonathan M Davis (12/18) Jul 10 2013 In general, I would strongly advise against using public fields if you e...
- Jacob Carlborg (6/15) Jul 11 2013 I use public fields sometimes but as you say, there are problems. One
- Jonathan M Davis (21/24) Jul 11 2013 Property rewrites would definitely help (and I agree that we should have...
- Mike James (6/9) Jul 11 2013 +1
- Jacob Carlborg (5/15) Jul 11 2013 I would really like to have this. It's like in Scala, all public fields
- Dicebot (8/9) Jul 11 2013 I often find it cleaner to consider D minimal hard encapsulation
Maybe this has already been discussed (if so sorry), but I would like to ask your opinion about the following. The condensed version: because we have properties, is it wise to use public member variables in D, in some circumstances? The long version, starting with the context: AFAIK, standard encapsulation wisdom says you should never expose member variables directly, like this: class X { public int phone; } void main() { X x = new X(); x.phone = 555123; } The usual admonitions apply, such that you might want to change the internal representation of the 'phone'. So, as with every problem in life, you decide to add one more level of indirection, and you implement some form of getters and setters: // in Java or in D without using properties... class X { public int _phone; void setPhone(int v) { _phone = v; } int getPhone() { return _phone; } } void main() { X x = new X(); x.setPhone(555123); } With such encapsulation, once you realise the silliness of having a phone number stored in an int, this is easy to change without breaking the client code: // in D without using properties class X { public string _phone; void setPhone(int v) { _phone = to!string(v); } void setPhone(string v) { _phone = to!string(v); } int getPhone() { return to!int(_phone); } } void main() { X x = new X(); x.setPhone(555123); // did not break the old client code x.setPhone("555 123"); // the new stringified API also works } Of course, in D we would do this with properties instead: class X { public string _phone; property void phone(int v) { _phone = to!string(v); } property int phone() { return to!int(_phone); } property void phone(string v) { _phone = v; } property string phoneString() { return _phone; } } void main() { X x = new X(); x.phone = 555123; x.phone = "555 123"; } But when we use the property syntax to implement the encapsulation the client code does not have to change; there is source code compatibility at the client side: the same client code can be used for both the the direct member variable implementation and the property accessors implementation, as long as we are willing to recompile. The client code is already shielded from such implementation issue; in a certain sense the 'phone' "field" was already encapsulated. Why, then, should one use (traditional, property based) encapsulation *proactively*? After all, if we later decide that we want to sanitize the setter inputs, or add logging, or trigger an action, or something like that, we can always refactor the public member variable into the respective property accessors. So why not, instead, just use a simple public member variable, when that is cleaner (less boilerplate code), more straightforward, and not obvious that we will really need the extra indirection? A case where such reasoning would not apply is when source code compatibility is not enough, such as when you have a D library. But this could be solved by something like... class X { property public int phone; } ...which would under the covers generate getter and setter properties, so that if you later wanted to encapsulate it without breaking binary compatibility you could do so. So, summing it up: even assuming that performance is not an issue, does the advice to always encapsulate your member variables (as one would do, for instance, in idiomatic Java) actually make sense for D, or would you recommend using public member variables when that is more straightforward, and the indirection is not yet needed?
Jul 10 2013
(Of course I forgot to change the variable to private in the versions with accessors)
Jul 10 2013
On Thursday, 11 July 2013 at 02:55:24 UTC, Luís Marques wrote:So, summing it up: even assuming that performance is not an issue, does the advice to always encapsulate your member variables (as one would do, for instance, in idiomatic Java) actually make sense for D, or would you recommend using public member variables when that is more straightforward, and the indirection is not yet needed?In general it seems people just use public fields. (I do, and sometimes annotated with property). Considering the history this makes sense; optional parens "addressed" properties (see property discussions). On of the main problems has been that public fields can be passed to ref parameters while this is not true for getters. I see this as a deficiency in property but don't recall all the detail of the problem.
Jul 10 2013
On Thursday, July 11, 2013 04:55:09 =?UTF-8?B?Ikx1w61z?=.Marques <luismarques gmail.com> puremagic.com wrote:So, summing it up: even assuming that performance is not an issue, does the advice to always encapsulate your member variables (as one would do, for instance, in idiomatic Java) actually make sense for D, or would you recommend using public member variables when that is more straightforward, and the indirection is not yet needed?In general, I would strongly advise against using public fields if you ever might want to replace them with property functions. The reason for this is that property functions really don't do all that great a job of emulating variables. For instance, taking the address of a variable gives you a completely different type than taking the address of a function does, and variables can be passed by ref, whereas the result of a property function can't (unless it returns by ref, which would ruin the encapsulation that it provides). In general, if you use public variables, you're just going to cause yourself trouble in the long run unless they stay public variables forever. - Jonathan M Davis
Jul 10 2013
On 2013-07-11 07:26, Jonathan M Davis wrote:In general, I would strongly advise against using public fields if you ever might want to replace them with property functions. The reason for this is that property functions really don't do all that great a job of emulating variables. For instance, taking the address of a variable gives you a completely different type than taking the address of a function does, and variables can be passed by ref, whereas the result of a property function can't (unless it returns by ref, which would ruin the encapsulation that it provides). In general, if you use public variables, you're just going to cause yourself trouble in the long run unless they stay public variables forever.I use public fields sometimes but as you say, there are problems. One can also run into problems with returning structs by value. The compiler would also need to do property rewriting to work flawlessly. -- /Jacob Carlborg
Jul 11 2013
On Thursday, July 11, 2013 09:37:56 Jacob Carlborg wrote:I use public fields sometimes but as you say, there are problems. One can also run into problems with returning structs by value. The compiler would also need to do property rewriting to work flawlessly.Property rewrites would definitely help (and I agree that we should have them), but they're far from enough to make variables and property functions interchangeable. I think that the only way that that could be done is if public variables could have additional restrictions put on them somehow (e.g. make it illegal to take their address or pass them by ref). Otherwise, there will always be a few things that will work with a public variable but not a property function. A programmer can certainly choose to use public variables and just put up with broken code when swapping them with property functions later if you're in control of all your code (especially if you rarely use public variables in contexts where a property function would be illegal), but if you're releasing a public API which could be used by anyone, then I would argue that it's far better to just not use public variables, because the risk of code breakage is too high to be worth it. Also, if we could make it so that something like property int value; got lowered to property functions for you, then we could eliminate the boilerplate that frequently leads people to want to make member variables public rather than declaring property functions. - Jonathan M Davis
Jul 11 2013
On Thursday, 11 July 2013 at 07:50:38 UTC, Jonathan M Davis wrote:On Thursday, July 11, 2013 09:37:56 Jacob Carlborg wrote:[snip]Also, if we could make it so that something like property int value;+1 It would be a great timesaver and give a reason for property to exist :-) -=mike=-
Jul 11 2013
On 2013-07-11 04:55, "Luís Marques" <luismarques gmail.com>" wrote:A case where such reasoning would not apply is when source code compatibility is not enough, such as when you have a D library. But this could be solved by something like... class X { property public int phone; } ...which would under the covers generate getter and setter properties, so that if you later wanted to encapsulate it without breaking binary compatibility you could do so.I would really like to have this. It's like in Scala, all public fields are implemented with methods. -- /Jacob Carlborg
Jul 11 2013
On Thursday, 11 July 2013 at 02:55:24 UTC, Luís Marques wrote:...I often find it cleaner to consider D minimal hard encapsulation unit to be a module, not a class - it just maps naturally to protection and module system. That means avoiding aggregate methods as part of stable API/ABI. The latter is also an issue with directly exposing public fields - while paren-less call allows to replace them with getter/setter with no syntax (API) change, ABI will change anyway.
Jul 11 2013