Welcome back, Explorer! Today, we delve into the heart of writing maintainable and scalable software through Code Decoupling and Modularization. We will explore techniques to minimize dependencies, making our code more modular, manageable, and easier to maintain.
What are Code Decoupling and Modularization?
Understanding Code Dependencies and Why They Matter
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal
Decoupling ensures our code components are independent by reducing the connections between them, resembling the process of rearranging pictures with a bunch of puzzles. Here's a PHP example:
// Coupled codeclass AreaCalculator{ public function calculateArea($length, $width, $shape) { if ($shape == "rectangle") { return $length * $width; // calculate area for rectangle } elseif ($shape == "triangle") { return ($length * $width) / 2; // calculate area for triangle } return 0; }}
After refactoring:
// Decoupled codeclass RectangleAreaCalculator{ public function calculateRectangleArea($height, $width) { return $height * $width; // function to calculate rectangle area }}class TriangleAreaCalculator{ public function calculateTriangleArea($height, $width) { return ($height * $width) / 2; // function to calculate triangle area }}
In the coupled code, the calculateArea method performs many operations — it calculates areas for different shapes. In the decoupled code, we split these operations into different, independent methods, leading to clean and neat code.
On the other hand, Modularization breaks down a program into smaller, manageable units or modules.
Code dependencies occur when one part of the code relies on another part to function. In tightly coupled code, these dependencies are numerous and complex, making the management and maintenance of the codebase difficult. By embracing decoupling and modularization strategies, we can significantly reduce these dependencies, leading to cleaner, more organized code.
Consider the following scenario in an e-commerce application:
// Monolithic code with high dependenciesclass Order{ private $items; private $prices; private $discountRate; private $taxRate; public function __construct($items, $prices, $discountRate, $taxRate) { $this->items = $items; $this->prices = $prices; $this->discountRate = $discountRate; $this->taxRate = $taxRate; } public function calculateTotal() { $total = 0; foreach ($this->prices as $price) { $total += $price; } $total -= $total * $this->discountRate; $total += $total * $this->taxRate; return $total; } public function printOrderSummary() { $total = $this->calculateTotal(); echo "Order Summary: Items: " . implode(", ", $this->items) . ", Total after tax and discount: $" . number_format($total, 2) . "\n"; }}
In the example with high dependencies, the Order class is performing multiple tasks: it calculates the total cost by applying discounts and taxes and then prints an order summary. This design makes the Order class complex and harder to maintain.
In the modularized code example below, we decouple the responsibilities by creating separate DiscountCalculator and TaxCalculator classes. Each class has a single responsibility: one calculates the discount, and the other calculates the tax. The Order class simply uses these calculators. This change reduces dependencies and increases the modularity of the code, making each class easier to understand, test, and maintain.
// Decoupled and modularized codeclass DiscountCalculator{ public static function applyDiscount($price, $discountRate) { return $price - ($price * $discountRate); }}class TaxCalculator{ public static function applyTax($price, $taxRate) { return $price + ($price * $taxRate); }}class Order{ private $items; private $prices; private $discountRate; private $taxRate; public function __construct($items, $prices, $discountRate, $taxRate) { $this->items = $items; $this->prices = $prices; $this->discountRate = $discountRate; $this->taxRate = $taxRate; } public function calculateTotal() { $total = 0; foreach ($this->prices as $price) { $total += $price; } $total = DiscountCalculator::applyDiscount($total, $this->discountRate); $total = TaxCalculator::applyTax($total, $this->taxRate); return $total; } public function printOrderSummary() { $total = $this->calculateTotal(); echo "Order Summary: Items: " . implode(", ", $this->items) . ", Total after tax and discount: $" . number_format($total, 2) . "\n"; }}
Introduction to Separation of Concerns
The principle of Separation of Concerns (SoC) allows us to focus on a single aspect of our program at one time.
// Code not following SoCclass InfoPrinter{ public function getFullInfo($name, $age, $city, $job) { echo "$name is $age years old.\n"; echo "$name lives in $city.\n"; echo "$name works as a $job.\n"; }}
// Code following SoCclass InfoPrinter{ public function printAge($name, $age) { echo "$name is $age years old.\n"; // prints age } public function printCity($name, $city) { echo "$name lives in $city.\n"; // prints city } public function printJob($name, $job) { echo "$name works as a $job.\n"; // prints job } public function getFullInfo($name, $age, $city, $job) { $this->printAge($name, $age); // sends name and age to `printAge` $this->printCity($name, $city); // sends name and city to `printCity` $this->printJob($name, $job); // sends name and job to `printJob` }}
By applying SoC, we broke down the getFullInfo method into separate methods, each dealing with a different concern: age, city, and job.
Brick by Brick: Building a Codebase with Modules
Just like arranging books on different shelves, creating modules helps structure our code neatly and efficiently. In PHP, each class can be placed in a separate file. Here's an example:
// The content of RectangleAreaCalculator.phpclass RectangleAreaCalculator{ public function calculateRectangleArea($length, $width) { return $length * $width; }}
// The content of TriangleAreaCalculator.phpclass TriangleAreaCalculator{ public function calculateTriangleArea($baseLength, $height) { return 0.5 * $baseLength * $height; }}
// Using the content of RectangleAreaCalculator and TriangleAreaCalculatorrequire 'RectangleAreaCalculator.php';require 'TriangleAreaCalculator.php';class Program{ public static function main() { $rectangleCalc = new RectangleAreaCalculator(); $triangleCalc = new TriangleAreaCalculator(); $rectangleArea = $rectangleCalc->calculateRectangleArea(5, 4); // calculates rectangle area $triangleArea = $triangleCalc->calculateTriangleArea(3, 4); // calculates triangle area echo "Rectangle Area: " . $rectangleArea . "\n"; echo "Triangle Area: " . $triangleArea . "\n"; }}Program::main();
The methods for calculating the areas of different shapes are defined in separate files — classes in PHP. In another file, we instantiate and use these classes.
Lesson Summary
Excellent job today! You've learned about Code Decoupling and Modularization, grasped the value of the Separation of Concerns principle, and explored code dependencies and methods to minimize them. Now, prepare yourself for some exciting practice exercises. These tasks will reinforce these concepts and enhance your coding skills. Until next time!
// Coupled codeclass AreaCalculator{ public function calculateArea($length, $width, $shape) { if ($shape == "rectangle") { return $length * $width; // calculate area for rectangle } elseif ($shape == "triangle") { return ($length * $width) / 2; // calculate area for triangle } return 0; }}
PHP
// Decoupled codeclass RectangleAreaCalculator{ public function calculateRectangleArea($height, $width) { return $height * $width; // function to calculate rectangle area }}class TriangleAreaCalculator{ public function calculateTriangleArea($height, $width) { return ($height * $width) / 2; // function to calculate triangle area }}
PHP
// Monolithic code with high dependenciesclass Order{ private $items; private $prices; private $discountRate; private $taxRate; public function __construct($items, $prices, $discountRate, $taxRate) { $this->items = $items; $this->prices = $prices; $this->discountRate = $discountRate; $this->taxRate = $taxRate; } public function calculateTotal() { $total = 0; foreach ($this->prices as $price) { $total += $price; } $total -= $total * $this->discountRate; $total += $total * $this->taxRate; return $total; } public function printOrderSummary() { $total = $this->calculateTotal(); echo "Order Summary: Items: " . implode(", ", $this->items) . ", Total after tax and discount: $" . number_format($total, 2) . "\n"; }}
PHP
// Decoupled and modularized codeclass DiscountCalculator{ public static function applyDiscount($price, $discountRate) { return $price - ($price * $discountRate); }}class TaxCalculator{ public static function applyTax($price, $taxRate) { return $price + ($price * $taxRate); }}class Order{ private $items; private $prices; private $discountRate; private $taxRate; public function __construct($items, $prices, $discountRate, $taxRate) { $this->items = $items; $this->prices = $prices; $this->discountRate = $discountRate; $this->taxRate = $taxRate; } public function calculateTotal() { $total = 0; foreach ($this->prices as $price) { $total += $price; } $total = DiscountCalculator::applyDiscount($total, $this->discountRate); $total = TaxCalculator::applyTax($total, $this->taxRate); return $total; } public function printOrderSummary() { $total = $this->calculateTotal(); echo "Order Summary: Items: " . implode(", ", $this->items) . ", Total after tax and discount: $" . number_format($total, 2) . "\n"; }}
PHP
// Code not following SoCclass InfoPrinter{ public function getFullInfo($name, $age, $city, $job) { echo "$name is $age years old.\n"; echo "$name lives in $city.\n"; echo "$name works as a $job.\n"; }}
PHP
// Code following SoCclass InfoPrinter{ public function printAge($name, $age) { echo "$name is $age years old.\n"; // prints age } public function printCity($name, $city) { echo "$name lives in $city.\n"; // prints city } public function printJob($name, $job) { echo "$name works as a $job.\n"; // prints job } public function getFullInfo($name, $age, $city, $job) { $this->printAge($name, $age); // sends name and age to `printAge` $this->printCity($name, $city); // sends name and city to `printCity` $this->printJob($name, $job); // sends name and job to `printJob` }}
PHP
// The content of RectangleAreaCalculator.phpclass RectangleAreaCalculator{ public function calculateRectangleArea($length, $width) { return $length * $width; }}
PHP
// The content of TriangleAreaCalculator.phpclass TriangleAreaCalculator{ public function calculateTriangleArea($baseLength, $height) { return 0.5 * $baseLength * $height; }}
PHP
// Using the content of RectangleAreaCalculator and TriangleAreaCalculatorrequire 'RectangleAreaCalculator.php';require 'TriangleAreaCalculator.php';class Program{ public static function main() { $rectangleCalc = new RectangleAreaCalculator(); $triangleCalc = new TriangleAreaCalculator(); $rectangleArea = $rectangleCalc->calculateRectangleArea(5, 4); // calculates rectangle area $triangleArea = $triangleCalc->calculateTriangleArea(3, 4); // calculates triangle area echo "Rectangle Area: " . $rectangleArea . "\n"; echo "Triangle Area: " . $triangleArea . "\n"; }}Program::main();