Object-Oriented Programming (OOP) is a powerful and widely used programming paradigm in modern software development. Understanding the fundamental principles of OOP is essential for building well-structured, maintainable, and scalable applications. This article will dive into the most important principles, including SOLID, DRY, KISS, Encapsulation, Inheritance, Polymorphism, and Abstraction.
SOLID is a set of design principles intended to help developers create software that is easy to understand, flexible, and maintain. SOLID stands for:
A class should have only one reason to change. This means that a class should have only one responsibility. For example, instead of one class handling both user authentication and data storage, split it into two separate classes: one for authentication and one for storage.
// Ví dụ vi phạm SRP class User { public function authenticate($username, $password) { // Xác thực người dùng } public function save($userData) { // Lưu trữ dữ liệu người dùng } } // Ví dụ tuân thủ SRP class Authenticator { public function authenticate($username, $password) { // Xác thực người dùng } } class UserRepository { public function save($userData) { // Lưu trữ dữ liệu người dùng } }
A software entity (class, module, function) should be open for extension, but closed for modification. This means you can add new functionality to the class without modifying the existing source code. This is usually done using abstraction (abstract classes or interfaces) and inheritance.
// Ví dụ vi phạm OCP class Discount { public function applyDiscount($price, $type) { if ($type == 'fixed') { return $price - 10; } elseif ($type == 'percentage') { return $price * 0.9; } } } // Ví dụ tuân thủ OCP interface DiscountStrategy { public function applyDiscount($price); } class FixedDiscount implements DiscountStrategy { public function applyDiscount($price) { return $price - 10; } } class PercentageDiscount implements DiscountStrategy { public function applyDiscount($price) { return $price * 0.9; } } class Discount { private $strategy; public function __construct(DiscountStrategy $strategy) { $this->strategy = $strategy; } public function applyDiscount($price) { return $this->strategy->applyDiscount($price); } }
Subclasses must be able to replace their parent classes without changing the correctness of the program. In other words, an object of a subclass can be used anywhere an object of a parent class can be used without causing an error.
// Ví dụ vi phạm LSP class Rectangle { protected $width; protected $height; public function setWidth($width) { $this->width = $width; } public function setHeight($height) { $this->height = $height; } public function getArea() { return $this->width * $this->height; } } class Square extends Rectangle { public function setWidth($width) { $this->width = $width; $this->height = $width; } public function setHeight($height) { $this->width = $height; $this->height = $height; } } // Ví dụ tuân thủ LSP (Có thể cần một thiết kế khác, ví dụ sử dụng interface) interface Shape { public function getArea(); } class Rectangle implements Shape { protected $width; protected $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function getArea() { return $this->width * $this->height; } } class Square implements Shape { protected $side; public function __construct($side) { $this->side = $side; } public function getArea() { return $this->side * $this->side; } }
Don't force a class to depend on methods it doesn't use. Instead, split large interfaces into smaller, more specific interfaces, so that classes only need to implement the methods they really need.
// Ví dụ vi phạm ISP interface Worker { public function work(); public function eat(); } class Human implements Worker { public function work() { // Làm việc } public function eat() { // Ăn } } class Robot implements Worker { public function work() { // Làm việc } public function eat() { // Robot không ăn, nhưng vẫn phải triển khai phương thức này throw new Exception("Robot cannot eat."); } } // Ví dụ tuân thủ ISP interface Workable { public function work(); } interface Eatable { public function eat(); } class Human implements Workable, Eatable { public function work() { // Làm việc } public function eat() { // Ăn } } class Robot implements Workable { public function work() { // Làm việc } }
// Ví dụ vi phạm DIP class LightBulb { public function turnOn() { // Bật đèn } public function turnOff() { // Tắt đèn } } class Switch { private $bulb; public function __construct(LightBulb $bulb) { $this->bulb = $bulb; } public function operate() { // Bật/Tắt đèn $this->bulb->turnOn(); // Phụ thuộc trực tiếp vào LightBulb } } // Ví dụ tuân thủ DIP interface Switchable { public function turnOn(); public function turnOff(); } class LightBulb implements Switchable { public function turnOn() { // Bật đèn } public function turnOff() { // Tắt đèn } } class Switch { private $device; public function __construct(Switchable $device) { $this->device = $device; } public function operate() { // Bật/Tắt thiết bị $this->device->turnOn(); // Phụ thuộc vào interface Switchable } }
The DRY principle encourages you to avoid repeating code. If you find yourself writing the same code over and over again, consider creating a function, class, or module to reuse that code. This helps reduce errors, makes the code easier to maintain, and improves readability.
// Ví dụ vi phạm DRY function calculateAreaRectangle($width, $height) { return $width * $height; } function calculatePerimeterRectangle($width, $height) { return 2 * ($width + $height); } // Ví dụ tuân thủ DRY class Rectangle { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function getArea() { return $this->width * $this->height; } public function getPerimeter() { return 2 * ($this->width + $this->height); } }
The KISS principle emphasizes the importance of keeping code simple and easy to understand. Avoid unnecessarily complex solutions. Focus on solving the problem in the most direct and efficient way. The simpler the code, the easier it is to maintain and debug.
// Ví dụ vi phạm KISS (Quá phức tạp cho một việc đơn giản) function isEven($number) { $binary = decbin($number); $lastDigit = substr($binary, -1); return $lastDigit == 0; } // Ví dụ tuân thủ KISS (Đơn giản và dễ hiểu) function isEven($number) { return $number % 2 == 0; }
Encapsulation is the practice of hiding data inside a class and only allowing access through public methods (getters and setters). This protects the data from being directly accessed and modified from outside the class, while also allowing you to control how the data is used.
class Person { private $name; private $age; public function __construct($name, $age) { $this->name = $name; $this->age = $age; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getAge() { return $this->age; } public function setAge($age) { if ($age >= 0) { $this->age = $age; } else { throw new Exception("Age cannot be negative."); } } }
Inheritance allows one class (child class) to inherit properties and methods from another class (parent class). This helps in code reuse and creates a hierarchy of related classes. For example, you can have an Animal
class and child classes like Dog
, Cat
, Bird
inherit from Animal
.
class Animal { protected $name; public function __construct($name) { $this->name = $name; } public function makeSound() { echo "Generic animal sound"; } } class Dog extends Animal { public function makeSound() { echo "Woof!"; } } class Cat extends Animal { public function makeSound() { echo "Meow!"; } }
Polymorphism allows objects of different classes to be treated in the same way. There are two main types of polymorphism:
// Ví dụ về đa hình thời gian chạy (ghi đè phương thức) interface Shape { public function getArea(); } class Rectangle implements Shape { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function getArea() { return $this->width * $this->height; } } class Circle implements Shape { private $radius; public function __construct($radius) { $this->radius = $radius; } public function getArea() { return pi() * $this->radius * $this->radius; } } function printArea(Shape $shape) { echo "Area: " . $shape->getArea(); } $rectangle = new Rectangle(5, 10); $circle = new Circle(7); printArea($rectangle); // Output: Area: 50 printArea($circle); // Output: Area: 153.93804002589985
Abstraction is the process of hiding complex details and displaying only the necessary information to the user. This reduces the complexity of the system and makes the code easier to understand. In OOP, abstraction is often achieved using abstract classes and interfaces.
// Ví dụ về lớp trừu tượng abstract class Vehicle { protected $model; protected $year; public function __construct($model, $year) { $this->model = $model; $this->year = $year; } abstract public function startEngine(); public function getModel() { return $this->model; } } class Car extends Vehicle { public function startEngine() { echo "Car engine started!"; } } class Motorcycle extends Vehicle { public function startEngine() { echo "Motorcycle engine started!"; } } // Bạn không thể tạo một đối tượng trực tiếp từ lớp Vehicle (abstract) // $vehicle = new Vehicle("Generic Vehicle", 2023); // Lỗi $car = new Car("Toyota Camry", 2023); $car->startEngine(); // Output: Car engine started!
Understanding and applying OOP principles such as SOLID, DRY, KISS, Encapsulation, Inheritance, Polymorphism and Abstraction are essential to writing high-quality, maintainable and scalable code. By following these principles, you can build more robust and flexible software applications.