Leveraging OOP Principles in PHP for Enhanced Code Structure
Lesson Overview
Hello once again! Today's lesson is centered around leveraging the principles of Object-Oriented Programming (OOP) — Encapsulation, Abstraction, Polymorphism, and Composition — to enhance code readability and structure. Buckle up for an exciting journey ahead!
Connection between OOP and Code Refactoring
OOP principles act as a scaffold for building readable, maintainable, and flexible code — these are the characteristics we seek while refactoring. By creating logical groupings of properties and behaviors in classes, we foster a codebase that's easier to comprehend and modify. Let's put this into perspective as we progress.
Applying Encapsulation for Better Code Organization
Encapsulation involves bundling related properties and methods within a class, thereby creating an organization that mirrors the real world.
Suppose we possess scattered student information within our program.
Although functional, the code could cause potential confusion as the related attributes and behaviors aren't logically grouped. On top of that, all these fields are mutable and accessible from the outer scope, introducing a bunch of security concerns. Let's encapsulate!
PHP
class Student { private $name; private $age; private $grade; public function __construct($name, $age, $grade) { $this->name = $name; $this->age = $age; $this->grade = $grade; } public function displayStudentInfo() { echo "Student Name: " . $this->name . PHP_EOL; echo "Student Age: " . $this->age . PHP_EOL; echo "Student Grade: " . $this->grade . PHP_EOL; } public function updateStudentGrade($newGrade) { $this->grade = $newGrade; } // Properties for encapsulated fields public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getAge() { return $this->age; } public function setAge($age) { $this->age = $age; } public function getGrade() { return $this->grade; } public function setGrade($grade) { $this->grade = $grade; }}
After refactoring, all student-related properties and methods are contained within the Student class, thereby enhancing readability and maintainability.
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal
Utilizing Abstraction to Simplify Code Interaction
Polymorphism to Cater for Multiple Behaviors
Polymorphism provides a unified interface for different types of actions, making our code more flexible.
Assume we are developing a simple graphics editor. Here is a code snippet without Polymorphism:
class Rectangle { public function drawRectangle() { echo "Drawing a rectangle." . PHP_EOL; }}class Triangle { public function drawTriangle() { echo "Drawing a triangle." . PHP_EOL; }}
We have different method names for each class. We can refactor this to have a singular draw method common to all shapes:
abstract class Shape { abstract public function draw();}class Rectangle extends Shape { public function draw() { echo "Drawing a rectangle." . PHP_EOL; }}class Triangle extends Shape { public function draw() { echo "Drawing a triangle." . PHP_EOL; }}
Now, regardless of the shape of the object, we can use draw() to trigger the appropriate drawing behavior, thus enhancing flexibility.
Building Better Structure with Composition
Refactoring with Composition
Summary
Great job! We've learned how to apply OOP principles to refactor code for improved readability, maintainability, and scalability.
Now, get ready for some exciting exercises. Nothing strengthens a concept better than practice! Happy refactoring!
We can encapsulate this within the calculateGpa() method of our Student class, thereby simplifying the interaction.
PHP
class Student { private $name; private $grades; private $gpa; public function __construct($name, $grades) { $this->name = $name; $this->grades = $grades; $this->gpa = $this->calculateGpa(); } private function calculateGpa() { $totalPoints = 0; $gradePoints = [ "A" => 4, "B" => 3, "C" => 2, "D" => 1, "F" => 0 ]; foreach ($this->grades as $grade) { $totalPoints += $gradePoints[$grade]; } return $totalPoints / count($this->grades); } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getGrades() { return $this->grades; } public function setGrades($grades) { $this->grades = $grades; $this->gpa = $this->calculateGpa(); } public function getGpa() { return $this->gpa; }}
We can now access the gpa as an attribute of the student object, which is calculated behind the scenes.
PHP
class Rectangle { public function drawRectangle() { echo "Drawing a rectangle." . PHP_EOL; }}class Triangle { public function drawTriangle() { echo "Drawing a triangle." . PHP_EOL; }}
PHP
abstract class Shape { abstract public function draw();}class Rectangle extends Shape { public function draw() { echo "Drawing a rectangle." . PHP_EOL; }}class Triangle extends Shape { public function draw() { echo "Drawing a triangle." . PHP_EOL; }}
Our last destination is Composition, which models relationships between objects and classes. Composition allows us to design our systems flexibly and maintainably by constructing complex objects from simpler ones. This principle helps us manage relationships by ensuring that objects are composed of other objects, thus organizing dependencies more neatly and making individual parts easier to update or replace.
Consider a system in our application that deals with rendering various UI elements. Initially, we might have a Window class that includes methods both for displaying the window and managing content like buttons and text fields directly within it.
PHP
class Window { private $content; public function __construct() { $this->content = "Default content"; } public function addTextField($content) { $this->content = $content; } public function display() { echo "Window displays: " . $this->content . PHP_EOL; }}
This approach tightly couples the window display logic with the content management, making changes and maintenance harder as we add more elements and functionalities. Let's now see how we can update this code with composition.
To implement Composition, we decouple the responsibilities by creating separate classes for content management (ContentManager) and then integrating these into our Window class. This way, each class focuses on a single responsibility.
PHP
class ContentManager { private $content; public function __construct() { $this->content = "Default content"; } public function updateContent($newContent) { $this->content = $newContent; } public function getContent() { return $this->content; }}class Window { private $manager; public function __construct() { $this->manager = new ContentManager(); } public function display() { echo "Window displays: " . $this->manager->getContent() . PHP_EOL; } public function changeContent($newContent) { $this->manager->updateContent($newContent); }}
By refactoring with Composition, we've encapsulated the content management within its class. The Window class now "has a" ContentManager, focusing on displaying the window. This separation allows for easier modifications in how content is managed or displayed without altering the other's logic. Composition, in this way, enhances our system's flexibility and maintainability by fostering a cleaner and more modular design.