In short when you look at how our economy is structured, the whole thing is a series of abstraction layers. At every layer, whether it is between businesses or between people within a business, there is a hiding of details that the person asking doesn't need to know which really do need to be known to satisfy the request. That isn't theory. That is specialization in action, and was documented centuries ago by Adam Smith.
This doesn't mean that any particular business problem should be solved with nested abstractions, or that OO is the right way of abstracting things. This just means that layering abstractions on abstractions is something that businesses do all of the time. Explain it in terms of actual people doing real jobs, and you can see it in practice. (OO models the people as objects and the jobs as method calls. That is all.)
Now as for Code Complete, Steve McConnell isn't grinding any ideological axes. He gives real examples from real code. But he isn't focussed on overall design of projects. Instead his focus is on actual decisions made while coding. It is very practical and direct. One of his points happens to be relevant for this discussion, but his book is just plain good stuff to know.
Furthermore if you want to see an example of OO design in Perl, I would point to LWP. Don't try to master its internal design, that is a mistake. The point of abstracting details into interfaces is to avoid having to learn those details. Instead try to use it. Here is one:
use LWP::Simple;
print "Please name the URL: ";
chomp(my $url = <STDIN>);
getprint($url);
Try it out. Note the good old [link|http://name:password@www.whatever.com/page.hml|http://name:passwor...com/page.hml] syntax for handling names and passwords, how easy is it to modify your application to handle that? Switch the protocol from http to https to ftp, was that hard? Put a proxy server in the way, set the appropriate environment variables (eg set http_proxy to [link|http://yourproxy:proxyport/|http://yourproxy:proxyport/], ditto for https_proxy, ftp_proxy, then set no_proxy to addresses you get directly), did that do what you expect?
If you allow the base URL to be given by the user, your code doesn't ever change at all. And wouldn't change even if 15 new protocols you had never heard of were added.
What actually happens is, of course, wildly different based on what the protocol is. But you don't know, and you don't care. Because the person writing that program shouldn't have to know and shouldn't have to care. So getprint is a very simple functional wrapper around a common set of OO requests to a fully functional OO library whose design makes replacement of one protocol by another utterly trivial and straightforward.
Cheers,
Ben