@@ -826,6 +826,125 @@ accepts a string.
826826As an added bonus, the ` get_version() ` is now reusable elsewhere.
827827
828828### ** Open/Closed Principle (OCP)**
829+
830+ > “Incorporate new features by extending the system, not by making
831+ modifications (to it)”, Uncle Bob.
832+
833+ Objects should be open for extension, but closed to modification. It should be
834+ possible to augment the functionality provided by an object (for example, a class)
835+ without changing its internal contracts. An object can enable this when it
836+ is designed to be extended cleanly.
837+
838+ In the following example, we try to implement a simple web framework that
839+ handles HTTP requests and returns responses. The ` View ` class has a single
840+ method ` .get() ` that will be called when the HTTP server will receive a GET
841+ request from a client.
842+
843+ ` View ` is intentionally simple and returns ` text/plain ` responses. We would
844+ also like to return HTML responses based on a template file, so we subclass it
845+ using the ` TemplateView ` class.
846+
847+ ** Bad**
848+ ``` python
849+ from dataclasses import dataclass
850+
851+
852+ @dataclass
853+ class Response :
854+ """ An HTTP response"""
855+
856+ status: int
857+ content_type: str
858+ body: str
859+
860+
861+ class View :
862+ """ A simple view that returns plain text responses"""
863+
864+ def get (self , request ) -> Response:
865+ """ Handle a GET request and return a message in the response"""
866+ return Response(
867+ status = 200 ,
868+ content_type = ' text/plain' ,
869+ body = " Welcome to my web site"
870+ )
871+
872+
873+ class TemplateView (View ):
874+ """ A view that returns HTML responses based on a template file."""
875+
876+ def get (self , request ) -> Response:
877+ """ Handle a GET request and return an HTML document in the response"""
878+ with open (" index.html" ) as fd:
879+ return Response(
880+ status = 200 ,
881+ content_type = ' text/html' ,
882+ body = fd.read()
883+ )
884+
885+ ```
886+
887+ The ` TemplateView ` class has modified the internal behaviour of its parent
888+ class in order to enable the more advanced functionality. In doing so,
889+ it now relies on the ` View ` to not change the implementation of the ` .get() `
890+ method, which now needs to be frozen in time. We cannot introduce, for example,
891+ some additional checks in all our ` View ` -derived classes because the behaviour
892+ is overridden in at least one subtype and we will need to update it.
893+
894+ Let's redesign our classes to fix this problem and let the ` View ` class be
895+ extended (not modified) cleanly:
896+
897+ ** Good**
898+ ``` python
899+ from dataclasses import dataclass
900+
901+
902+ @dataclass
903+ class Response :
904+ """ An HTTP response"""
905+
906+ status: int
907+ content_type: str
908+ body: str
909+
910+
911+ class View :
912+ """ A simple view that returns plain text responses"""
913+
914+ content_type = " text/plain"
915+
916+ def render_body (self ) -> str :
917+ """ Render the message body of the response"""
918+ return " Welcome to my web site"
919+
920+ def get (self , request ) -> Response:
921+ """ Handle a GET request and return a message in the response"""
922+ return Response(
923+ status = 200 ,
924+ content_type = self .content_type,
925+ body = self .render_body()
926+ )
927+
928+
929+ class TemplateView (View ):
930+ """ A view that returns HTML responses based on a template file."""
931+
932+ content_type = " text/html"
933+ template_file = " index.html"
934+
935+ def render_body (self ) -> str :
936+ """ Render the message body as HTML"""
937+ with open (" index.html" ) as fd:
938+ return fd.read()
939+
940+
941+ ```
942+
943+ Note that we did need to override the ` render_body() ` in order to change the
944+ source of the body, but this method has a single, well defined responsibility
945+ that ** invites subtypes to override it** . It is designed to be extended by its
946+ subtypes.
947+
829948### ** Liskov Substitution Principle (LSP)**
830949### ** Interface Segregation Principle (ISP)**
831950### ** Dependency Inversion Principle (DIP)**
0 commit comments