44

When reading various Stack Overflow questions and others' code the general consensus of how to design classes is closed. This means that by default in Java and C# everything is private, fields are final, some methods are final, and sometimes classes are even final.

The idea behind this is to hide implementation details, which is a very good reason. However with the existence of protected in most OOP languages and polymorphism, this doesn't work.

Every time I wish to add or change functionality to a class I'm usually hindered by private and final placed everywhere. Here implementation details matter: you're taking the implementation and extending it, knowing full well what the consequences are. However because I can't get access to private and final fields and methods, I have three options:

  • Don't extend the class, just work around the problem leading to code that's more complex
  • Copy and paste the whole class, killing code reusability
  • Fork the project

Those aren't good options. Why isn't protected used in projects written in languages that support it? Why do some projects explicitly prohibit inheriting from their classes?

TheLQ
  • 13,478
  • 7
  • 55
  • 87
  • 1
    I agree, I have had this problem with Java Swing components, it's really bad. – Jonas Jul 12 '11 at 18:08
  • 20
    Oblig.: http://steve-yegge.blogspot.com/2010/07/wikileaks-to-leak-5000-open-source-java.html – Shog9 Jul 12 '11 at 19:16
  • 3
    There is a good chance that if you are having this problem the classes were designed poorly in the first place--or perhaps you are trying to use them incorrectly. You don't ask a class for information, you ask it to do something for you--therefore you should not generally need it's data. Although this isn't always true, if you find you need to access a classes data a lot chances are something has gone awry. – Bill K Jul 12 '11 at 21:15
  • 2
    "most OOP languages?" I know a lot more where classes can't be closed. You left out the fourth option: change languages. – kevin cline Jul 12 '11 at 22:18
  • @Jonas One of Swing's major problems is that it exposes way too much implementation. That really kills development. – Tom Hawtin - tackline Jul 13 '11 at 11:16
  • I wonder, why do people who dislike the idea of private data don't just go all the way and use public instead of protected? At least, when you are using public, you aren't forcing others to inherit from your class to use its implementation details. Considering that inheritance introduces a stronger coupling than composition, if you are going to introduce dependencies on implementation details, at least do it so in the way that uses the weakest coupling possible! – Joh Jul 14 '11 at 09:06
  • @Joh Because you do need to hide some things. It also has the side effect of cleaning up your public API and making methods shorter as people aren't afraid of adding another public method to already long list of public methods. Honestly you can do things without being extreme about it – TheLQ Jul 14 '11 at 17:54
  • @TheLQ: Taking .net as an example, `protected` is not a level of access that's between `public` and `private`. `internal` is better for that. The difference between `protected` and `public` lies in **how** you access, not **who** can access. – Joh Jul 14 '11 at 18:33

8 Answers8

56

Designing classes to work properly when extended, especially when the programmer doing the extending doesn't fully understand how the class is supposed to work, takes considerable extra effort. You can't just take everything that's private and make it public (or protected) and call that "open." If you allow someone else to change the value of a variable, you have to consider how all the possible values will affect the class. (What if they set the variable to null? An empty array? A negative number?) The same applies when allowing other people to call a method. It takes careful thought.

So it's not so much that classes shouldn't be open, but that sometimes it's not worth the effort to make them open.

Of course, it's also possible that the library authors were just being lazy. Depends on which library you're talking about. :-)

