Subclassing is a very common feature of most languages. Typically you'll see it written in as a best practice. Unfortunately I don't really agree with this and here's why:
Subclassing promotes tight coupling. Tight coupling as you may know is a bad thing. It happens when a piece of code knows a little too much about the implementation of another piece of code. This causes all sorts of maintenance and extension problems because some code can't be pulled out or changed without changing things that it's tightly coupled to. This promotes leaving it and building layers of hacks on top. Well, when you have a class and you'd like to extend it using built-in subclassing, you naturally have to know a decent amount about the implementation for it to work. That is to say, if I change the base class' implementation, I probably need to change the subclasses too because they have knowledge of it ("protected" attributes for instance). This is a very problematic way to do things and I'm disappointed that so many languages adopted it.
You may of course ask "well then what can we do?" The answers is to use the decorator pattern. The decorator pattern is where you take the class you're interested in and wrap it in a new class. So in the constructor of the new class you might instantiate the class you are interested in extending.
class Foo {
Foo();
public doSomething(args);
private implementation(args);
}
interface IFoo{
doSomething(args)
}
class Bar : IFoo{ //
Bar(iFoo){
this.foo = iFoo
};
public doSomething(args){
//extensions
this.foo.doSomething(args);
}
}
Here I've actually made an interface for Foo that just has all the public methods. Unfortunately, not all languages support duck-typing so this is really just language baggage, note that Bar can also implement IFoo to be used in places where Foo might. However, this allows you to swap out Foo's implementation if you want and it will not have any impact on Bar. This is really nice. You could also just have Bar new-up it's own Foo but I don't recommend that because it's not very functional and is brittle for the same reason subclassing is.
Note that this will also make the unit testing scenario much, much easier, again because you can swap out concrete types for stubs very easily. In the end, just because subclassing is a language feature doesn't mean it's a good one.