4 ms·
1. So the reason here to use private is as a way to tell programmers to not use a thing in too many places because then it will be hard to change in all those p
by robalni 4y ago
1. So the reason here to use private is as a way to tell programmers to not use a thing in too many places because then it will be hard to change in all those places if you need to change the private member?
2. First, remember that I am only interested in code that everyone has write access to so even if something is private, everyone can still change it. "This makes the class/module author responsible for controlling this invariant" - does that mean that private is mostly useful together with a rule that only one person (or a few) should change code that belongs to their class, so that that person can keep the invariants in their head and that way avoid breaking them?
- Jtsummers 4y ago1. It's private, only internal routines can use it. You aren't telling anyone to avoid using it, you are constraining where it can be used. You can use it directly in every internal routine if you want, that's not a problem. But no one outside the class/module can directly access it so if you change it (in whatever fashion) then those outside users will not be impacted because they are only dependent on the public interface. Now if you change the public interface, then they are impacted but that's often rare after the initial development effort, in my experience. 2. Forget about people, it's about place. I don't care how many people alter a particular class or module. What I care about is how many places some information has to exist within the code and has to be maintained and synchronized as a result. As a mostly-useless-after-CS101 example, consider a bounded stack. An implementation might have a number (perhaps variable) which describes its limit, an array backing it, and another number indicating where the current "top" of the stack is (in languages where arrays carry their size you don't necessarily need that limit number as a separate thing). If you leave everything public then every user of this bounded stack could directly alter the backing array and change the "top" of the stack, artificially indicating that something had been popped off or incorrectly incrementing beyond the limit. The limit itself could be altered without actually changing the backing array. The backing array could be made smaller or larger without correspondingly changing the limit. All of that would make this data structure useless, because it would be in an arbitrary, likely invalid, state. In order to preserve the invariants of the system (limit == array size, 0 <= top <= limit; as just two of them), you write routines internal to this module (class, whatever) that make sure that when one is changed the others are changed, or that changes are only valid (top can never be larger than limit or less than 0). If users are able to bypass these routines by directly accessing these three member fields of the data structure then to correctly use it they have to preserve all these invariants everywhere they use the data structure. The code is now scattered and contains many duplications. Again, this isn't about people, it's about places and the number of them. If you want to change the internal structure of this bounded stack, you have to change every place that currently accesses the public fields. Or you can be a sane developer, use private fields and public routines that manipulate the state so that it's always in a valid state. Now when you change the internal structure you only have to change those public routines and the private fields, no other place has to be altered. Every use of this bounded stack will look exactly the same as before, just push and pop and some error handling for when the stack is full or empty.
- robalni 4y ago1. So it's not to make it easier to change things? Otherwise what's the difference, in difficulty to change things, between using a member in 20 places outside of the class and 20 places inside the class? It's the same amount of code to change if you change the member. > But no one outside the class/module can directly access it Everyone can access all the code. > those outside users will not be impacted What do you mean that they will not be impacted? If I change the internal array and all the places where it's used, nobody might even notice it. Or is the assumption that one team can only change inside the class and other teams can only change outside of the class? Remember that we assume that everyone has access to the all the code. 2. > it's about places and the number of them. So again, is private a way to tell programmers to reduce the number of places they use the member in so that it will be easier to change it?
- Jtsummers 4y ago>> But no one outside the class/module can directly access it > Everyone can access all the code. I don't care what everyone can access (in terms of altering on a filesystem and code repository). It's about what the code can access. public class BoundedStack<T> { ... private int limit; // initialized in constructor private T[] array; private int top = 0; } Yes. Everyone who can see this code can alter this code, who cares? What matters is that users of this class cannot, in a separate module/class, directly access those three fields. You cannot, after marking these as private, do this inside some other class or module: void some_method() { BoundedStack<Messages> stack = new BoundedStack<Messages>(100); ... stack.top = 200; // invalid ... } That's invalid code, and that's a good thing. top is only modifiable by the BoundedStack class's own methods, which ensures that the state is always valid (so long as the code itself is correct, this is a trivial example to test so that's easy to verify one way or the other). Using private, here, is moving a convention (like _ in Python or just a verbal agreement or a note in the documentation) to a compiler checked thing. It makes it guaranteed that users cannot (modulo reflection facilities in some languages, but that's jumping through hoops, but that's their problem when it breaks, not mine) access these without going through your intended interfaces. >> those outside users will not be impacted > What do you mean that they will not be impacted? If I change the internal array and all the places where it's used, nobody might even notice it. Or is the assumption that one team can only change inside the class and other teams can only change outside of the class? Remember that we assume that everyone has access to the all the code. What I mean is that in the above, you can change the manner in which BoundedStack (or whatever the module is) is implemented and the interface remains the same. Users still have access to peek, pop, push, and whatever other methods you provided. What they cannot do is assume (you know what assuming does, right?) that the underlying representation is stable and bypass your intended interfaces. BoundedStack could switch to using a LinkedList for all the user needs to care, but if they assume it's an array backing it and have access to that array they might write something like this (even assuming they do everything as safely as possible so that this is a valid piece of data they're accessing, but in an unintended way): void bad_user() { BoundedStack<int> stack = new BoundedStack<int>(100); ... int a_datum = stack.array[n]; // where `n`, to be generous, is a valid item at this point ... } Now let's suppose you used public fields and that currently works. Later on you decide to switch to a linked list. Hypothetical rationale: Users often create large bounds for the stack for exceptional circumstances, but have small stacks 99% of the time; a linked list ends up using a lot less memory because it doesn't allocate a massive, and mostly unused, array; access is sufficiently infrequent that the allocations are a non-issue. For some reason you left the name array in place, so the user's code still works as is, or even if you did change it they changed the name (or maybe you did when you applied the name change to the whole codebase, oops). Except that array[n] is now a linear operation and not constant time. Suddenly there's a massive performance regression because the users: 1. Did something unexpected with the code (why the fuck are they using a stack if they want to access anything but the top?). 2. Assumed the internal implementation details were stable. It doesn't matter how many teams are involved, you can (and ought) to use private even for yourself or for one team. It makes the system far more maintainable and reliable to either have an actual language notion of private or a community consensus on what is private (like _ prefixed member variables in Python). It reduces coupling, which is one of the number one ways to cripple development as a system grows in size.