Aaron
  • 531
  • 3
  • 3
  • 13
    Yes, *this is why classes are sealed by default.* Because you cannot predict the many ways your clients may extend the class, it is safer to seal it than to commit to supporting the potentially limitless number of ways the class might be extended. – Robert Harvey Jul 12 '11 at 19:42
  • 3
    See also http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx?PageIndex=2 – Robert Harvey Jul 12 '11 at 19:52
  • 18
    You don't have to predict how your class will be overridden, you just have to assume that people extending your class know what their doing. If they extend the class and set something to null when it shouldn't be null then its their fault, not yours. The only place that this argument makes sense is in super critical applications where something will go horribly wrong if a field is null. – TheLQ Jul 12 '11 at 19:58
  • 5
    @TheLQ: That's a pretty big assumption. – Robert Harvey Jul 12 '11 at 20:05
  • 9
    @TheLQ Whose fault it is is a highly subjective question. If they set a variable to a seemingly reasonable value, and you didn't document the fact that that wasn't allowed, and your code didn't throw an ArgumentException (or equivalent), but it causes your server to go offline or leads to a security exploit, then I would say it's as much your fault as theirs. And if you did document the valid values and put an argument check in place, then you have put forth exactly the kind of effort I'm talking about, and you have to ask yourself if that effort was really a good use of your time. – Aaron Jul 12 '11 at 20:14
  • 1
    @Aaron, TheLQ: While the need to protect programmers from shooting their foot is reasonable, more often than not developers lock down their classes more than they need and remove your ability to consciously change fields that, for particular application need to be changed, declaring 'private' what should be 'protected' or there's no getter for a variable that is private while it should be read-only, etc. Member protection should be advisory and overridable, so you must explicitly state "I know, I access a private member here" but you can access it if you need. – SF. Jul 13 '11 at 07:34
  • 1
    @RobertHarvey by choosing private over protected aren't you assuming that you are smarter than anyone who might come along and use your class/have thought of all usage scenarios your users might have? *That* sounds like a bigger assumption – Code Silverback Jul 14 '11 at 21:05
  • 1
    @brian: Read the blog article I linked. The goal is not to support every conceivable unknown scenario; it's to write components that (to the best of your ability) are guaranteed to work, under known conditions. – Robert Harvey Jul 14 '11 at 21:08
  • @brian: I'm not necessarily saying I like the fact that I can't extend a class because it is sealed (being able to do so under certain circumstances would certainly make life easier), only that I do understand why programmers (especially those writing frameworks) make the decision to seal their classes. – Robert Harvey Jul 14 '11 at 21:47
  • 2
    On the private/sealed side here. Why should my internal processing methods have to validate their arguments and throw `ArgumentException`s? Better to freely extract method without guarding against extension by derived classes. Furthermore, keep in mind the Liskov Substitution Principle. Private members indicate "I am not part of the LSP contract, but just an implementation detail encapsulated by the base class." Protected members need to be carefully thought out to avoid LSP violations: it's not fair to assume "know what you're doing" => "know when the base class breaks a basic OO principle." – Domenic Jul 15 '11 at 01:21
24

Making everything private by default sounds harsh, but look at it from the other side: When everything is private by default, making something public (or protected, which is almost the same thing) is supposed to be a conscious choice; it is the class author's contract with you, the consumer, about how to use the class. This is a convenience for both of you: The author is free to modify the inner workings of the class, as long as the interface remains unchanged; and you know exactly which parts of the class you can rely on and which ones are subject to change.

The underlying idea is 'loose coupling' (also referred to as 'narrow interfaces'); its value lies in keeping complexity down. By reducing the number of ways in which components can interact, the amount of cross-dependency between them is also reduced; and cross-dependency is one of the worst kinds of complexity when it comes to maintenance and change management.

In well-designed libraries, classes that are worth extending through inheritance have protected and public members in just the right places, and hide everything else.

tdammers
  • 52,406
  • 14
  • 106
  • 154
  • That does make some sense. There's still the issue though when you need something very similar but with 1 or 2 things changed in the implementation (inner workings of the class). I'm also struggling to understand that if your overriding the class, aren't you depending heavily on its implementation already? – TheLQ Jul 12 '11 at 20:09
  • 5
    +1 for the benefits of loose coupling. @TheLQ, it's the _interface_ you should be depending heavily on, not the implementation. – Karl Bielefeldt Jul 12 '11 at 21:16
19

Everything that is not private is more-or-less supposed to exist with unchanged behaviour in every future version of the class. In fact, it can be considered part of the API, documented or not. Therefore, exposing too many details is probably causing compatibility problems later.

Regarding "final" resp. "sealed" classes, one typical case are immutable classes. Many parts of the framework depend on strings being immutable. If the String class wasn't final, it would be easy (even tempting) to create a mutable String subclass that cause all kinds of bugs in many other classes when used instead of the immutable String class.

user281377
  • 28,352
  • 5
  • 75
  • 130
  • 4
    **This is the real answer.** Other answers touch on important things but the real relevant bit is this: *everything that’s not `final` and/or `private` is locked in place* **and can never be changed**. – Konrad Rudolph Jul 13 '11 at 11:23
  • 1
    @Konrad: Until the developers decide to revamp everything and be non-backwards-compatible. – JAB Jul 13 '11 at 15:15
  • That is true, but it's worthwhile to note that's a far weaker requirement than for `public` members; `protected` members form a contract only between the class which exposes them and its immediate derived classes; by contrast, `public` members for contracts with all consumers on behalf of all possible present and future inherited classes. – supercat Nov 18 '12 at 14:57
14

In OO there are two ways to add functionality to existing code.

The first one is by inheritance: you take a class and derive from it. However, inheritance should be used with care. You should use public inheritance mainly when you have a isA relationship between the base and the derived class (e.g. a Rectangle is a Shape). Instead, you should avoid public inheritance for reusing an existing implementation. Basically, public inheritance is used for making sure the derived class has the same interface as the base class (or a larger one), so that you can apply the Liskov's substitution principle.

The other method to add functionality is to use delegation or composition. You write your own class that uses the existing class as an internal object, and delegates part of the implementation work to it.

