This is a technique that can replace your if/else and case statements.

You don’t need to replace all of your if statements with polymorphism, but you should definitely learn this technique. In many cases, a simple if statement is sufficient. But this is a convenient tool to have in the draw that can lead to code that better expresses intent.
Here’s how it works. Say you have a Shipment class that calculates a shipping fee in a method. In the method, you have an if statement, where if the shipment is over 80 pounds, it uses a freight calculation. Otherwise, it uses a standard USPS parcel post calculation. And then later, in the code, when determining what kind of label to print, it does this if/else thing again. Then, a third time, when deciding where to drop off the shipment, the if/else appears once more. Now you’re feeling like you’re repeating yourself.
Instead, you could have Shipment be an abstract class, and let it be extended by FreightShipment and PostalShipment classes. The abstract class could define abstract methods for calculating shipping fees, printing labels, and locating drop-off points. In the concrete implementation classes, you will the respective and proper implementations for each method.
Now, these methods are not concerned with determining what kind of shipment it is. It already knows that based on the class. This allows the class to focus on its particular implementation.
Also, if a third kind of shipment is added later, you don’t need to change the other two classes. That’s a huge win.