5
$\begingroup$

As a computer science educator, I have extensively researched various books for relevant examples to teach programming concepts, particularly Object-Oriented Programming (OOP). A recurring challenge I've encountered is the lack of concrete examples illustrating OOP principles, especially inheritance, beyond the frequently cited Shape/Triangle/Square or Animal/Cat/Mouse analogies.

Additionally, the use of Run-Time Type Information (RTTI) often includes controversial or incorrect examples (such as improper use of typeid). In several texts, design patterns are illustrated with examples far removed from the computing domain. For instance, using the Observer pattern to notify Readers of a Library about a new Newsletter seems impractical.

I am somewhat disappointed by the scarcity of accessible and relevant examples for my students. While there are indeed complex, real-world applications of polymorphism, reflection, and the Visitor pattern, these are often too intricate for classroom discussion. Consequently, I find myself reverting to the simpler, albeit less practical, examples I initially sought to avoid.

Does anyone have advice or resources for effectively teaching programming concepts, particularly OOP, in a way that resonates with students and transcends these common, over-simplified analogies?

$\endgroup$
2
  • $\begingroup$ What are your students' interests? I can give you examples simulating a numerical library, but if they are not math/engineering students that will be meaningless. $\endgroup$ Jan 22 at 17:35
  • $\begingroup$ @VictorEijkhout I think they would be happy if I come across real engineering problems $\endgroup$
    – nowox
    Jan 22 at 19:06

5 Answers 5

5
$\begingroup$

An idea I have though a bit about is using several puzzles to illustrate some OOP concepts. Consider Sudoku and KenKen. Both are played on a square board and share the constraint that the values in row/column are unique. But there is a third constraint. For Sudoku, all the values in an area must be unique. In KenKen, the values in an area are combined with a mathematical operaton and the result must match what is given. Furthermore, in Sudoku, all the "areas" have the same size/shape. In KenKen, the "areas" may be irregular and have different sizes.

Now think about the search for a solution. At a high level it is:

  1. Select a value for some empty square.
  2. Determine if the game is in a valid state. If it is, return to step 1.
  3. If the state is not valid, undo the previous selection and try again.
  4. Continue until you find a solution

So one could have the base class implement the basic algorithm, but defer to the child class for evaluating constraints. Now you have a hierarchy. There are many puzzles that fit this pattern.

Another game I like is TwoNotTouch. The constraints for this are are different, but it shares the idea of "irregular" areas with KenKen. So, how does one represent the areas for reading a puzzle from a file? One can use a list of cells for each area, or use a grid a letters, where each letter represents one area. You can use the same strategy for describing the areas in both games. The constraints are a bit different, but that may just result in a wider hierarchy.

Continuing with games, but thinking of reflection, consider a two person game in which the two "players" are bots developed as part of the assignment. You want each student to write a class (perhaps their name, e.g. Fritz.java), that implements an interface or extends an abstract class. When you want to run a tournament among all the students, the driver program takes two class names, then uses reflection to find and call a constructor for the class. After that, everything is done via the methods of the interface/abstract class. Simple, but effective use of reflection.

$\endgroup$
4
$\begingroup$

Somewhat depends upon the age/type of the trainees. Employees want relevance to their work. High school students rarely do more than think they know about business...

Just about everyone knows about retail stores. Accordingly, classes such as Product and Person. An order is a non-obvious entity and creates interaction/interface between a Customer type of Person and Product. Products are seen slightly differently by Customers compared with the Purchasing Department's view. There's also plenty of room for subtle distinctions, eg discounts by product or by quantity/retail-value, and complications, eg period during which 'special price' is available. StaffMember( Person ) and Customer( Person ) demonstrate inheritance. Given that they and Supplier all have addresses, there is plenty of scope for composition. The necessity to compute sales-taxes and similar yields opportunity to look at polymorphism - what does it mean in xyz-situation. (am presuming illustrations of abstraction and encapsulation are inherent in this scenario).

$\endgroup$
3
$\begingroup$

Consequently, I find myself reverting to the simpler, albeit less practical, examples I initially sought to avoid.

I empathize with having long round trips to then have to come back to the conclusion you start off trying to avoid. It sucks but it's a fact of life that not every attempt to improve something ends up successful. Sometimes the journey teaches you why the thing you disliked is actually better than you were giving it credit for.

I've encountered is the lack of concrete examples illustrating OOP principles, especially inheritance, beyond the frequently cited Shape/Triangle/Square or Animal/Cat/Mouse analogies.

The reason these examples are so common is because they're really good examples. Not so much to explore the technical concept in all of its depth, but because students require no business context to understand these concepts.

The point of an example is to quickly showcase your topic. "Quick" is an operative word there, no one wants to sit through a long buildup for a throwaway example. This is why all good examples use a context that the intended recipients are familiar with, e.g. using a sports analogy with a sports fan. It cuts down on the boring and distracting buildup time.

