Ask HN: Why do you make class members private?
47 comments
To hide implementation details from class users. You ideally only want to have a documented interface and all implementation details should be opaque to class users. This can be quite tricky and usually classes will export some state, typically done through getters and setters.
Then one day you decide you want to aggressively refactor that class. Now you can because the interface can stay the same even though you could completely re-do the guts. Better still: all your tests will still work, as will your class documentation.
Then one day you decide you want to aggressively refactor that class. Now you can because the interface can stay the same even though you could completely re-do the guts. Better still: all your tests will still work, as will your class documentation.
Can we acknowledge yet that the concept of classes as tiny programs with tiny APIs and private implementations has failed?
Admittedly, my opinion is mainly based around working mostly in C++, but every codebase I work in of significant age is a knitted castle of interlocking classes with poorly thought out accessors and private variables. While the vision of carefully encapsulating implementation details is nice, in practice the discipline required to do so seems unachievable.
Instead of using private members, if I want an interface I explicitly make an abstract base class interface and inherit from it.
This has two advantages: (1) unit tests are simpler when I can poke/prod the concrete internals because they're public. (2) I can make an "include" directory with the interfaces in it when I want to distribute a shared library and those interfaces will be more concise and less likely to leak all sorts of implementation details in the headers.
Admittedly, my opinion is mainly based around working mostly in C++, but every codebase I work in of significant age is a knitted castle of interlocking classes with poorly thought out accessors and private variables. While the vision of carefully encapsulating implementation details is nice, in practice the discipline required to do so seems unachievable.
Instead of using private members, if I want an interface I explicitly make an abstract base class interface and inherit from it.
This has two advantages: (1) unit tests are simpler when I can poke/prod the concrete internals because they're public. (2) I can make an "include" directory with the interfaces in it when I want to distribute a shared library and those interfaces will be more concise and less likely to leak all sorts of implementation details in the headers.
No.
I think that compared to the giant hairballs of absolutely unmanageable spaghetti classes when done right have made larger scale projects tractable to mere mortals and have significantly improved software maintenance.
Classes done wrong (hundreds or even thousands of classes that all do almost nothing) are a terrible anti-pattern and should be avoided.
The exact same thing goes for services. And in fact for any tool used in a really bad way.
I think that compared to the giant hairballs of absolutely unmanageable spaghetti classes when done right have made larger scale projects tractable to mere mortals and have significantly improved software maintenance.
Classes done wrong (hundreds or even thousands of classes that all do almost nothing) are a terrible anti-pattern and should be avoided.
The exact same thing goes for services. And in fact for any tool used in a really bad way.
Sad to see this earnest post down-voted. Maybe you had to be more precise about the poor thought out nature of what you saw. In my experience you are right, in as much as developers whose notions of design only go so far as creating many small classes do not think enough about design...
Can we acknowledge yet that the concept of classes as tiny programs with tiny APIs has failed? [...] mainly based around working mostly in C++
Speaking as a C# developer, I think the concept of classes as tiny programs with tiny APIs works very well.
Speaking as a C# developer, I think the concept of classes as tiny programs with tiny APIs works very well.
It's been a decade since I used C# but the corporate design pattern culture of that language back then turned me off of it forever.
Everything looked like this: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
Maybe it's better now but the Java/C# practice of shoveling largely empty classes around with an IDE isn't something I'd point to as a good example.
Everything looked like this: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
Maybe it's better now but the Java/C# practice of shoveling largely empty classes around with an IDE isn't something I'd point to as a good example.
I've worked with C# off and on for over a decade, and never encountered anything close to that. Maybe the trick was that I was working with competent people.
Now the Fortran code I've encountered, that's the stuff of nightmares (to be fair to Fortran I've also encountered good code, but there seems to be a generation of embedded developers who have no formal programming training that all adopted a style of Fortran inspired by Cthulhu).
Now the Fortran code I've encountered, that's the stuff of nightmares (to be fair to Fortran I've also encountered good code, but there seems to be a generation of embedded developers who have no formal programming training that all adopted a style of Fortran inspired by Cthulhu).
Wow, what a repo. I can't even tell if the author is serious or this is some tinderbox-dry satire, bravo either way.
I'm sad that I've worked with plenty of codebases that look exactly like that and I twitched a little bit browsing around. :)
I'm sad that I've worked with plenty of codebases that look exactly like that and I twitched a little bit browsing around. :)
From the readme:
> Although this project is intended as satire, we take openness and inclusivity very seriously. To that end we have adopted the following code of conduct.
You can see the exact equivalent in the Java world.
It's been a decade since I used C#
Well the last time I regularly used C++ was Borland C++ 3.1 from 1992. I'm not sure you'd be interested in my opinions on the language from 30 years ago.
Well the last time I regularly used C++ was Borland C++ 3.1 from 1992. I'm not sure you'd be interested in my opinions on the language from 30 years ago.
> Can we acknowledge yet that the concept of classes as tiny programs with tiny APIs and private implementations has failed?
I lived the past 10 years in a ruby codebase where since effectively everything was public, we didn't mark enough stuff private and you can always YOLO monkeypatch or call things with `send(:method)` there was zero effective encapsulation.
And that pretty much crippled the product because now everything could be a public API so you can't change some shitty little helper to clean the code up somewhere because someone might have written code against that, in their private codebase that you have no visibility at all into. Every change becomes a potentially breaking change, because you have no idea how someone else's codebase might or might have fucked you by digging into your codebase.
Rejecting encapsulation is literally the dumbest post-OO take, and I have the scars to prove it.
Good fences make good neighbors and all that.
I lived the past 10 years in a ruby codebase where since effectively everything was public, we didn't mark enough stuff private and you can always YOLO monkeypatch or call things with `send(:method)` there was zero effective encapsulation.
And that pretty much crippled the product because now everything could be a public API so you can't change some shitty little helper to clean the code up somewhere because someone might have written code against that, in their private codebase that you have no visibility at all into. Every change becomes a potentially breaking change, because you have no idea how someone else's codebase might or might have fucked you by digging into your codebase.
Rejecting encapsulation is literally the dumbest post-OO take, and I have the scars to prove it.
Good fences make good neighbors and all that.
No, it didn’t fail. People are just bad at architecturing their programs and/or there is not enough time for technical debt.
While not much difference in C++ specifically, but having a distinction between a class that encapsulates state and a record/struct that is nothing but plain old data is essential in my opinion. Both are useful for different problems.
While not much difference in C++ specifically, but having a distinction between a class that encapsulates state and a record/struct that is nothing but plain old data is essential in my opinion. Both are useful for different problems.
> you could completely re-do the guts...all your tests will still work
That's optimistic. I suspect there are some people who would be confused by your statement if they've been exposed mostly to tests that verify the steps taken by the implementation.
That's optimistic. I suspect there are some people who would be confused by your statement if they've been exposed mostly to tests that verify the steps taken by the implementation.
That's what I would call 'brittle tests'.
Yeah, "brittle", or "fragile", or "tightly coupled to the implementation"
In any case, it's not good. Also not uncommon.
In any case, it's not good. Also not uncommon.
> Also not uncommon.
Fair enough.
Typically this is a case of people not really getting what unit tests are for, so they end up testing the implementation rather than the behavior.
Fair enough.
Typically this is a case of people not really getting what unit tests are for, so they end up testing the implementation rather than the behavior.
> To hide implementation details from class users.
Yes, that's what private does. My question was more why you should do that.
> the interface can stay the same even though you could completely re-do the guts.
So is the reason to use private that it's a way to tell programmers to not use something in too many places because that will make it hard to change in all those places if you need to change the private member?
Yes, that's what private does. My question was more why you should do that.
> the interface can stay the same even though you could completely re-do the guts.
So is the reason to use private that it's a way to tell programmers to not use something in too many places because that will make it hard to change in all those places if you need to change the private member?
No, it says 'this is not meant for you to change from the outside'. It's an implementation detail, not part of the interface and any references to it are bound to break without notice, warning or acceptance of the consequences.
Think of the interface to your class as a contract: this is how this class works, from now until eternity (or until the next breaking change ;) ). Relying on implementation details breaks that contract, the contract is on the interface not on the guts.
It won't be 'hard to change' it may be impossible to change. Because someone that relies on your class may not have access to the code of the other class!
Think of the interface to your class as a contract: this is how this class works, from now until eternity (or until the next breaking change ;) ). Relying on implementation details breaks that contract, the contract is on the interface not on the guts.
It won't be 'hard to change' it may be impossible to change. Because someone that relies on your class may not have access to the code of the other class!
> any references to it are bound to break
Do you mean that if someone changes a class member, they would not change in all places that use that class member? Why not?
> someone that relies on your class may not have access to the code
As I wrote in the clarification, I'm only interested in cases where everyone has access to all the code.
Do you mean that if someone changes a class member, they would not change in all places that use that class member? Why not?
> someone that relies on your class may not have access to the code
As I wrote in the clarification, I'm only interested in cases where everyone has access to all the code.
> You have been taught to do it so you just do it without thinking.
Ouch, that's a pretty careless reason to start with. Why does insulting your audience seem like the right first move?
In much of the classist (classy? You decide) code I've written, the entire reason that I've chosen to encapsulate the data into a class is that maintaining a consistent data structure requires book-keeping. The fields and methods associated with this bookkeeping are not part of the public interface, because there should never be a reason for consumers to handle that stuff directly.
In short: even in a project that "everybody has access to," you should still implement clean APIs. If you let everybody put hooks into everything anywhere, you get horrible hacky spaghetti code.
Edit: and just FYI, "private" does not mean "secure" or "secret" or "untouchable." A dedicated user can gain access to, and modify, everything.
Ouch, that's a pretty careless reason to start with. Why does insulting your audience seem like the right first move?
In much of the classist (classy? You decide) code I've written, the entire reason that I've chosen to encapsulate the data into a class is that maintaining a consistent data structure requires book-keeping. The fields and methods associated with this bookkeeping are not part of the public interface, because there should never be a reason for consumers to handle that stuff directly.
In short: even in a project that "everybody has access to," you should still implement clean APIs. If you let everybody put hooks into everything anywhere, you get horrible hacky spaghetti code.
Edit: and just FYI, "private" does not mean "secure" or "secret" or "untouchable." A dedicated user can gain access to, and modify, everything.
> Ouch, that's a pretty careless reason to start with
I think it's fine. This is probably the reason that I used to have for using private until I started thinking about it.
> there should never be a reason for consumers to handle that stuff directly
Are we assuming here that the organization is structured so that only one team works on a class, so that that's the team that knows how the bookkeeping is done and no other team should touch it?
I think it's fine. This is probably the reason that I used to have for using private until I started thinking about it.
> there should never be a reason for consumers to handle that stuff directly
Are we assuming here that the organization is structured so that only one team works on a class, so that that's the team that knows how the bookkeeping is done and no other team should touch it?
> Are we assuming here that the organization is structured so that only one team works on a class, so that that's the team that knows how the bookkeeping is done and no other team should touch it?
No. If you're in such an organization, anybody on any team should be able to read the code and discover how the bookkeeping is done and locate bugs therein. A good API will obscure the necessity of bookkeeping from consumers in order to reduce bug-ridden boilerplate. Said in more highfalutin' language, privacy should be used to prevent leaky abstractions.
No. If you're in such an organization, anybody on any team should be able to read the code and discover how the bookkeeping is done and locate bugs therein. A good API will obscure the necessity of bookkeeping from consumers in order to reduce bug-ridden boilerplate. Said in more highfalutin' language, privacy should be used to prevent leaky abstractions.
> > Ouch, that's a pretty careless reason to start with
> I think it's fine. This is probably the reason that I used to have for using private until I started thinking about it.
I mean I understand if no-one would claim to have that reason currently because people like to have more specific reasons for what they do but it's still a valid reason for using private so I didn't want to leave it out.
> I think it's fine. This is probably the reason that I used to have for using private until I started thinking about it.
I mean I understand if no-one would claim to have that reason currently because people like to have more specific reasons for what they do but it's still a valid reason for using private so I didn't want to leave it out.
> A dedicated user can gain access to, and modify, everything.
That may be so, but it is akin to using an undocumented system call. And good luck if that class ever gets refactored or turned into a service.
That may be so, but it is akin to using an undocumented system call. And good luck if that class ever gets refactored or turned into a service.
[deleted]
> To clarify, I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.
Within reason, you should treat every class as a tiny library that defines an API for other people to use.
That allows you freedom to change things within the class without breaking other code. It also makes it easier to prevent accidental misuse of the class that can leave it in weird states that aren't supposed to happen. It allows you to reason about the intended use cases of the class and write tests for them all.
Within reason, you should treat every class as a tiny library that defines an API for other people to use.
That allows you freedom to change things within the class without breaking other code. It also makes it easier to prevent accidental misuse of the class that can leave it in weird states that aren't supposed to happen. It allows you to reason about the intended use cases of the class and write tests for them all.
Because classes are encapsulated data plus contracts over managing the object's state. If a class does not have control of its own data, then it cannot fulfill its contracts.
Let's say I have a Motorcycle class with 2 Wheel members. What if in the code I assume 2 Wheels when writing the serializer? And then someone comes along, instantiates a Motorcycle and nukes a Wheel? The code will break.
You can make an argument that OOP is not what you want, and instead create something akin to namespaces+data, and that's fine. But OOP's thing is to create a public API, and controlled state behind it.
Let's say I have a Motorcycle class with 2 Wheel members. What if in the code I assume 2 Wheels when writing the serializer? And then someone comes along, instantiates a Motorcycle and nukes a Wheel? The code will break.
You can make an argument that OOP is not what you want, and instead create something akin to namespaces+data, and that's fine. But OOP's thing is to create a public API, and controlled state behind it.
"With a sufficient number of users of an API,
it does not matter what you promise in the contract:
all observable behaviors of your system
will be depended on by somebody."
it does not matter what you promise in the contract:
all observable behaviors of your system
will be depended on by somebody."
Corollary:
If your programming language supports reflection, people will depend on private properties and methods regardless of what you do.
If your programming language supports reflection, people will depend on private properties and methods regardless of what you do.
People who can't accomplish something with an API because things are kept private won't be users.
It is always better to get the authors of the API to provide a supported way of doing something, rather than trying to bypass the interface. That requires human skills rather than computer skills though.
Because people might throw a lot of weird things at my class and I want to make sure that no invalid values are set on those properties. I might use the property or class member later on. Let's assume my app uses stock prices and because of some issue, the price for a given equity comes through as Null. And then I don't want someone to be able to do Share.Price = API.Price.
I might want to have a method called SetPrice(APIPrice price) and there I do a check to see if it's a valid value as far as my code is concerned. I had a situation like this handling pricing information taken from a variety of indices but I want a calculation to still work out to something usable. Also, most business users don't understand what NULL * 3.5 would mean, but they would understand 0.0 * 3.5.
That's the main one off the top of my head.
I might want to have a method called SetPrice(APIPrice price) and there I do a check to see if it's a valid value as far as my code is concerned. I had a situation like this handling pricing information taken from a variety of indices but I want a calculation to still work out to something usable. Also, most business users don't understand what NULL * 3.5 would mean, but they would understand 0.0 * 3.5.
That's the main one off the top of my head.
The following is a public API for a Car:
- Steering wheel
- Transmission
- Brakes and Gas pedal
- Doors and Windows, etc.
The following is a private API for a Car:
- The inner workings of the engine
- The inner workings of the battery
- Smart break system, internal wiring, etc.
All users of the Car only care about how to drive it with as little knowledge as possible.
Now Mazda can do a full recall and upgrade the smart breaking systems internally but millions of drivers around the world don't have to "learn" anything new, they can happily continue using the public API.
It makes refactoring much easier, reduces dependency and enables duct typing (in supported languages) via message passing for objects with a common public API. You also don't have to perform shotgun surgery[0] when changing classes..
[0] https://refactoring.guru/smells/shotgun-surgery
- Steering wheel
- Transmission
- Brakes and Gas pedal
- Doors and Windows, etc.
The following is a private API for a Car:
- The inner workings of the engine
- The inner workings of the battery
- Smart break system, internal wiring, etc.
All users of the Car only care about how to drive it with as little knowledge as possible.
Now Mazda can do a full recall and upgrade the smart breaking systems internally but millions of drivers around the world don't have to "learn" anything new, they can happily continue using the public API.
It makes refactoring much easier, reduces dependency and enables duct typing (in supported languages) via message passing for objects with a common public API. You also don't have to perform shotgun surgery[0] when changing classes..
[0] https://refactoring.guru/smells/shotgun-surgery
>To clarify, I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.
If you can see the use in access modifiers for libraries then it's not so hard to make the leap to its general usefulness. Defining objects with clear and simple APIs is extremely valuable when working on complex software projects. As the code base inevitably grows to a point where no one knows every part of it, you want developers to be able to contribute without having intimate knowledge of everything. The value proposition is the same as for third-party libraries.
If you can see the use in access modifiers for libraries then it's not so hard to make the leap to its general usefulness. Defining objects with clear and simple APIs is extremely valuable when working on complex software projects. As the code base inevitably grows to a point where no one knows every part of it, you want developers to be able to contribute without having intimate knowledge of everything. The value proposition is the same as for third-party libraries.
That's what it's called when you do things like that but what's the reason?
From the linked page I read: "Encapsulation is used to hide the values or state of a structured data object inside a class, preventing direct access to them by clients in a way that could expose hidden implementation details or violate state invariance maintained by the methods."
So there are two possible reasons here:
1. The access could "expose hidden implementation details". This is not really a reason because it's just saying that you hide something to prevent access in a way that can expose what you hid.
2. The access could "violate state invariance maintained by the methods". Let's say the class has two arrays, `vertices` and `edges`. The state invariance is that they need to have the same number of items (like you have in a polygon). Then why would you make them private? A possible reason could be that it's easy to forget to add an edge if you add a vertex. But why would that be easier to remember if you do it in a member function?
From the linked page I read: "Encapsulation is used to hide the values or state of a structured data object inside a class, preventing direct access to them by clients in a way that could expose hidden implementation details or violate state invariance maintained by the methods."
So there are two possible reasons here:
1. The access could "expose hidden implementation details". This is not really a reason because it's just saying that you hide something to prevent access in a way that can expose what you hid.
2. The access could "violate state invariance maintained by the methods". Let's say the class has two arrays, `vertices` and `edges`. The state invariance is that they need to have the same number of items (like you have in a polygon). Then why would you make them private? A possible reason could be that it's easy to forget to add an edge if you add a vertex. But why would that be easier to remember if you do it in a member function?
Re 1: The reason is not just to hide access, but also to permit change. Suppose someone implements a data structure using a backing array. Later on they change it to use a b-tree, but users have started directly accessing the backing array (because it's faster than the presented interface, even if just by a function call) and passing around references/pointers to array elements. Oops. You can't make changes anymore, the users are no longer using the intended data structure but a wrapped array.
Re 2: Supposing that the two collections have to be the same size, this implies that they get extended at the same time. If they are public, then anyone can add either a vertex or an edge at will. But they are not required (by the interface, only by a verbal contract of sorts) to extend both at the same time. With private members, you present a public interface that ensures both collections are updated simultaneously (or "simultaneously", as a transaction both happen before the method/function/subroutine/procedure/whatever the fuck ends). This makes the class/module author responsible for controlling this invariant, and not the users. Which is the only sane option unless you really like broken contracts and duplicated code scattered throughout a code base.
Re 2: Supposing that the two collections have to be the same size, this implies that they get extended at the same time. If they are public, then anyone can add either a vertex or an edge at will. But they are not required (by the interface, only by a verbal contract of sorts) to extend both at the same time. With private members, you present a public interface that ensures both collections are updated simultaneously (or "simultaneously", as a transaction both happen before the method/function/subroutine/procedure/whatever the fuck ends). This makes the class/module author responsible for controlling this invariant, and not the users. Which is the only sane option unless you really like broken contracts and duplicated code scattered throughout a code base.
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?
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?
1. 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.
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.
1.
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?
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?
>> 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.
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):
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.
> 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.
> What matters is that users of this class cannot, in a separate module/class, directly access those three fields.
Yes that's what private does, it makes sure that you can't access something from any place in the code. The questions is why you should make this constraint.
> That's invalid code
Is private used to tell programmers that if you change this variable or call this function, the object can be in an invalid state?
> For some reason you left the name array in place
Is the reason here that private makes it easier to find all uses of something because it reduces the number of places you have to look in? Example, you change the meaning of a variable but forget to change the name so the compiler will not tell you about all the places to change. You can still change all the uses of that member in the class because those uses are easy to find and know about.
Yes that's what private does, it makes sure that you can't access something from any place in the code. The questions is why you should make this constraint.
> That's invalid code
Is private used to tell programmers that if you change this variable or call this function, the object can be in an invalid state?
> For some reason you left the name array in place
Is the reason here that private makes it easier to find all uses of something because it reduces the number of places you have to look in? Example, you change the meaning of a variable but forget to change the name so the compiler will not tell you about all the places to change. You can still change all the uses of that member in the class because those uses are easy to find and know about.
Consider a class like a HashedLinkedList. This is a linked list which allows direct access to nodes using dictionary look up. It is generally implemented with a LinkedList and Dictionary internally. The expectation is that all elements in the dictionary are in the linked list. Any deviation from this implementation detail will cause invalid state.
Making the list or the dictionary public opens you up to situations where consumers of the code modify either datastore independently and corrupt the consistency of the class.
Making the list or the dictionary public opens you up to situations where consumers of the code modify either datastore independently and corrupt the consistency of the class.
History time: In 1972, David Parnas wrote a paper about "Information Hiding," and a few years later Fred Brooks wrote The Mythical Man-Month, possibly the most influential book about software engineering, which largely discounted the value of information hiding. 20 years later, in an updated anniversary edition of The Mythical Man-Month, Brooks wrote "Parnas was right, and I was wrong about information hiding."
Coming from Fred Brooks, this was an enormous admission! What is this amazing "information hiding?" It is separating interface from implementation, leaving one public and the other private. It is accomplished, among other means, by marking things "private."
This is the one thing in 20 years that Brooks found to be valuable in increasing programmer productivity in terms of essential complexity. Marking implementation details as private reduces the essential complexity of the code.
By the way, in some of your comments you talk about "everyone" having access to things, as if that makes a difference. Marking things as "private" is not about closing off access to particular people. It's not a security measure. It's about reducing the complexity of a given class by ensuring that the number of access points to that class are limited. That could affect other teams, sure, but it could also affect future-you, or you-working-on-other-class.
Coming from Fred Brooks, this was an enormous admission! What is this amazing "information hiding?" It is separating interface from implementation, leaving one public and the other private. It is accomplished, among other means, by marking things "private."
This is the one thing in 20 years that Brooks found to be valuable in increasing programmer productivity in terms of essential complexity. Marking implementation details as private reduces the essential complexity of the code.
By the way, in some of your comments you talk about "everyone" having access to things, as if that makes a difference. Marking things as "private" is not about closing off access to particular people. It's not a security measure. It's about reducing the complexity of a given class by ensuring that the number of access points to that class are limited. That could affect other teams, sure, but it could also affect future-you, or you-working-on-other-class.
You can change something private w/o breaking the api.
I think that lurking behind the question is an assumption that freedom and openness is better. Freedom and openness is fine until you want to build a house. Then you actually have to put walls somewhere to hold the roof up...and then you build inner walls to hide the bathroom from the rest of the house. In building software, structure is inevitable. The rightly chosen structure will give benefits; poorly chosen structure will work against you. When I build a class, it serves a purpose to some other part of the system. The purpose it serves is understood by its public members and functions. Everything else is private. What happens if you don't do this is that the next person to read and use/modify this class isn't going to know what your intention was or all the details of how it works, so it helps to give them the benefit of some hints of structure.
The only time I break this rule is for data-transport type classes. For example if I serialize some object into some class X before sending it to some API. Since all those objects do is hold data, I see no point in making the members private. Many people do, I suppose out of habit; or in anticipation that maybe one day those objects will do more than hold data. I'd like to hear a good argument for making members private in data-transport type classes.
The only time I break this rule is for data-transport type classes. For example if I serialize some object into some class X before sending it to some API. Since all those objects do is hold data, I see no point in making the members private. Many people do, I suppose out of habit; or in anticipation that maybe one day those objects will do more than hold data. I'd like to hear a good argument for making members private in data-transport type classes.
> To clarify, I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.
A lot of people are ignoring this part of the question. The primary usefulness of object-oriented programming is scope control. Dependencies kill teams, and scope control helps them survive. The more I limit the scope of something, the easier for someone else to understand it and make necessary changes.
Imagine a language without scope control: When people try to understand how something works and what might break it, they have to consider all possible points of entry, which becomes every variable & function in your class.
Java programmers often pitch fits about "getters" and "setters", insisting that every variable be private and guarded by a public method. In your context, you might find this to be kind of silly since we're mostly just moving scope control around instead of limiting it, but good luck fighting that battle (I'm not going to try). At any rate, I still would not use this kind of annoyance as an excuse to wave off scope control as nothing but bureaucratic boilerplate. It has real value when used pragmatically.
A lot of people are ignoring this part of the question. The primary usefulness of object-oriented programming is scope control. Dependencies kill teams, and scope control helps them survive. The more I limit the scope of something, the easier for someone else to understand it and make necessary changes.
Imagine a language without scope control: When people try to understand how something works and what might break it, they have to consider all possible points of entry, which becomes every variable & function in your class.
Java programmers often pitch fits about "getters" and "setters", insisting that every variable be private and guarded by a public method. In your context, you might find this to be kind of silly since we're mostly just moving scope control around instead of limiting it, but good luck fighting that battle (I'm not going to try). At any rate, I still would not use this kind of annoyance as an excuse to wave off scope control as nothing but bureaucratic boilerplate. It has real value when used pragmatically.
> Dependencies kill teams
Do you mean that when you have structured the organization so that each class is worked on by at most one team, you want to reduce the number of dependencies between classes in order to reduce the number of dependencies between teams?
> they have to consider all possible points of entry
So is this basically what I mentioned, that you want to reduce the number of places you have to look in when you want to find all uses of it?
Do you mean that when you have structured the organization so that each class is worked on by at most one team, you want to reduce the number of dependencies between classes in order to reduce the number of dependencies between teams?
> they have to consider all possible points of entry
So is this basically what I mentioned, that you want to reduce the number of places you have to look in when you want to find all uses of it?
To the first question:
By "dependencies kill teams" I mean all the forms of dependencies: Database dependencies, web service dependencies, library dependencies, across teams, across organizations, and so on.
Arguably internal dependencies inside a program are the least lethal. You are largely doing others the favor of implicit documentation by using scope control, a guaranteed statement of "This is never used outside this file". They might be able to figure as much out by grepping, remove & recompile, or IDE-based tools. "Greppability" is often part of the solution when dealing with database & http dependencies.
As for the second question, basically, yes.
By "dependencies kill teams" I mean all the forms of dependencies: Database dependencies, web service dependencies, library dependencies, across teams, across organizations, and so on.
Arguably internal dependencies inside a program are the least lethal. You are largely doing others the favor of implicit documentation by using scope control, a guaranteed statement of "This is never used outside this file". They might be able to figure as much out by grepping, remove & recompile, or IDE-based tools. "Greppability" is often part of the solution when dealing with database & http dependencies.
As for the second question, basically, yes.
The reason is simple: to prevent objects from getting to an invalid state.
The set of property values of an object is its state, and you can model every possible valid state and state transition. Not every state is valid. If you allow all variables to be touched willy-nilly, you allow an object to be put into an invalid state.
The only variables and functions that should be public are those that cannot put an object into an invalid state.
The set of property values of an object is its state, and you can model every possible valid state and state transition. Not every state is valid. If you allow all variables to be touched willy-nilly, you allow an object to be put into an invalid state.
The only variables and functions that should be public are those that cannot put an object into an invalid state.
> The only variables and functions that should be public are those that cannot put an object into an invalid state.
That's an interesting one. So a reason to use private is to tell programmers that calling this function or changing this variable can leave the object in an invalid state? I guess invalid state then means that you can't call a public function on that object.
It might not be clear what an invalid state is though. I mean, every state has its place. Example, after I decrease the health of the player to 0 but before the death animation is played, is that a valid state? I should not be able to play in that state but it's a state that the player might be in for a frame and it will be visible on the screen.
That's an interesting one. So a reason to use private is to tell programmers that calling this function or changing this variable can leave the object in an invalid state? I guess invalid state then means that you can't call a public function on that object.
It might not be clear what an invalid state is though. I mean, every state has its place. Example, after I decrease the health of the player to 0 but before the death animation is played, is that a valid state? I should not be able to play in that state but it's a state that the player might be in for a frame and it will be visible on the screen.
Invalid state means invalid state. Suppose (going with my bounded stack example from another reply to you) you initialize the stack with:
a_bounded_stack = new BoundedStack(100);
There are various actions applied, correctly, through the interface (push, pop, and peek). Somewhere, you decide to manually alter the size of the stack but not through its interface (resize) a_bounded_stack.limit = 50;
But you forget (someone came by with cookies) to actually shrink the backing array and also to change "top". So now you end up with this state: a_bounded_stack
limit = 50
array.size = 100
top = 60
Oops, top is not allowed to be larger than limit, and array.size is supposed to be equal to limit. That is an invalid state, people can still use the public interface (push, pop, peek), but who knows what they'll get back.> But you forget (someone came by with cookies) to actually shrink the backing array and also to change "top".
Someone could come by with cookies while I'm writing a private function too. This is more of a reason for having a function for doing something. Is the reason to make those variables private then to tell the programmer that there is a function that changes them and that you have to look through all the functions of the class before you touch them to see if there is a function that does what you need?
Someone could come by with cookies while I'm writing a private function too. This is more of a reason for having a function for doing something. Is the reason to make those variables private then to tell the programmer that there is a function that changes them and that you have to look through all the functions of the class before you touch them to see if there is a function that does what you need?
[deleted]
If it's intended to be in that state, then it's valid.
But following the example: if you made the player's health a public property, code could straight up just set it to a negative number. The game engine this is running in may not properly support a negative health value. If instead it was set using a public method, there can be added checks to lower bound health to zero. And depending on the architecture, all of the post death animation stuff could also be handled as a check in that function call.
But following the example: if you made the player's health a public property, code could straight up just set it to a negative number. The game engine this is running in may not properly support a negative health value. If instead it was set using a public method, there can be added checks to lower bound health to zero. And depending on the architecture, all of the post death animation stuff could also be handled as a check in that function call.
Ok, let's assume we only care about states that's always invalid, like negative health. So then my question is the same as for Jtsummers: is private a way to tell the programmer to look at all member functions to see if there is already a function that does what you need?
From your other replay:
> Is the reason to make those variables private then to tell the programmer that there is a function that changes them and that you have to look through all the functions of the class before you touch them to see if there is a function that does what you need?
I'm not sure I understand the question.
A private property is a property that a class user should have no reason to want to directly write to. Or more accurately, it's a variable that directly writing to could invalidate the state of the object. They are intermediate values or implementation details that define the state of the object. Reading them should be fine, but allowing unrestricted writing is dangerous. If there is reason to directly change the value, there should be a public method that includes validation to prevent the user from invalidating the object.
On the other hand, private methods exist for code re-use convenience purposes. You could just as easily only have public methods and write all logic inside of them. But a lot of the time you have pieces of code that could be reused between methods, but on its own doesn't guarantee the object to be in a valid state at the beginning or end of the call. So, you make it private. It only exists as a means of avoiding code duplication and possibly improving readability.
Private methods may also have value in inheritance / abstract classes, but I'm focusing on invalid state.
> Is the reason to make those variables private then to tell the programmer that there is a function that changes them and that you have to look through all the functions of the class before you touch them to see if there is a function that does what you need?
I'm not sure I understand the question.
A private property is a property that a class user should have no reason to want to directly write to. Or more accurately, it's a variable that directly writing to could invalidate the state of the object. They are intermediate values or implementation details that define the state of the object. Reading them should be fine, but allowing unrestricted writing is dangerous. If there is reason to directly change the value, there should be a public method that includes validation to prevent the user from invalidating the object.
On the other hand, private methods exist for code re-use convenience purposes. You could just as easily only have public methods and write all logic inside of them. But a lot of the time you have pieces of code that could be reused between methods, but on its own doesn't guarantee the object to be in a valid state at the beginning or end of the call. So, you make it private. It only exists as a means of avoiding code duplication and possibly improving readability.
Private methods may also have value in inheritance / abstract classes, but I'm focusing on invalid state.
> A private property is a property that a class user should have no reason to want to directly write to.
What the user of a class has reason to do depends on what they need to get done. Maybe there is a function that already does what they need to do or maybe there isn't. So the choice is: call a function or write code that directly accesses the variable. This is a choice you would have both for private and public variables. But people say that they make things private because the class user should use already existing functions instead of touching the variable directly if there is already a function that does it. So does that mean that private is a way to tell the class user that there are functions that touch this variable, to increase the chances that the class user finds and uses them?
What the user of a class has reason to do depends on what they need to get done. Maybe there is a function that already does what they need to do or maybe there isn't. So the choice is: call a function or write code that directly accesses the variable. This is a choice you would have both for private and public variables. But people say that they make things private because the class user should use already existing functions instead of touching the variable directly if there is already a function that does it. So does that mean that private is a way to tell the class user that there are functions that touch this variable, to increase the chances that the class user finds and uses them?
> So does that mean that private is a way to tell the class user that there are functions that touch this variable, to increase the chances that the class user finds and uses them?
I would say private is a way to tell the user to not touch the variable, or to use an accessor function that has validation to prevent entering invalid state.
At this point I feel like I'm just repeating myself, as I can't imagine a concrete example of what you're asking. Could you give a hypothetical, or better yet real, example of what you're thinking of here? I can't think of any at all. I felt that the example by Jtsummers was a great illustration.
I would say private is a way to tell the user to not touch the variable, or to use an accessor function that has validation to prevent entering invalid state.
At this point I feel like I'm just repeating myself, as I can't imagine a concrete example of what you're asking. Could you give a hypothetical, or better yet real, example of what you're thinking of here? I can't think of any at all. I felt that the example by Jtsummers was a great illustration.
> I would say private is a way to tell the user to not touch the variable, or to use an accessor function that has validation to prevent entering invalid state.
That's what I meant, that it's a way to tell the programmer that you may not need to touch this variable directly because there are already functions that do that so please look at those functions to see if there is one that does what you need. Private then makes sure that all functions that touch the variable are added to the list because you can't use a private variable in a function not in the list. The list I'm talking about is the function declarations/definitions in the class.
This C++ code doesn't compile because `number` is private by default:
Otherwise, what's the difference between my two examples that makes the second one better than the first one (other than that the first one doesn't compile)? The only difference is that the function has been added to a list inside the class and got a slightly different name.
That's what I meant, that it's a way to tell the programmer that you may not need to touch this variable directly because there are already functions that do that so please look at those functions to see if there is one that does what you need. Private then makes sure that all functions that touch the variable are added to the list because you can't use a private variable in a function not in the list. The list I'm talking about is the function declarations/definitions in the class.
This C++ code doesn't compile because `number` is private by default:
class A {
int number;
};
void zero(A &a) {
a.number = 0;
}
But this works because now `zero` is added to a list of functions inside the class and that way the function is easier to find for someone who wants to modify `number` (assuming the function and the class were not already defined right next to each other): class A {
int number;
void zero();
};
void A::zero() {
number = 0;
}
So the idea is that private can help with reusing code because it makes sure that all functions that modify the variable are listed in the class, which increases the chance of them being used.Otherwise, what's the difference between my two examples that makes the second one better than the first one (other than that the first one doesn't compile)? The only difference is that the function has been added to a list inside the class and got a slightly different name.
I think the disconnect between me and you here is that you're looking at the the "user" ("programmer" in your comment) in our hypotheticals as a person who is going to implement the class. In my mind, they are the person who is using the class. They will not be implementing unimplemented function signatures. In my mind, in my comments, they are library users. I'm thinking of someone who is using std::vector and using std::vector::pop_back instead of trying to access the underlying container, grabbing the value, deleting the value, and setting the private variables to match the new state.
> Otherwise, what's the difference between my two examples that makes the second one better than the first one (other than that the first one doesn't compile)? The only difference is that the function has been added to a list inside the class and got a slightly different name.
The second one is not better than the first one. I would say it's a completely pointless approach to a problem, and would be better served by just making number public. Leaving an empty zero function signature around so someone can come by and fill it in to do what they want serves absolutely no useful purpose at all here. If zeroing out the number property of class A is useful for A, then it should be an already implemented function.
That being said, the example given, in my eyes, is not useful. What is "number"? Why is it private? What does "A" do? What are it's expected states? What are it's failure conditions? If A cannot enter an invalid state by changing number, there is no reason for it to be private.
As I previously said, private exists to prevent objects from entering invalid states from external influence. If A#number has no invalid state, then it need not be private.
> Otherwise, what's the difference between my two examples that makes the second one better than the first one (other than that the first one doesn't compile)? The only difference is that the function has been added to a list inside the class and got a slightly different name.
The second one is not better than the first one. I would say it's a completely pointless approach to a problem, and would be better served by just making number public. Leaving an empty zero function signature around so someone can come by and fill it in to do what they want serves absolutely no useful purpose at all here. If zeroing out the number property of class A is useful for A, then it should be an already implemented function.
That being said, the example given, in my eyes, is not useful. What is "number"? Why is it private? What does "A" do? What are it's expected states? What are it's failure conditions? If A cannot enter an invalid state by changing number, there is no reason for it to be private.
As I previously said, private exists to prevent objects from entering invalid states from external influence. If A#number has no invalid state, then it need not be private.
> They will not be implementing unimplemented function signatures. In my mind, in my comments, they are library users.
I clarified in the question that I'm only asking about situations where everyone has access to all of the code. So are we assuming that the organization is structured so that only one person/team works on a class and that no-one else is supposed to touch it? Or why would the class user not write a new function that accesses the member variables? Is it because the variables are private, which tells them to look at existing functions, just like I described?
> That being said, the example given, in my eyes, is not useful.
Ok, let's do a more useful example, a string class, where we assume that `len` may never be negative (and we assume that there are other string functions defined in the same way as `truncate` to make the class more useful; I just didn't want to write them in this example).
Code 1:
I clarified in the question that I'm only asking about situations where everyone has access to all of the code. So are we assuming that the organization is structured so that only one person/team works on a class and that no-one else is supposed to touch it? Or why would the class user not write a new function that accesses the member variables? Is it because the variables are private, which tells them to look at existing functions, just like I described?
> That being said, the example given, in my eyes, is not useful.
Ok, let's do a more useful example, a string class, where we assume that `len` may never be negative (and we assume that there are other string functions defined in the same way as `truncate` to make the class more useful; I just didn't want to write them in this example).
Code 1:
class Str {
public:
char *data;
int len;
};
void truncate(Str *s) {
s->len = 0;
}
Code 2: class Str {
char *data;
int len;
public:
void truncate();
};
void Str::truncate() {
len = 0;
}
What's the difference between these two and why is code 2 better than code 1? As I currently understand it, the difference is that in code 2 the truncate function has been forced (because of private member variables) to be added to a list of functions inside the class, which makes it easier to find the function for someone who wants to truncate the string and therefore it reduces the risk that someone implements that function again when it already exists.> is private a way to tell the programmer to look at all member functions to see if there is already a function that does what you need?
Private is a way of excluding the member so designated from any consideration at all by a consumer of the class. Assuming that they see it at all (which if it is in a compiled library using normal tooling they might well not at all), they are effectively told to treat it as if it does not exist.
There may exist mechanism to bypass this (especially in dynamic languages and/or those with robust introspection/metaprogramming), but that's breaking the warranty seal on the class.
All the functionality that a class provides to consumers is provided by its public members. That's what public members are.
Private is a way of excluding the member so designated from any consideration at all by a consumer of the class. Assuming that they see it at all (which if it is in a compiled library using normal tooling they might well not at all), they are effectively told to treat it as if it does not exist.
There may exist mechanism to bypass this (especially in dynamic languages and/or those with robust introspection/metaprogramming), but that's breaking the warranty seal on the class.
All the functionality that a class provides to consumers is provided by its public members. That's what public members are.
Encapsulation is a founding principle of OOP. You should not be able to directly tool with a class' internals, but use its methods to interact with it.
The practical reason is that this way you have limited paths (methods) with which the inner workings of a class can be modified with, whereas if anything would be public, any other obejct would be able to change a class' status rendering debugging incredibly more complicaed.
This is also a part of the Open-Closed principle: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification" https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
The practical reason is that this way you have limited paths (methods) with which the inner workings of a class can be modified with, whereas if anything would be public, any other obejct would be able to change a class' status rendering debugging incredibly more complicaed.
This is also a part of the Open-Closed principle: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification" https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
> Encapsulation is a founding principle of OOP.
The more fundamental principle that predates OOP is: Abstraction
https://en.wikipedia.org/wiki/Abstraction_(computer_science)
The more fundamental principle that predates OOP is: Abstraction
https://en.wikipedia.org/wiki/Abstraction_(computer_science)
>I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use
Even within a single module of code, your public interfaces are "APIs". If I write a class that displays a paged interface in a popup modal, there is no relevant difference between a member of my team utilizing that class (from within the same module) and other people using it (as a library I have published). They both read the documenting comments and access the public members to achieve their goals, and both will suffer from the same confusion if there is no way to tell which members are the interface and which members are implementation details.
Even within a single module of code, your public interfaces are "APIs". If I write a class that displays a paged interface in a popup modal, there is no relevant difference between a member of my team utilizing that class (from within the same module) and other people using it (as a library I have published). They both read the documenting comments and access the public members to achieve their goals, and both will suffer from the same confusion if there is no way to tell which members are the interface and which members are implementation details.
> there is no relevant difference between a member of my team utilizing that class (from within the same module) and other people using it (as a library I have published).
The difference between a team member and a library user is that the team member works in the same project while library users have their own projects. So if you change something inside the class, you can change all the uses of that class in your project but you can't change other people's projects. That's why I added the clarification, because there is a very obvious reason to be strict about the API of a library that other people use: you can't change those uses.
The difference between a team member and a library user is that the team member works in the same project while library users have their own projects. So if you change something inside the class, you can change all the uses of that class in your project but you can't change other people's projects. That's why I added the clarification, because there is a very obvious reason to be strict about the API of a library that other people use: you can't change those uses.
When I write a private class, I'm declaring to the world:
These are the methods now available, their expected inputs, and their return types.
Anything I do inside that class that isn't marked public is an implementation detail, and might change. Given time, likely will change! But if it's not marked public, then if you want to access something in there, you'll need to think through the implications of making it public, either a new method of by adding to an existing method.
If I come along later and want to change things about those methods, I can do that freely, so long as I maintain the same input types and return types.
Internally I can change a counter from an Int to an Long, and it doesn't matter to anybody. Internally I can change the way I'm assembling a String, and that's fine. Internally I can move the definition for an interim variable to a separate function with its own tests, and that's fine. So long as I maintain the input types and return types, all is good.
If the fields and methods were public, on the other hand, then other classes might be accessing them directly, and now any change has to be negotiated with all callers. The counter needs to be changed from an Int to a Long? If it's public, you'll need to make sure that no other classes anywhere are referring to that counter and expecting it to be an Int.
Side effects are generally unwanted, and marking things as public is making everything in an implementation a side effect.
Anything I do inside that class that isn't marked public is an implementation detail, and might change. Given time, likely will change! But if it's not marked public, then if you want to access something in there, you'll need to think through the implications of making it public, either a new method of by adding to an existing method.
If I come along later and want to change things about those methods, I can do that freely, so long as I maintain the same input types and return types.
Internally I can change a counter from an Int to an Long, and it doesn't matter to anybody. Internally I can change the way I'm assembling a String, and that's fine. Internally I can move the definition for an interim variable to a separate function with its own tests, and that's fine. So long as I maintain the input types and return types, all is good.
If the fields and methods were public, on the other hand, then other classes might be accessing them directly, and now any change has to be negotiated with all callers. The counter needs to be changed from an Int to a Long? If it's public, you'll need to make sure that no other classes anywhere are referring to that counter and expecting it to be an Int.
Side effects are generally unwanted, and marking things as public is making everything in an implementation a side effect.
If the exposed data is (and will always be) the same as the stored data, and the data is immutable - then you are right there is little point.
If the field is mutable then obviously you may want to keep it private to ensure some invariant, or to cache/invalidate/log something when accessed. Accessing the private data directly will circumvent that.
If the exposed data isn’t the same as the stored data (e.g a DateTime struct might have a field with a 32 bit Unix timestamp but only exposes year/month/day/hour…). The reason for not exposing it because you don’t want consumers to depend on it. If you changed to a 64 bit internal representation you will break any consumer who is using the private field. But if you only expose year/month/day/hour/… then you can solve your year-2038-problem by simply changing the data type. Anything exposed is depended on. Always. And here is the kicker: it doesn’t matter if your consumers are other classes in the same program, or library users for a shipped api, they are users and they will (mis)use your class in any way possible. In the library api case you break others’ code. In the internal code case you might get a missed cache, or a harder refactor or similar.
If the field is mutable then obviously you may want to keep it private to ensure some invariant, or to cache/invalidate/log something when accessed. Accessing the private data directly will circumvent that.
If the exposed data isn’t the same as the stored data (e.g a DateTime struct might have a field with a 32 bit Unix timestamp but only exposes year/month/day/hour…). The reason for not exposing it because you don’t want consumers to depend on it. If you changed to a 64 bit internal representation you will break any consumer who is using the private field. But if you only expose year/month/day/hour/… then you can solve your year-2038-problem by simply changing the data type. Anything exposed is depended on. Always. And here is the kicker: it doesn’t matter if your consumers are other classes in the same program, or library users for a shipped api, they are users and they will (mis)use your class in any way possible. In the library api case you break others’ code. In the internal code case you might get a missed cache, or a harder refactor or similar.
Suppose all fields are public, but the legal value of a field is dependent upon others. Then every time you change it you also have to include all the invariant preserving logic. This is duplication of the insane sort, totally unnecessary and gratuitous and gloriously error prone (at scale). The sane option is to make it private and have one place that checks that the input is valid and have everything make use of that.
> I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.
It's for the same reasons. When you write code you are always writing code that some other human will consume through an interface of sorts. Even on small three person projects two truths hold:
- Your project mates are other humans.
- Your future self is an other human.
By defining members as private you're announcing in code to other humans that the private members are important only to the internal implementation of the class and should not be used elsewhere. And yes, your future self with thank you for the distinction.
Now, you don't need to have private methods. In some languages, a work around is to label the methods as "private" by adding a prefix of some sort like an underscore.
Another more functional reason has to do with inheritance. Private methods have different behaviors in different languages when inherited.
It's for the same reasons. When you write code you are always writing code that some other human will consume through an interface of sorts. Even on small three person projects two truths hold:
- Your project mates are other humans.
- Your future self is an other human.
By defining members as private you're announcing in code to other humans that the private members are important only to the internal implementation of the class and should not be used elsewhere. And yes, your future self with thank you for the distinction.
Now, you don't need to have private methods. In some languages, a work around is to label the methods as "private" by adding a prefix of some sort like an underscore.
Another more functional reason has to do with inheritance. Private methods have different behaviors in different languages when inherited.
I too had that same question in the past, and the answers I got for variables were that by using them directly was harder if in the future you wanted to change the "how things are stored" in the class. With nowadays ides it's as easy as a refactor, so not a problem I personally ever had.
For personal projects I do use public things where necessary, and for team programming I also use public functions, but not public variables (sonar doesn't allow it) unless you make them final, which I do.
The important part is "when necessary". Think that each class is like a small little library (it should be at least). There are things you want others to know and use, and other internal things you don't. Public for the first, private for the second.
For personal projects I do use public things where necessary, and for team programming I also use public functions, but not public variables (sonar doesn't allow it) unless you make them final, which I do.
The important part is "when necessary". Think that each class is like a small little library (it should be at least). There are things you want others to know and use, and other internal things you don't. Public for the first, private for the second.
This reads more like a question for Stack Overflow or https://softwareengineering.stackexchange.com/ than HN, to be honest.
You sound like a junior developer looking for guidance.
You sound like a junior developer looking for guidance.
Not for safety, gave up on that about 20 years ago. But definitely to keep state that I don't want to get messed up by outside calls. Also just to document/signal how an interface should get used to the outside world.
As software projects grow, development really becomes an exercise in managing complexity. One of the benefits of encapsulating state and functionality into classes is to reduce the amount of coupling that can occur between components. By coupling, I mean the dependence of one component on the specific behavior or implementation details of another component.
There will always be some amount of coupling, but reducing it makes it much easier to reason in your mind about small sections of code. With good encapsulation, there are clear interfaces between components and it is difficult to use them incorrectly.
You could argue that if there are only a few or even one developer on a project, there is no harm in making everything public since everyone will just use the class "correctly". However, the "correct" way to use the class is not codified or enforced anywhere other than the minds of the developers, comments, or external documentation. All of these sources can easily fall out of sync with the actual code being written. The compiler/runtime should be used to enforce correct usage when possible. This greatly reduces the cognitive load on the developer/s since they know if the code compiles or runs without error, a whole class of bugs has already been eliminated.
I would argue that even if access controls were completely ineffective (they didn’t actually control access), they would still be useful as API documentation within the source code to point others and your future self to the set of variables and functions that should be used to interact with the class. There is a benefit to writing code that is itself expressive of intent without requiring additional documentation or knowledge.
Another reason to reduce coupling is to make it easier to re-implement a single component of the system. If the component was well encapsulated and has a clean and minimal public interface, the only restriction on the new implementation is to meet that same public interface. If the component was not well encapsulated, the new implementation may have to maintain a number of details from the previous implementation than no longer make sense just to maintain all of the unnecessary coupling that’s been created between the component and the code that uses it.
There will always be some amount of coupling, but reducing it makes it much easier to reason in your mind about small sections of code. With good encapsulation, there are clear interfaces between components and it is difficult to use them incorrectly.
You could argue that if there are only a few or even one developer on a project, there is no harm in making everything public since everyone will just use the class "correctly". However, the "correct" way to use the class is not codified or enforced anywhere other than the minds of the developers, comments, or external documentation. All of these sources can easily fall out of sync with the actual code being written. The compiler/runtime should be used to enforce correct usage when possible. This greatly reduces the cognitive load on the developer/s since they know if the code compiles or runs without error, a whole class of bugs has already been eliminated.
I would argue that even if access controls were completely ineffective (they didn’t actually control access), they would still be useful as API documentation within the source code to point others and your future self to the set of variables and functions that should be used to interact with the class. There is a benefit to writing code that is itself expressive of intent without requiring additional documentation or knowledge.
Another reason to reduce coupling is to make it easier to re-implement a single component of the system. If the component was well encapsulated and has a clean and minimal public interface, the only restriction on the new implementation is to meet that same public interface. If the component was not well encapsulated, the new implementation may have to maintain a number of details from the previous implementation than no longer make sense just to maintain all of the unnecessary coupling that’s been created between the component and the code that uses it.
I see a class as the implementation of an interface. The "interface" part of the class should be public, while the "implementation" part of the class should be private. That way, if you ever have to refactor or change the class, you can be confident your colleagues haven't built stuff on top of the "implementation" part of the class, and that you can change whatever as long as you maintain the same interface.
[deleted]
A class always has an API and an internal implementation. Sometimes, it is necessary to split some of that implementation up (for, e.g., code reuse) into methods or properties that have no purpose in the API, and making those methods/properties private prevents them from becoming accidental parts of the API.
This applies to classes without external users, too, the same divisions between class responsibilities and implementation remains valod
This applies to classes without external users, too, the same divisions between class responsibilities and implementation remains valod
To keep narrow waists between your software layers.
A low surface area API means clean, elegant code that is simpler to use and understand.
>To clarify, I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.
The concept of API still applies to same codebase modules. Ditching it is the shortest path to spaghetti code.
A low surface area API means clean, elegant code that is simpler to use and understand.
>To clarify, I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.
The concept of API still applies to same codebase modules. Ditching it is the shortest path to spaghetti code.
I don’t want them to show up in auto complete and accidentally use them because its not my business, I should not use things that I shouldnt.
I minimize the externally visible names even/especially when I'm the only programmer on the project.
I do that so that "future me" has as little to understand as possible. ("reduce the number of files to search" seems silly in a world with filename wildcards and find. Are "modern" tools less capable?)
I do that so that "future me" has as little to understand as possible. ("reduce the number of files to search" seems silly in a world with filename wildcards and find. Are "modern" tools less capable?)
Like alot of written code that doesn't really matter to the computer, I do it as a signal to the reader: you _shouldn't_ be messing with these variable/methods just to use the class. As often noted, someone can and will still go mess with them if they want to, but at least now it's obvious.
Changing public things means changing the API. It means having to update documentation and, in case of breaking changes, major version bumps and maybe even other communication.
Changing private things and nobody might ever know.
It's about the need to coordinate and communicate changes and avoid people relying on unspecified behavior.
Changing private things and nobody might ever know.
It's about the need to coordinate and communicate changes and avoid people relying on unspecified behavior.
Intent.
It says "don't call me". That might be because it might be refactored without notice, or might break the intended use of the Class.
Even in languages without formal class access control, there is convention for labelling something as externally unsafe. It's an important part of sharing code.
It says "don't call me". That might be because it might be refactored without notice, or might break the intended use of the Class.
Even in languages without formal class access control, there is convention for labelling something as externally unsafe. It's an important part of sharing code.
Because I'm only interested in the minimal functionality to use a class. That's it.
I don't care and I shouldn't care about the dependencies or statefulness. I'm interested in the before-and-after of using the class methods.
I don't care and I shouldn't care about the dependencies or statefulness. I'm interested in the before-and-after of using the class methods.
I advocate for private to avoid namespace pollution and reduce cognitive load. It reduces the size of a given namespace because simply less appears when I auto-complete.
It lowers cognitive load because it provides demarcation of inside vs outside.
It lowers cognitive load because it provides demarcation of inside vs outside.
Because they are for the use of the functions within the class/module/whatever, not for the use of functions interfacing with it. Private functions for me are mainly for DRY, not for being secret special menu items.
-To hide implementation details from users of the library. We don't want to unnecessarily expose functions
-Reduce the amount tests, as the private functions wouldn't have to be explicitly tested
It probably also lets the compiler do more optimizations than it otherwise would've, but that's just a guess and largely compiler dependent.
Outside of library usage though? Mostly counterproductive in my experience.
Outside of library usage though? Mostly counterproductive in my experience.
Marking it private doesn't allow any subclasses to change the value directly and must use the get/set methods provided where you can enforce standards or format accordingly.
To enforce a public API. I separate it so that callers don't accidentally muck up the workings of the class, and it simplifies how a class can be accessed and mutated
Because if someone is reading or modifying a value where that never makes sense, it indicates a bug. Compilers and IDEs catching bugs automatically is always the ideal.
Enforce invariants
I used to believe in information hiding, but not anymore.
I used to believe in information hiding, but not anymore.
Easy way to avoid this problem: don't use classes.
This idea is not limited to OO systems and classes, "private" visibility is very useful in languages that make use of modules as well.
To me the point is that public members define the API.
[deleted]
Variables should not be accessed directly outside of the object
(encapsulation principle) and thus should all be private as a general rule.
Then, you will probably divide up the code into a number of separate methods for better structured code. All the methods that are purely internal and not part of the public API should be private if only to enforce the public API, this is also the encapsulation principle.
Then, you will probably divide up the code into a number of separate methods for better structured code. All the methods that are purely internal and not part of the public API should be private if only to enforce the public API, this is also the encapsulation principle.
Python would like a word...
While we're on Python and private methods - what's with everyone just _underscoring every method and variable by default? Does anyone teach people to do this or have I just ran into a few people with this habit by chance?
I know what _underscoring does in Python and what PEP8 recommends but doing it all the time for everything is so ugly and unnecessary.
I know what _underscoring does in Python and what PEP8 recommends but doing it all the time for everything is so ugly and unnecessary.
I think it's because of what underscoring does. It's the "fake" privacy in Python. Also autocompleters tend to ignore those functions
Probably for the same reason that zero-based versioning (https://0ver.org/) is so popular. People want to avoid commitment.
Perhaps you worked with Java/C++ programmers using Python.
The language is not relevant to the question.
Making variables and methods private means making them inaccessible outside the class and I gave the typical reasons/best practices for that.
Making variables and methods private means making them inaccessible outside the class and I gave the typical reasons/best practices for that.
> Making variables and methods private means making them inaccessible outside the class and I gave the typical reasons/best practices for that.
The point they are making is in python there's no private method, everything is public. There's only a convention of "avoid using anything starting with "_"
The point they are making is in python there's no private method, everything is public. There's only a convention of "avoid using anything starting with "_"
Yes but that point is irrelevant to the question...
Why do you write functions?
Should the implementation of a module not be able to utilize functions internally?
Should the implementation of a module not be able to utilize functions internally?
We do it because that's how it's always been done. There is no why of OOP, there is only do.
> There is no why of OOP, there is only do.
You really think that in the 50+ years of OOP and modular programming no one has considered why? That they've really just thrown shit at the wall and seen what stuck and never considered what to throw at the wall or why it stuck?
You really think that in the 50+ years of OOP and modular programming no one has considered why? That they've really just thrown shit at the wall and seen what stuck and never considered what to throw at the wall or why it stuck?
I’m sure lots of people have considered it. But lots of people can be wrong, myself included.
We do it because it’s largely automated by IDEs now. I strongly suspect that if IDEs didn’t make it so easy to do, we basically wouldn’t bother with it. Python does OOP just fine without real visibility control.
We do it because it’s largely automated by IDEs now. I strongly suspect that if IDEs didn’t make it so easy to do, we basically wouldn’t bother with it. Python does OOP just fine without real visibility control.
I want to know the main reasons for making variables and functions in a class private. How is it better? What can happen if you don't do it?
Here are a few possible reasons that I can think of that someone could have:
* You have been taught to do it so you just do it without thinking.
* It reduces the number of files you have to search in if you want to find all uses of a member.
* The member is hard to understand so you want to discourage people from using it.
To clarify, I'm only talking about code in the same project that everyone has access to. I'm not talking about defining an API for other people to use that don't have access to the code, like when you make a library.