Access modifiers and interfaces serve different purposes in an object-oriented language. An interface is used in cases where you desire loose coupling and polymorphism. Access modifiers are used to promote data hiding and encapsulation at the compiler level.
The critical flaw with using interfaces for data hiding and encapsulation is revealed when down-casting an interface to a concrete class. Most OO languages allow you to do this:
public interface IFoo
{
void Something();
}
public class Foo : IFoo
{
public void Something() { }
public void DoNotCallMe() { }
}
IFoo foo = new Foo();
foo.Something(); // <-- "allowed" by the interface
Foo bar = (Foo)foo; // <-- cast IFoo down to concrete Foo
bar.DoNotCallMe(); // <-- this compiles and executes at runtime
This allows code to circumvent restrictions provided by the interface if you are willing to risk a runtime exception. Most programmers, since it compiles, will ignore the runtime risk, because they got something to work.
Access modifiers prevent unauthorized access at compile time. If you mark a method or field private
, even down-casting from an interface to a concrete class prevents careless or malicious programmers from using something inappropriately.
A word of caution about languages that support class reflection. You can still circumvent private access modifiers in many languages, but it is a much bigger pain than simply down-casting an object.
Solution: trust that a developer who is using class reflection has a valid use case for doing so (see Object-Relational Mappers). If you do not trust the developer, do not hire them.
Also, a word of caution when using compilers that allow you to turn off access modifiers. Access modifiers are not magic. They are enforced by the compiler, so if you tell the compiler to not enforce this restriction, then you can circumvent those restrictions.
Solution: never compile your application using a compiler you do not trust, and never have someone compile your application if you do not trust them.
private
, to be able to hide implementation details from things that subclass you, unless you remove subclassing.private
is the most important access modifier. You don't need to directly test private code. You test the reachable private code, as part of testing the public interface, and it's perfectly fine for any unreachable private code to have undefined behaviour.private
in your scheme