Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

Open-Closed

00:00 In the previous lesson, I described the single-responsibility principle. In this lesson, I’ll talk about the open-closed principle. The open-closed principle, or OCP to its friends.

00:13 You down with OCP? Yeah, you know me. Okay, now I have to apologize twice. First, for doing that while being as pasty white as I am. Truly, I put mayonnaise to shame.

00:24 And second, for mangling the name of a thirty-year-old R & B hit and including it in an object-oriented programming course. Where was I? The open-closed principle is as follows: software entities should be open for extension but closed for modification.

00:42 In this case, open means I should be able to modify it without breaking anything dependent on it. In classes, this typically means any subclass shouldn’t break if I touch the parent class.

00:54 Closed for modification means the interface is stable. If someone is extending my class, the base class should be as stable as possible. Otherwise, it will muck with the subclass. Essentially, if adding features means you end up having to redesign your structure, you’re probably not embodying this principle.

01:14 Let’s look at a violation of OCP and then a better approach.

01:20 Another object-oriented day, another example using shapes. If you took part two of the course, you’ll remember it’s the law. This Shape class is a bit of a factory abstraction. To create a circle or rectangle, you pass in the type of the shape you want, and the class becomes a generic wrapper to it. Of course, if you take this approach, your code is going to be littered with if it’s a rectangle, then do …

01:48 The same goes for circle. This mess isn’t just in .__init__(), but any method that needs to understand the difference between the types of shapes is going to have the same big if then else block.

02:01 Besides being a bit ugly, how does this violate the open-closed principle? Well, think about adding a triangle. I now have to go back and do surgery on every one of the methods, adding an ifelse "triangle" clause.

02:14 This violates the closed for modification part of the open-closed principle. If someone extended this class and implemented, say, a perimeter method, they’d have to be aware of the internal mechanisms of the parent.

02:29 This violates the open part of open-closed. If I add the triangle to the parent, the child’s perimeter method would now be broken.

02:42 A better way to do this is to split out circles and rectangles into their own classes. If you want to enforce the interface that Shape should provide, you can do that through an abstract base class in Python.

02:55 By inheriting from ABC, I get to use the @abstractmethod decorator inside the class. When I use that to wrap a method, it tells the extender they must implement this method. If they forget, they’ll get an exception the first time they go to instantiate their class.

03:13 By separating the code out into different classes, you get another advantage. The previous version of Shape needed to use the **kwargs mechanism for initial arguments because different shapes have different needs.

03:25 By using a dedicated Circle class, I can be very specific about its need for a radius and only a radius.

03:32 If I still want the shape-type information, I can keep that in the parent and use super().__init__() to populate it. In this simple example, you really don’t need the shape-type info, as the class makes itself evident, but I left it there so you could see an example of the base class requiring an argument.

03:50 The .calculate_area() method now is specific to a circle without any extra if then else cruft.

03:58 The same goes for our rectangle. Its constructor can use width and height, and it can implement its own area formula.

04:09 If you’re finding you are violating the open-closed principle, you typically can fix it by reexamining your class hierarchy or sometimes just introducing one. You saw me use an abstract base class and inheritance to solve the Shape class problem.

04:24 You might also use delegation or dependency injection as well. Both of these are ways of doing composition that keeps the implementation objects isolated.

04:34 If you’re coming from another language besides Python and you skipped part two of this course, you may not have seen an abstract base class before. Python implements the concept as an actual class rather than as part of the language’s syntax.

04:50 One way to consider whether you’ve nailed the open-closed principle is to think about the consequences of adding something with the better implementation of the Shape class. Adding a new color attribute is fine.

05:02 It would just get passed to the children without breaking anything. Likewise, I can tell that it’s closed, as adding Triangle would mean a new class and wouldn’t require surgery on Shape, Circle, or Rectangle to get it to work.

05:19 That’s the O. Time for L.

Become a Member to join the conversation.