Hello! Today, we'll venture into the realm of design patterns. Specifically, we'll tackle exercises that apply a single design pattern to problem-solving. Mastering these patterns is a surefire way to extend your coding skills.
Our goal today is to fortify your understanding of when and how to apply specific Object-Oriented Programming (OOP) design patterns. These patterns include Encapsulation
, Polymorphism
, Composition
, and Abstraction
.
We'll dissect four real-life scenarios and determine which pattern is applicable and why.
Let's get underway!
The Encapsulation
pattern proves beneficial for the development of a Database Management System (DBMS). Each DBMS table represents a class, the fields represent private data members, and the functions operating on this data serve as methods.
Encapsulation
ensures that data members are accessed through methods that promote data integrity and prevent inadvertent anomalies. Here's a mini-code snippet to support this concept:
Ruby1class Employees 2 def initialize 3 @employees = {} # private data member 4 end 5 6 def add_employee(eid, name) # method to operate on private data 7 @employees[eid] = name 8 end 9 10 def update_employee(eid, new_name) # method to operate on private data 11 @employees[eid] = new_name if @employees.key?(eid) 12 end 13 14 def employee_details(eid) 15 @employees[eid] 16 end 17end 18 19employees = Employees.new 20employees.add_employee(1, "John") 21employees.add_employee(2, "Mark") 22 23employees.update_employee(2, "Jake") 24 25print employees.employee_details(2) # Outputs: Jake
In this context, Encapsulation
restricts direct access to employee data, presenting a protective layer via designated methods.
When transitioning to GUI development, consider the creation of controls like buttons or checkboxes. Despite belonging to different classes, each responds differently when clicked. This situation illustrates Polymorphism
, which allows us to handle different objects uniformly via a common interface.
Check out this illustrative example:
Ruby1class Button 2 def click 3 puts "Button Clicked!" 4 end 5end 6 7class CheckBox 8 def click 9 puts "CheckBox Clicked!" 10 end 11end 12 13# Array of controls 14controls = [Button.new, CheckBox.new] 15 16# Iterate and invoke click on each control 17controls.each(&:click) 18# Outputs: Button Clicked! 19# Outputs: CheckBox Clicked!
Despite sharing the common click
interface, different controls display unique responses. This characteristic demonstrates Polymorphism
.
Let's explore the Composition
design pattern through a Ruby approach to create a simple web page structure. Here, we'll build a fundamental structure representing a webpage composed of various elements like headers, paragraphs, and lists. This abstraction allows us to understand how composite objects work together to form a larger system.
Ruby1class WebPageElement 2 def render 3 raise NotImplementedError, 'Subclasses must implement the render method' 4 end 5end 6 7class Header < WebPageElement 8 def initialize(text) 9 @text = text 10 end 11 12 def render 13 "<h1>#{@text}</h1>" 14 end 15end 16 17class Paragraph < WebPageElement 18 def initialize(text) 19 @text = text 20 end 21 22 def render 23 "<p>#{@text}</p>" 24 end 25end 26 27class List < WebPageElement 28 def initialize(items) 29 @items = items 30 end 31 32 def render 33 items_str = @items.map { |item| "<li>#{item}</li>" }.join("\n") 34 "<ul>#{items_str}</ul>" 35 end 36end 37 38class WebPage 39 def initialize(title) 40 @title = title 41 @elements = [] 42 end 43 44 def add_element(element) 45 @elements << element 46 end 47 48 def display 49 elements_str = @elements.map(&:render).join("\n") 50 "<html>\n<head>\n <title>#{@title}\n</title>\n</head>\n<body>\n #{elements_str}\n</body>\n</html>" 51 end 52end 53 54# Example usage 55page = WebPage.new("My Web Page") 56page.add_element(Header.new("Welcome to My Web Page")) 57page.add_element(Paragraph.new("This is a paragraph of text on the web page.")) 58page.add_element(List.new(["Item 1", "Item 2", "Item 3"])) 59 60puts page.display
In this code, we've designed a web page structure using the Composition
design pattern. Each web page element (Header
, Paragraph
, and List
) is a WebPageElement
, allowing for unified handling while maintaining their specific behaviors (rendering as HTML elements).
The WebPage
class acts as a composite object that can contain an arbitrary number of WebPageElement
instances, each representing different parts of a web page. By adding these elements to the WebPage
and invoking the display
method, we dynamically compose a complete web page structure in HTML format.
Consider creating a Vehicle
class in Ruby. Here, Abstraction comes into play. You expose only the necessary functionality and abstract away the internal workings of the Vehicle
.
Let's see this in code:
Ruby1class Vehicle 2 attr_accessor :color, :engine_type 3 4 def initialize(color, engine_type) 5 @color = color 6 @engine_type = engine_type 7 @engine_running = false 8 end 9 10 def start_engine 11 raise NotImplementedError, 'Subclasses must implement the start_engine method' 12 end 13 14 def stop_engine 15 raise NotImplementedError, 'Subclasses must implement the stop_engine method' 16 end 17 18 def drive 19 raise NotImplementedError, 'Subclasses must implement the drive method' 20 end 21end 22 23class Car < Vehicle 24 def start_engine 25 @engine_running = true 26 puts "Car engine started!" 27 end 28 29 def stop_engine 30 @engine_running = false 31 puts "Car engine stopped!" 32 end 33 34 def drive 35 if @engine_running 36 puts "Car is driving!" 37 else 38 puts "Start the engine first!" 39 end 40 end 41end 42 43# Example usage 44car = Car.new("red", "gasoline") 45car.start_engine 46car.drive
Here, the Vehicle
class establishes a blueprint by outlining necessary methods such as start_engine()
, stop_engine()
, and drive()
. The Car
class inherits from Vehicle
, providing concrete implementations while hiding the internal state management (@engine_running
). This is a basic instance of Abstraction, which simplifies the interaction with the class and hides underlying complexity.
Let's recap the major OOP patterns:
Encapsulation
: This pattern confines data and related methods into one unit, veiling direct data access.Abstraction
: This pattern offers a simplified interface, cloaking complexity.Polymorphism
: This pattern facilitates treating different objects as related objects of a common superclass.Composition
: This pattern builds elaborate systems by composing closely related objects.
Reflect on these principles and practice applying them to a variety of scenarios to better recognize suitable patterns.
Great job! You've explored the practical applications of OOP design patterns. We've examined the use of Encapsulation
in Database Management Systems, the pivotal role of Polymorphism
in GUI development, the importance of Composition
when designing a web page builder, and how Abstraction helps to build a vehicle structure.
Next up are hands-on exercises to reinforce these concepts. Remember, practice is the master key to understanding these concepts. So keep coding!