Structural design patterns are concerned with the composition of classes and objects to form larger structures. They help in creating a system of objects that work together in an efficient and flexible manner. In PHP, structural design patterns are used to simplify the code and make it more modular and maintainable.
Table of Contents
Types of Structural Design Patterns in PHP
There are several types of structural design patterns in PHP,
- Adapter pattern.
- Decorator pattern.
- Facade pattern.
- Proxy pattern.
- Bridge pattern.
Adapter Pattern
The adapter pattern is used when we want to convert the interface of a class into another interface that clients expect. This pattern allows us to use classes that otherwise wouldn’t be compatible due to different interfaces.
interface MediaPlayer {
public function play($filename);
}
class MP3Player implements MediaPlayer {
public function play($filename) {
echo "Playing MP3 file: {$filename}\n";
}
}
interface AdvancedMediaPlayer {
public function playMP4($filename);
}
class MP4Player implements AdvancedMediaPlayer {
public function playMP4($filename) {
echo "Playing MP4 file: {$filename}\n";
}
}
class MediaAdapter implements MediaPlayer {
private $advancedMediaPlayer;
public function __construct(AdvancedMediaPlayer $advancedMediaPlayer) {
$this->advancedMediaPlayer = $advancedMediaPlayer;
}
public function play($filename) {
$extension = pathinfo($filename, PATHINFO_EXTENSION);
if ($extension === 'mp4') {
$this->advancedMediaPlayer->playMP4($filename);
} else {
echo "Invalid media type: {$extension}\n";
}
}
}
// Usage
$mp3Player = new MP3Player();
$mp4Player = new MP4Player();
$mediaAdapter = new MediaAdapter($mp4Player);
$mediaAdapter->play("song.mp3"); // Invalid media type: mp3
$mp3Player->play("song.mp3"); // Playing MP3 file: song.mp3
$mediaAdapter->play("movie.mp4"); // Playing MP4 file: movie.mp4
Decorator Pattern
The decorator pattern is used to dynamically add new behavior to an object without changing its interface. This allows us to extend the functionality of an object at runtime.
interface Car {
public function getDescription();
public function getPrice();
}
class BasicCar implements Car {
public function getDescription() {
return "Basic Car";
}
public function getPrice() {
return 10000;
}
}
abstract class CarDecorator implements Car {
protected $car;
public function __construct(Car $car) {
$this->car = $car;
}
public function getDescription() {
return $this->car->getDescription();
}
public function getPrice() {
return $this->car->getPrice();
}
}
class LeatherSeats extends CarDecorator {
public function getDescription() {
return $this->car->getDescription() . ", leather seats";
}
public function getPrice() {
return $this->car->getPrice() + 2000;
}
}
class Sunroof extends CarDecorator {
public function getDescription() {
return $this->car->getDescription() . ", sunroof";
}
public function getPrice() {
return $this->car->getPrice() + 1500;
}
}
// Usage
$basicCar = new BasicCar();
$carWithLeatherSeats = new LeatherSeats($basicCar);
$carWithLeatherSeatsAndSunroof = new Sunroof($carWithLeatherSeats);
echo $carWithLeatherSeatsAndSunroof->getDescription(); // Basic Car, leather seats, sunroof
echo "\n";
echo $carWithLeatherSeatsAndSunroof->getPrice(); // 13500
Facade Pattern
The facade pattern is used to provide a simplified interface to a complex system of classes. It allows us to reduce the complexity of the code and make it easier to use.
class SubsystemA {
public function operationA() {
echo "Subsystem A operation\n";
}
}
class SubsystemB {
public function operationB() {
echo "Subsystem B operation\n";
}
}
class SubsystemC {
public function operationC() {
echo "Subsystem C operation\n";
}
}
class Facade {
private $subsystemA;
private $subsystemB;
private $subsystemC;
public function __construct() {
$this->subsystemA = new Subsystem
Proxy Pattern
The Proxy pattern is a structural design pattern that provides a surrogate or placeholder for an object in order to control access to it. It allows us to create an object that acts as a representative of another object. The Proxy object hides the original object from the client and provides additional functionality, such as caching, security, and remote access.
<?php
interface DatabaseAccess {
public function connect();
}
class DatabaseAccessProxy implements DatabaseAccess {
private $databaseAccess;
private $user;
public function __construct(DatabaseAccess $databaseAccess, $user) {
$this->databaseAccess = $databaseAccess;
$this->user = $user;
}
public function connect() {
if ($this->user != "admin") {
throw new Exception("Access denied.");
}
$this->databaseAccess->connect();
}
}
class DatabaseConnection implements DatabaseAccess {
public function connect() {
echo "Connected to database.";
}
}
// Usage example
$adminConnection = new DatabaseAccessProxy(new DatabaseConnection(), "admin");
$userConnection = new DatabaseAccessProxy(new DatabaseConnection(), "user");
$adminConnection->connect(); // Output: "Connected to database."
$userConnection->connect(); // Output: Exception: "Access denied."
Bridge Pattern
The Bridge pattern is a structural design pattern that separates an object’s abstraction from its implementation. It allows us to decouple an abstraction from its implementation, so that both can vary independently. This pattern involves creating two parallel hierarchies: one for the abstraction and one for the implementation, and using composition to link them together.
<?php
interface DrawingAPI {
public function drawCircle($x, $y, $radius);
}
class DrawingAPI1 implements DrawingAPI {
public function drawCircle($x, $y, $radius) {
echo "API1.circle at ($x,$y) radius $radius.";
}
}
class DrawingAPI2 implements DrawingAPI {
public function drawCircle($x, $y, $radius) {
echo "API2.circle at ($x,$y) radius $radius.";
}
}
abstract class Shape {
protected $drawingAPI;
public function __construct(DrawingAPI $drawingAPI) {
$this->drawingAPI = $drawingAPI;
}
public abstract function draw();
}
class CircleShape extends Shape {
private $x, $y, $radius;
public function __construct($x, $y, $radius, DrawingAPI $drawingAPI) {
parent::__construct($drawingAPI);
$this->x = $x;
$this->y = $y;
$this->radius = $radius;
}
public function draw() {
$this->drawingAPI->drawCircle($this->x, $this->y, $this->radius);
}
}
// Usage example
$shapes = [
new CircleShape(1, 2, 3, new DrawingAPI1()),
new CircleShape(5, 7, 11, new DrawingAPI2()),
];
foreach ($shapes as $shape) {
$shape->draw(); // Output: "API1.circle at (1,2) radius 3." and "API2.circle at (5,7) radius 11."
}
Conclusion
Structural Design Patterns in PHP provide efficient and maintainable code solutions for software development. The Adapter, Decorator, Facade, Proxy, and Bridge Patterns offer unique advantages and disadvantages that developers should consider when choosing the appropriate design pattern for their project. The use of these design patterns can greatly enhance the overall quality and scalability of a PHP application.