Apologies for the unoriginal OOP example, but: Suppose class Car implements interface Vehicle. Regular code using this interface works great: Users can write generic code that works for any Vehicle,…
make_car
with return type Vehicle. Since Car can't restrict subclasses to only use an interface, any subclasses can violate Car's internal invariants.
So either Car should disallow inheritance (always an option by simply not exposing the Car type at all, as in point 2), or Car needs another way to define an interface that will take effect on subclasses.
That other way to define an interface which works for subclasses is the access modifiers: public, protected, and private. By marking members as private, Car can do something like define an interface which affects subclasses; this pseudo-interface contains exactly and only those members of Car which are public and protected.
But this is absurd: Why do we have two ways to define an interface?
Wouldn't it be better for Car to be able to force subclasses to only use Car through a specific (normal) interface? There'd be no loss in functionality; Car could still have separate interfaces for instantiators and subclasses. We'd just define those interfaces with the same mechanism, instead of using one mechanism for instantiators and another for subclasses.
Access modifiers were originally invented in Simula. As far as I can tell from extensive research, the inventors and users at that time simply didn't realize that access modifiers duplicated the interface-defining features that were already available: virtual methods and subtyping, which together are sufficient to define interfaces in Simula.
It's important to realize that, in the absence of inheritance, access modifiers provide no additional implementation-hiding power if you have interfaces. Remember point 2 from above; we don't need protected/private to hide the implementation details of a class.
Access modifiers were useful in Simula, of course, given that Simula invented and heavily used inheritance, so they needed to find some way to protect the implementation internals of base classes.
But other means could have been used, which reused the existing interface abilities of Simula. For example, Car could specify in the definition of Car that when Car is subclassed, only the fields declared in the base class Vehicle are available to the subclass.
Alas, as far as I can tell, they just simply didn't realize the possibility. And thus we've ended up with the unnecessary duplicate feature of access modifiers, carried on into language after language for the sake of backwards-compatibility.
Of course, we can still make things better in the future, by just not using access modifiers. If your class isn't available to be subclassed (say, because it's final), and you're restricting construction of it as in point 2 above, then there's absolutely no need to annotate it with access modifiers. Protection of the internals of the class can, and should, be achieved through defining an interface.
If you do really want to inherit from a non-abstract class... well, don't. Use composition instead. Inheritance was a hack in the first place.
I primarily work in C# and access modifiers allow me to constrain which types or their members on it are accessible to other types or assemblies.
This is particularly useful when authoring libraries as I know that for anything not public, I can refactor to my hearts content (provided the externally observed behaviour is otherwise unchanged).
It’s a bit of a pity that it was only relatively recently that Visual Studio changed the template for new classes to use internal instead of public.
There are lots of public classes in codebases I work on which are probably only public due to the old default and not used externally so could be refactored, but it’s hard to be sure.
Whereas with internal classes I can be sure (unless someone has used reflection, but then that’s their problem).
I also wish 'sealed' was the default for classes in templates. Usually you're not thinking about what will happen if someone shows up 5 years later to derive from a random class you created, and it has performance benefits to seal a type. You can always remove the sealed qualifier later on purpose.
Same here. I recently came across a real use for the newer `private protected` (`FamANDAssembly`) modifier combo. Only derived types that are internal to my assembly can access this member. Beautiful. It re-confirmed my long standing belief that OOP is an incredibly powerful and valid pattern.
`internal` maps to `pub(crate)`. But there's no analog for `protected` because Rust doesn’t have inheritance.
`private protected` means "only me or anyone inheriting me in this assembly". There's also `protected internal` which means "me, anyone inheriting me, and anyone inside this assembly". An assembly is analogous to a Rust crate.
It's telling that you worded this as a writer of an API, not as a consumer.
Note that only a consumer of your apis can evaluate how well designed they are.
Yeah, I am sure that some fo the users may have higher skill set than authors. But majority of users suck at designing. And would add more fuel to the problems. And any refactoring efforts
I think this is not about your skill set, this is about use cases that the author of the library could not anticipate.
Yes, it does matter even in functionally pure languages.
If the inner structure of your API's types are visible to callers, that's now part of your API, because callers can and will start to rely on that specific representation. Now you're constrained from updating your API, in order not to break code which depends on it.
Tell me you haven't written a large library for a wide array of users (many of which you wouldn't trust with a potato peeler) without telling me
I agree that it's unnecessary, and Python's success despite not having private members/fields is testament to that. But public/private fields/methods, like pretty much everything in OOP, is a 'social' rather than engineering innovation. Just as a class tells you certain operations and data are inter-related, private methods or fields signal to a class's users that you probably will break things if you interfere with certain parts.
Otherwise, at least in C++, you can often bypass the private specifier without much difficulty. Perhaps the laziest and easiest way to do so is
#define private public
#include "foo.hpp"
#undef private
You should safely shoot yourself in the foot with `friend`, which is a core language feature for accessing private members/functions/etc.
https://www.cppreference.com/w/cpp/language/friend.html
Redefining public as private can violate the ODR rule and triggers undefined behaviour because private members are ordered in memory in an undefined way, while public members are ordered in a standard way.
Python absolutely does have private members. If a member starts with two underscores, its class name (and an underscore) is prefixed to the member name, essentially resulting in the same behavior as a private modifier. (Although there are ways to circumvent this in Python, there are also ways to access private fields in most other languages.)
But even if that were true, that wouldn't mean that accessibility modifiers are unnecessary in other languages. Python is a certain language with certain idioms, and other languages are more or less OOP and hence some constructs that might not make sense in Python definitely make sense elsewhere.
No, it doesn't: double underscore field names are mangled basically to avoid easy overriding in su classes; keep in mind that it is a feature that predates super() in almost any form
I see what you mean what I wouldn't really qualify name mangling as private members, you're discouraged to access them but you can access them all the same.
I'd consider it the same, personally. It's a social contract that you can absolutely break if you want, just with a bit of work.
Less work in Python than it is Java, but there are ways to get to them in Java, or hell, just modify the source and change it directly. By doing that, you're doing something the developer didn't intend and you shouldn't be surprised if it doesn't work at some point in the future. Same as I'd expect in the Python case.
Sure, I think the bar to modify is higher in Java for example but I can't really find fault in what you're saying because I'd never unknowingly modify a member with double underscores in Python.
> but you can access them all the same
Can't you do the same in C++ using struct offsets and reinterpret_cast<>? I'm not sure what you are saying here.I'm not familiar with C++ enough to know about circumventing `private` but in Python you can access "private" members just like any other "public" ones, with the dot notation. There's no specific enforcement at the language level, no error will be thrown.
Something something undefined behavior
Not only are those not technically private, they were never intended for information hiding in the first place. Name mangling was introduced to avoid namespace conflicts in inheritance scenarios.
That is absolutely a social construct and not a language feature. You can override “private” members just fine with a simple assignment.
#define duck_season wabbit_season
#include "shoot.hpp"
The #define private public hack doesn't work reliably in modern C++ compilers and violates the One Definition Rule, potentially causing undefined behavior and hard-to-debug errors.
Actually, it won't compile in msvc (the linking phase, to be precise), MSVC uses different names for methods with different visibilities
I’ve seen a ton of this “#define private public” in a previous team that was forced to write unit tests in a rush due to the whims of leadership. It hurt to see, and the resulting white box tests were useless.
That #define trick is not valid due to how C++ groups members of different visibility classes together. See e.g. https://stackoverflow.com/a/36149568 It will work until it doesn't.
Don't do that in C++. Maybe unless if you are debugging. This can blow up in your face in unexpected ways.
Pretty much all language features save for a shockingly small set are "unnecessary". The proper question is "are they useful?"
The usefulness of public/protected/private is that it provides a way to narrow the surface area through which derived classes can interact with their superclasses. Which is enormously useful from a maintenance perspective.
The place where inheritance is better than composition is where base classes provide partial implementations that are used by derived classes.
Exactly: "Loops are an unnecessary feature"