I am not sure what was the idea behind all those finals in the library you are trying to use. Probably the programmers wanted to make sure you were not going to inherit a new class from their ones, because that's not the best way to extend their code.

knulp
  • 141
  • 4
  • 5
    Great point with composition vs. inheritance. – Zsolt Török Jul 13 '11 at 07:12
  • +1, but I never liked the "is a shape" example for inheritance. A rectangle and a circle could be two very different things. I like more to use, a square is a rectangle that is constrained. Rectangles can be used like Shapes. – tylermac Jul 13 '11 at 13:59
  • @tylermac: indeed. The Square/Rectangle case is actually one of the counterexamples of the LSP. Probably, I should start thinking of a more effective example. – knulp Jul 13 '11 at 14:47
  • 3
    Square/Rectangle is only a counterexample if you allow modification. – starblue Jul 13 '11 at 19:55
11

Most of the answers have it right: Classes should be sealed (final, NotOverridable, etc) when the object is not designed or intended to be extended.

However, I would not consider simply closing everything proper code design. The "O" in SOLID is for "Open-Closed Principle", stating that a class should be "closed" to modification, but "open" to extension. The idea is, you have code in an object. It works just fine. Adding functionality should not require opening that code up and making surgical changes which may break previously working behavior. Instead, one should be able to use inheritance or dependency injection to "extend" the class to do additional things.

For instance, a class "ConsoleWriter" may get text and output it to the console. It does this well. However, in a certain case you ALSO need the same output written to a file. Opening up the code of ConsoleWriter, changing its external interface to add a parameter "WriteToFile" to the main functions, and placing the additional file-writing code next to the console-writing code would be generally considered a bad thing.

Instead, you could do one of two things: you could derive from ConsoleWriter to form ConsoleAndFileWriter, and extend the Write() method to first call the base implementation, and then also write to a file. Or, you could extract an interface IWriter from ConsoleWriter, and reimplement that interface to create two new classes; a FileWriter, and a "MultiWriter" which can be given other IWriters and will "broadcast" any call made to its methods to all the given writers. Which one you choose depends on what you're going to need; simple derivation is, well, simpler, but if you have a dim foresight of eventually needing to send the message to a network client, three files, two named pipes and the console, go ahead and take the trouble to extract the interface and create the "Y-adapter"; it'll save you time on the back end.

Now, if the Write() function had never been declared virtual (or it was sealed), now you're in trouble if you don't control the source code. Sometimes even if you do. This is generally a bad position to be in, and it frustrates users of closed-source or limited-source APIs to no end when it happens. But, there are legitimate reason why Write(), or the entire class ConsoleWriter, are sealed; there may be sensitive information in a protected field (overridden in turn from a base class to provide said information). There may be insufficient validation; when you write an api, you have to assume the programmers who consume your code are no smarter or benevolent than the average "end user" of the final system; that way you're never disappointed. You either take the time to validate the hell out of anything your consumer can do to extend your API, or you lock the API down like Fort Knox, so they can only do exactly what you expect.

KeithS
  • 21,994
  • 6
  • 52
  • 79
  • Good point. Inheritance can be good or bad, so it is wrong to say that every class should be sealed. It really depends on the problem at hand. – knulp Jul 13 '11 at 12:48
2

I'd think it's probably from all the people using an oo language for c programming. I'm looking at you, java. If your hammer is shaped like an object, but you want a procedural system, and/or don't really understand classes, then your project will need to be locked down.

Basically, if your going to write in an oo language, your project needs to follow the oo paradigm, which includes extension. Of course, if someone wrote a library in a different paradigm, maybe your not supposed to extend it anyway.

Spencer Rathbun
  • 3,576
  • 1
  • 21
  • 28
  • 7
    BTW, OO and procedural are most emphatically NOT mutually exclusive. You can't have OO without procedures. Also, composition is just as much an OO concept as extension. – Michael K Jul 12 '11 at 19:17
  • @Michael of course not. I stated that you may want a *procedural* system, that is, one without OO concepts. I've seen people use Java to write purely procedural code, and it doesn't work if you try and interact with it in an OO fashion. – Spencer Rathbun Jul 12 '11 at 19:38
  • 1
    That could be solved, if Java had first class functions. Meh. – Michael K Jul 12 '11 at 20:11
  • It's difficult to teach Java or DOTNet Languages to new students. I usually teach Procedural with Procedural Pascal or "Plain C", and later, switch to O.O. with Object Pascal or "C++". And, leave Java for later. Teaching programming with a procedura programs look like a single (singleton) object works well. – umlcat Jul 13 '11 at 15:20
  • @Michael K: In OOP a first class function is just an object with exactly one method. – Giorgio Jun 12 '13 at 20:45
2

Because they don't know any better.

The original authors are probably clinging to a misunderstanding of SOLID principles which originated in a confused and complicated C++ world.