I'm not continuing down this train of thought but just to point out here that it's about the relative size between setup cost and example lifetime. If you're going to work with this example across the entire curriculum (building on it as you go), then it's okay if you need to spend a short amount of time to set up the example context. But this would require you to model a significant part of your curriculum around that example, which is a bit out of scope for this question.

In several texts, design patterns are illustrated with examples far removed from the computing domain.

Removing it from the computing context is one of the main points of these examples. Students are focusing on learning this new computing topic, and throwing even more computing context at them is not going help their digestion of this information.
Instead, it's better for them to focus on something that isn't foreign to them, so they relax their focus and consider something they're already familiar with, and at the end of the example they suddenly realize that this idea applies to the foreign topic (computing) as well.

Even all the way back in ancient Greece, complicated psychological or social issues (or narrative plots on such at topic) would be explained using nature analogies. The uneducated (or low educated) would understand the workings of nature, and these nature analogies would be used to explain what would otherwise be a more abstract and complicated topic to them.
Explaining the intricate justification and morality of why a killing is not always a murder; e.g. when in defense of others, is a difficult subject to discuss today, let alone back then. But pointing out that a [insert local animal] would attack you if you threaten her children (and would not have attacked you if you have not threatened her children) is much easier to innately understand if you've lived around this animal.

To a very real degree, fairy tales perform this exact role for children. Rather than being taught some abstract lesson on how to behave, they are instead told a story with characters whose motives they understand (protagonists are often lacking information or maturity), with consequences that seem obvious, and the narrative supports an idea/conclusion that the child can then transplant onto their real life without needing to find out in real life why things are this way.

teaching programming concepts, particularly OOP, in a way that resonates with students and transcends these common, over-simplified analogies?

Commonality is a good thing, not a bad thing.

However, the example only needs to be as common as your students' knowledge is. If you're dealing with a very diverse crowd, this will inherently limit you to very common experiences that all students will be familiar with. The weather, everyday life, stuff everyone learns in school.

However, if the students share more commonalities (compared to what you'd expect from a random collection of people), e.g. they're all avid gamers, that gives you additional common contexts to use for your examples, which is likely going to open some doors.

Based on your question it seems to me that you're judging examples by how deeply correct they and are glossing over how unfamiliar the students likely are with the computing context that you're already familiar with.

Unless this is an advanced course that inherently focuses on these kinds of deep dives, I would re-evaluate if your metric is right for your target audience.

$\endgroup$
2
$\begingroup$

I'm slightly confused by your question, as you talk about needing to teach inheritance (which is typically right at the beginning of OOP), and then mention the Visitor Pattern, which would typically be taught much, much later.

The silly examples you give are fine for teaching the mechanics of inheritance and polymorphism within your language of choice, and that is a real benefit. But to go beyond mechanics into real function and object patterns, you obviously need to go beyond those toy examples.

I constantly use games. Games can be structured to have literally any set of rules that you'd like in order to motivate any pattern you'd like to meet.

I've come to use Greenfoot for teaching advanced object patterns (a full year after I teach inheritance). The patterns end up very clear within Greenfoot's simple environment, and Greenfoot generates something like a UML class diagram, which helps to illustrate what's happening. These are still toy programs, but I've found that students grasp the patterns very well after building them out in this simple environment with the tiny games as an easy illustration of the behaviors we are creating. I can then verbally go through where else you might use the pattern to help them see it more broadly.

$\endgroup$
2
0
$\begingroup$

these are often too intricate for classroom discussion. Consequently, I find myself reverting to the simpler, albeit less practical, examples I initially sought to avoid.

I think the problem is that many educators start with the idea that objects are a basic concept, and the design of systems of objects from scratch ought to be simple to teach to students who have no prior commercial or professional experience.

In reality, systems of objects are more suited to applications which are non-trivial (in the sense that the development is too much for one person to handle alone), and one does not necessarily have to be taught how to design libraries of objects in order to understand how to use existing object-oriented libraries within what is essentially standard procedural code.

Once you're digging into how to design whole systems of objects (which is the necessary context to discuss inheritance), you're really at quite an advanced level of design complexity.

It's like discussing the design principles of buildings with flying buttresses, with young apprentices who haven't laid a brick in the field yet. You just wouldn't do it - in the medieval world, the audience for any serious discussion of designing buildings with flying buttresses, would be master masons.

It's a number of years since I've written any code that involved designing systems of objects (as opposed to merely using pre-made objects from standard libraries), so the fine details of examples are fading from memory, but I'm sure each and every attempt to use inheritance resulted in code that was ultimately far more difficult to deal with (including reading, maintaining, and re-adapting to change) than alternative approaches not involving inheritance, and the attempts were quickly undone.

Even with screens/visual forms where there seemed to be an element of common layout, inheritance (speaking in the context of Winforms here) always seemed eventually to lead to more pain than it was worth, and copy-and-paste or repetition was the way to go. My understanding from discussion and from wide reading, is that I'm not alone in these experiences.

My advice would be to heed the fact that most object-oriented design is not a simple topic to teach nor appropriate for beginners, and that the difficulty finding scenarios that are both plausible/practical and simple to describe in a classroom, is the symptom of the fact.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.