I remember reading somewhere that the original concepts behind OO were to find a better architecture for handling the messaging of data between multiple systems in a way that protected the state of that data. Now that is probably a poor paraphrase, but it made me wonder if there is a way of teaching OO without the (Bike, Car, Person, etc.) object analogies, and that instead focuses on the messaging aspects. If you have articles, links, books, etc., that would be helpful.
The original concept of OO doesn't have anything to do with what today's OO is. (See So what *did* Alan Kay really mean by the term "object-oriented"?). Today's object oriented programing IS about creating objects like the metaphors of bicycles and houses and people, etc. I would highly recommend sticking with these because the purpose of the metaphors is to help people understand by using a concept they already understand. Help them see the correlation then help them see the differences THEN dive in to deeper things about OO.
EDIT: Today's OO is about creating fully self-contained objects whose properties and abilities are fully/partially described using various methods (functions) and attributes (references AKA variables and constants).
You can talk about concepts of coupling and cohesion. Objects should be composed of attributes and methods with high cohesion and implicitly high coupling. They should map to the least granular operations and attributes needed for the system to work. They should also satisfy the desire to keep code as small and straightforward as possible, ie coding with maintenance and extensibility in mind.
This also prevents "object explosion", over-generalization, and wrong metaphor choice which are all common mistakes.
I wouldn't focus on the real-world objects, and I wouldn't focus on the messaging either. Rather an example I've used is in graphics, where you want to have objects that "know how to draw themselves".
If you're working in C, for example, that doesn't have OO built in, you may find it convenient to store pointers to functions inside of data objects. If you do, then you're wedging your way into OOP.
I don't like to refer to Alan Kay as if he were Moses handing down the tablets. Rather, he was trained in math and bio, I believe. As a math guy, he probably had some familiarity with Lambda Calculus, which was pretty abstract, not related to hardware. In LC, you might say everything is an "object" - like the number 0 and the number 1 are objects that evaluate to different things when given an argument. That leads into Smalltalk pretty nicely. The idea of "message" is so we can avoid talking about hardware. You could say when you call a function (or a method of an object) you are sending it a message, and when it returns, it is sending a message to you (or to your continuation). That was latched onto as a way of describing ways to communicate between programs running asynchronously on separate hardware. That's fine, but for ordinary programming it's getting carried away. To get the value of the OOP idea, you don't need to deny the relevance of the concrete task you're trying to do, or deny the concreteness of the hardware you're running on. I think teaching about OOP in terms of contrived analogies leads people to think about software design too much in terms of data structure, leading to its over-design, leading to code bloat and massive performance issues, that I have to spend time cleaning up when it gets bad enough.
I'd claim there is little difference in using a physical object for an example and using a non physical object as an example. In code they both have the exact same parts. If we use the graphics example and teach it with Sphere, cube, cylinder, its nearly the same as using ball, box, pole.
So to teach it without using physical examples I would suggest not using any examples at all, but I don't see why you wouldn't want physical examples so my stance on the topic is
No, you shouldn't teach it without physical real world objects
Interestingly some of my favourite examples are not physical objects. Take Bank Account for example. Everyone "gets" why deposit() and withdraw() should charge the service charge, rather than relying on calling code to change the value of balance and remember to take off the service charge. Shapes on a screen are doubly intangible, and Stroustrup told me the classic "Shapes" example is one of the two oldest OO examples he knows, dating back 40 years now (the other is vehicles, now 44 years old.)
What's important is that people understand your examples right away. Elevators make a good example only with people who are all familiar with elevators. Etc.
If you are in a programming group get a few people together and start discussing how you would tell each other to do what you need the system to do. Literally take roles in the system (you can do this on your own by just playing each role, but it is easier with a group of people. Toys help if you are on your own). Focus on what each person is doing/will do, rather than on what data they have. Doing this with people helps this focus on messages and roles because people tend to remember what they are doing but not the data they have.
Take note of what you have to ask each other to do, and what information you need to do it. Be protective of your own data, if another programmer asks for your data for something say no and ask him why he needs it (helps data encapsulation).
I think a bottom-up/metal approach might be useful. First explain C-style structs and pointers, to show how data can be structured rather than just using primitives directly. Then explain late binding and function pointers. Then explain that you can use these to build objects, which are basically well-encapsulated piles of data and pointers to the functions that are needed to operate on said data.
This explanation contradicts the conventional math/comp sci way of teaching the concept independently of the implementation, but it's the perspective that made me (admittedly someone with an engineering, not a comp sci, background) finally get OO.