I hope you will notice that the ruby, python, and perl worlds don't have the problems that the answers here claim to be the reason for sealing. Note that its orthogonal to dynamic typing. Access modifiers are easy to work in most (all?) languages. C++ fields can be mucked with by casting to some other type (C++ is more weak). Java and C# can use reflection. Access modifiers make things just difficult enough to prevent you from doing it unless you REALLY want to.

Sealing classes and marking any members private explicitly violates the principle that simple things should be simple and hard things should be possible. Suddenly things that should be simple, aren't.

I'd encourage you to try to understand the viewpoint of the original authors. Much of it is from an academic idea of encapsulation that has never demonstrated absolute success in the real world. I've never seen a framework or library where some developer somewhere didn't wish it worked slightly differently and didn't have good reason to change it. There are two possibilities that may have plagued the original software developers which sealed and made members private.

  1. Arrogance - they really did believe they were open for extension and closed for modification
  2. Complacence - they knew there might be other use cases but decided not to write for those use cases

I think in the corporate framework wold, #2 is probably the case. Those C++, Java and .NET frameworks have to be "done" and they have to follow certain guidelines. Those guidelines usually mean sealed types unless the type was explicitly designed as part of a type hierarchy and private members for many things which might be useful for others use.. but since they don't directly relate to that class, they aren't exposed. Extracting a new type would be too expensive to support, document, etc...

The entire idea behind access modifiers is that programmers should be protected from themselves. "C programming is bad because it lets you shoot yourself in the foot." It is not a philosophy that I agree with as a programmer.

I much prefer python's name mangling approach. You can easily (much easier than reflection) replace privates if you need. A great writeup on it is available here: http://bytebaker.com/2009/03/31/python-properties-vs-java-access-modifiers/

Ruby's private modifier is actually more like protected in C# and doesn't have a private as C# modifier. Protected is a little different. There is great explaination here: http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/

Remember, your static language doesn't have to conform to the antiquated styles of the code writte in that language past.

jrwren
  • 234
  • 1
  • 5
  • 5
    "encapsulation has never demonstrated absolute success". I would have thought that everyone would agree that lack of encapsulation has caused a lot of trouble. – Joh Jul 13 '11 at 11:10
  • 5
    -1. Fools rush in where angels fear to tread. Celebrating the ability to change private variables shows a serious flaw in your coding style. – riwalk Jul 13 '11 at 15:19
  • 2
    Apart from being debatable and probably wrong, this posting is also quite arrogant and insulting. Nicely done. – Konrad Rudolph Jul 13 '11 at 20:46
  • 1
    There are two schools of thoughts whose proponents don't seem to get along well. The static typing folk want it to be easy to reason about the software, the dynamic folk want it to be easy to add functionality. Which one is better basically depends on the expected life length of the project. – Joh Jul 14 '11 at 09:17
1

EDIT: I believe classes should be designed to be open. With some restrictions, but shouldn't be closed for inheritance.

Why: "Final classes" or "sealed classes" seems to me weird. I never have to mark one of my own classes as "final" ("sealed"), because I may have to inheret that classes, later.

I have buy / download third party libraries with classes, (most visual controls), and really grateful none of those libraries used "final", even if supported by the programming language, in which they where coded, because I ended extending those classes, even if I have compiled libraries without the source code.

Sometimes, I have to deal with some ocassional "sealed" classes, and ended making a new class "wrapper" containg the given class, that has similar members.

class MyBaseWrapper {
  protected FinalClass _FinalObject;

  public virtual DoSomething()
  {
    // these method cannot be overriden,
    // its from a "final" class
    // the wrapper method can  be open and virtual:
    FinalObject.DoSomething();
  }
} // class MyBaseWrapper


class MyBaseWrapper: MyBaseWrapper {

  public override DoSomething()
  {
    // the wrapper method allows "overriding",
    // a method from a "final" class:
    DoSomethingNewBefore();
    FinalObject.DoSomething();
    DoSomethingNewAfter();
  }
} // class MyBaseWrapper

Scope clasifiers allow to "seal" some features without restricting inheritance.

When I switched from Structured Progr. to O.O.P., I started using private class members, but, after many projects, I ended using protected, and, eventually, promote those properties or methods to public, if necessarily.

P.S. I don't like "final" as keyword in C#, I rather use "sealed" like Java, or "sterile", following the inheritance metaphor. Besides, "final" is an ambiguos word used in several contexts.

umlcat
  • 2,146
  • 11
  • 16
  • -1: You stated you like open designs, but this does not answers the question, which was why classes shouldn't be designed to be open. – Joh Jul 13 '11 at 09:19
  • @Joh Sorry, I didn't explain myself well, I tried to express the opposite opinion, there shouldn't be sealed. Thanks for describe why you disagree. – umlcat Jul 13 '11 at 15:07