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).
`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.
Note that only a consumer of your apis can evaluate how well designed they are.
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.
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
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.
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.
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.
> 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.#include "shoot.hpp"
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.
Composition is great. It's the preferred way.
However, if you take it to the extreme (composition only, never inheritance) you sometimes end up with really weird meaningless objects that have only a single method and a one-to-one relationship with another object.
Private methods are ideal for making those abstractions easier to understand. A private method, therefore, is equivalent to an object that has a one-to-one relationship with another object (it's only composed there) and a single method.
A protected method is similar, but allows whoever subclasses it to replace that single one-to-one "mini object" inside with another one-to-one "mini object".
When used within this mindset, it greatly simplifies code. It lets you simplify a branch that has only a single leaf into something easier to read.
Together with classical refactorings (extract method, move method, extract class), they're a precious tool.
Of course, typing is a consideration as well, but I'm talking exclusively about their role in granularity control.
It is unecessary, but convenient. It can be misused. Composition can also be misused.
We can take a cue from the functional languages and make this less code-smelly by using anonymous function types (e.g. lambdas). This removes the one issue with single-purpose/single-method objects, which is naming them.
There is no silver bullet. No one likes huge inheritance chains, no one likes only tiny lego pieces, no one likes callback hell.
While true that all classes can be inherited, treating _all_ classes with the assumption that they will be would just lead to a labyrinth of code.
> While true that all classes can be inherited
That's what the 'final' keyword in C++ is for: https://en.cppreference.com/w/cpp/language/final.html
"Specifies that a virtual function cannot be overridden in a derived class, or that a class cannot be derived from."
In practice? abstract classes all the way down to technical debt hell.
If you don't have a mechanism to indicate which parts of your software's API are internal implementation details and which parts are intended for use by users of that software, then you're going to have to rely on convention. The problem with convention is that no ten people do it the same way... which increases cognitive load... which is to be avoided in a field that has more than enough little things to keep track of already.
"Stuff that starts with one underscore is an internal implementation detail, use at your own risk" in Python is as close to a universal convention as you can get.
It should at most be a soft annotation to prevent it from getting auto-completed, not a restriction.
• There are also runtimes, installed separately from the application. You might be able to fork the JDK, but people want to run your Java application on their JRE, not yours.
• There are proprietary applications that nevertheless expose APIs to code against. A Photoshop plugin author can't fork Photoshop to expose fields of the plugin host.
• There is the OS itself. You can fork the Linux kernel (and gcc/clang) to expose some internal data your application needs as a system call — but unless you get all that code upstreamed, your application is now entirely non-portable, running only on your own servers at best.
• Most commonly of all, there are system libraries. You can certainly fork (and co-distribute / bake in) a library like libicu or libxml — but no major OS will be willing to ship your app or lib as a system package of its own, if it does that. They'll want your app/lib to depend on the existing packaged version of those libs.
None of this matters, of course, if you develop proprietary-service backend software for a living. You can maintain a menagerie of weird lib forks, shove all the build artifacts into a docker image, and it'll work for you. But much (I'd say the majority) of the world's programming is not in proprietary-service backends, and so needs to actually depend on code it doesn't control.
More power to them. They can take responsibility for that code and maintain it and I don't have to worry about breaking them when I release a new version. Everyone's happy.
Then the internals are accessible if you need it for a temporary workaround or such, but it's also very obvious when you are accessing stuff you really shouldn't. And implementers are still free to rearrange the internals as they see fit.
[1]: Not saying Boost invented it, just where I saw it first.
I can't imagine somebody monkey patching exposing internals of a lib and blaming lib author for poor effects.
[0] Good luck with enforcing the One True Naming Convention.
It can still be using keywords like public, protected, private. Just make them non-binding. In similar manner how TypeScript types are non-binding. You can ignore them in any program that uses TS lib.
Yeah, the users aren't likely to be really limited these days.
1) A huge number of widely-used languages have reflection, so you can get at internal members if you really try.
2) For those languages that don't, just remove the keyword and recompile. Someone who's willing to take on the maintenance burden of directly using parts of the software marked by their author as "internal only do not use" is clearly willing to maintain such a minor fork.
Also, you seem to assume we're talking about open source here.
class Foo {
public:
void method_A();
protected:
friend class Bar;
void method_B();
protected:
friend class Baz;
void method_C();
private:
void method_D();
};
anyone can call method_A(), only Bar can call method_B(), only Baz can call method_C() and nobody can call method_D().You can do this by inheriting from 2 different pure abstract classes, but that feels much kludgier to me.
Unexported package symbols do two things:
- when visibility "windows" are created between packages via the "use" mechanism, unexported symbols are excluded.
- unexported symbols require tokens with two colons: you have to write foo::bar, not just foo:bar, to refer to bar if it is unexported from package foo; moreover you must still write foo::bar even if you are using package foo, whereas if it were exported, you would just write bar.
CLOS uses symbols for naming entities like generic functions, classes and slots. It has no additional protection mechanisms beyond what the symbols provide, and symbols are agnostic of classes (and everything/anything else which they name).
Languages like Python don't have keywords but enforce using conventions like underscore and 'dunder', the latter of which is actually enforced using obfuscation. It is extremely helpful to signal to users that they should not be using certain methods or fields. When there are no access modifiers available, we still see teams writing methods like "xyx_UNSAFE" or "abc_DO_NOT_USE". It's ridiculous to me to encode this kind of information like this instead of having first class support in the language.
Access modifiers also tell the compiler or JIT what is safe or unsafe to manipulate. It has implications beyond users, and can be a determining factor when inlining or making other optimizations.
This article is so opinionated (edgy?) that it ends with "Inheritance was a hack in the first place." I think, like all things, we need to consider the broader landscape for object oriented programming instead of just saying these things are irrelevant and absurd and useless.
A long time ago in Java corp world, teams would create their own integration libraries with suspicious/horrible contracts. Ended up just using Java’s reflection api to update access modifiers to get their shitty library to work until they fix it (months later…).
See also https://steve-yegge.blogspot.com/2010/07/wikileaks-to-leak-5...
I'd expect any language that uses annotations to have a fairly-complete reflection capability. I'd also expect a good debugger to be able to use that to permit me to read, modify, and call methods and members marked as 'private'.
It has been decades (so perhaps my memory is incorrect), but I remember being able to access private parts in C++ debuggers. If this was true way back when for C++, I expect it to be a common feature now in debuggers for languages that have reflection & etc.
An awful lot of my debugging is done without a debugger. I generally expect a language to permit me to do anything that can be expressed in a logically coherent manner even when I would clearly be shooting myself in the foot. I also expect a bright neon sign warning me not to proceed.
I quite like python in that regard. You can monkey patch just about anything even when you really shouldn't.
> An awful lot of my debugging is done without a debugger.
I would like to hear more about this. What are the alternatives? printf? To me, nothing beats a good debugger.I always start debugging by coming up with a theory about why the observed behavior occurred. The fastest way to validate that is generally one or two tiny changes to the program.
I only break out the debug tooling for the serious stuff that has me scratching my head in confusion. Either because the behavior has me thinking "there's no way that's possible" (spoiler: almost always memory corruption) or because I'm segfaulting in a shared library that I didn't compile or because I'm well into some obvious badlands with concurrency or memory corruption or the like.
"I don't like making functions, methods, and members nonpublic because how would I access them when debugging?" simply isn't a credible complaint when applied to most mainstream languages.
Brilliant steve yegge article on the topic.
Wikileaks To Leak 5000 Open Source Java Projects With All That Private/Final Bullshit Removed
On a more serious note. While I can see the reason for private members, the case for private methods has always been much weaker.
With inheritance, you’re breaking some level of encapsulation by choice; it needs to be done carefully.
Likewise, the base class needs to be designed thoughtfully.
No thanks. And I'll continue using inheritance and composition correctly too.
There are desktop GUI libraries implemented in C, Go, and other languages that do not have subclassing, so, the answer kind of has to be yes.
I wonder if there are any modern UI toolkits that do not use subclassing at all -- actual (C++/Java/C#) or synthetic (C). It would be such a PIA not to inherit from a base widget that handles most basic operations. Maaaaaybe it could be done purely with encapsulation... But, oh brother, that would a lot of boilerplate code!
There is plenty of evidence that OOP provides some flexible, extensible, and intuitive ways to design GUI libraries.
I have personal experience that OOP can be the better approach in other fields too (such as overriding a specific method of a larger structure).
And if you don't need private then why have public?