Skip to content

nullodyssey/strategy-pattern

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎯 Strategy Pattern: First-Match Implementation

A comprehensive PHP tutorial demonstrating the Strategy Pattern with a "first-match" approach for data transformation operations.

PHP Version License Tests Code Style

πŸ“š Table of Contents

🧠 What is the Strategy Pattern?

The Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.

Key Components

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     Context     │───▢│    Strategy      │◀───│ ConcreteStrategyβ”‚
β”‚ (Transformer)   β”‚    β”‚   (Interface)    β”‚    β”‚ (JsonTransformerβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β–²               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                 β”‚               β”‚ ConcreteStrategyβ”‚
                                 └───────────────│ (XmlTransformer β”‚
                                                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                 β”‚ ConcreteStrategyβ”‚
                                                 β”‚ (CsvTransformer β”‚
                                                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ’ͺ Why is it Powerful?

πŸ”„ Runtime Algorithm Selection

Switch between different algorithms at runtime without changing the client code:

$transformer = new TransformerProvider($strategies);

// Same interface, different behaviors
$result = $transformer->transform($data, Type::JSON);  // Uses JsonTransformer
$result = $transformer->transform($data, Type::XML);   // Uses XmlTransformer
$result = $transformer->transform($data, Type::CSV);   // Uses CsvTransformer

🧩 Easy Extension

Add new transformation formats without modifying existing code:

// Add new strategy without touching existing code
class PdfTransformer implements TransformerInterface {
    public function supports(Type $type): bool {
        return $type === Type::PDF;
    }
    
    public function transform(array $data): string {
        // PDF transformation logic
    }
}

πŸ”’ SOLID Principles Compliance

  • Single Responsibility: Each strategy handles one transformation format
  • Open/Closed: Open for extension, closed for modification
  • Dependency Inversion: Depends on abstractions, not concretions

πŸ§ͺ Testability

Easy to unit test each strategy independently:

public function testJsonTransformer(): void
{
    $transformer = new JsonTransformer();
    $result = $transformer->transform(['name' => 'John']);
    
    $this->assertJson($result);
}

βœ… When to Use It

Perfect Scenarios

πŸ“Š Multiple Data Formats

When you need to support multiple output formats:

// Transform user data in different formats
$userData = ['id' => 1, 'name' => 'John', 'email' => 'john@example.com'];

$transformer->transform($userData, Type::JSON);  // For APIs
$transformer->transform($userData, Type::XML);   // For legacy systems
$transformer->transform($userData, Type::CSV);   // For spreadsheets

πŸ” Payment Processing

Different payment gateways with same interface:

$processor = new PaymentProcessor([
    new StripePayment(),
    new PayPalPayment(),
    new SquarePayment()
]);

$processor->process($amount, PaymentType::CREDIT_CARD);

πŸ“§ Notification Systems

Multiple notification channels:

$notifier = new NotificationService([
    new EmailNotification(),
    new SmsNotification(),
    new PushNotification()
]);

$notifier->send($message, NotificationType::EMAIL);

πŸ—‚οΈ File Storage

Different storage backends:

$storage = new FileStorage([
    new LocalStorage(),
    new S3Storage(),
    new GoogleCloudStorage()
]);

$storage->store($file, StorageType::CLOUD);

❌ When NOT to Use It

Avoid in These Scenarios

🚫 Single Algorithm

Don't use Strategy Pattern if you only have one way to do something:

// ❌ Overkill - just use a simple method
class Calculator {
    public function add(int $a, int $b): int {
        return $a + $b;  // Only one way to add numbers
    }
}

🚫 Simple Conditional Logic

For simple if/else scenarios:

// ❌ Strategy Pattern is overkill here
public function getDiscount(string $customerType): float {
    return match($customerType) {
        'premium' => 0.20,
        'regular' => 0.10,
        default => 0.0
    };
}

🚫 Algorithms Rarely Change

When the algorithm is stable and unlikely to change:

// ❌ String length calculation doesn't need strategies
public function getStringLength(string $text): int {
    return strlen($text);  // This won't change
}

🚫 Performance-Critical Code

When every nanosecond matters:

// ❌ Strategy overhead might be too much for tight loops
for ($i = 0; $i < 1000000; $i++) {
    $result = $strategy->calculate($data[$i]);  // Overhead adds up
}

πŸ—οΈ Project Overview

This project demonstrates a "first-match" Strategy Pattern implementation for data transformation operations. The system automatically selects the first strategy that supports the requested transformation type.

Key Features

  • 🎯 First-match selection algorithm
  • πŸ”„ Runtime strategy switching
  • πŸ“¦ Easy strategy extension
  • πŸ§ͺ Comprehensive test coverage
  • πŸ“ Detailed documentation
  • 🐳 Docker development environment

πŸ›οΈ Architecture

Core Components

// Strategy Interface
interface TransformerInterface {
    public function transform(array $data): string;
    public function supports(Type $type): bool;
}

// Context (Strategy Manager)
class TransformerProvider implements TransformerProviderInterface {
    public function transform(array $data, Type $type): string {
        foreach ($this->strategies as $strategy) {
            if ($strategy->supports($type)) {
                return $strategy->transform($data);  // First match wins!
            }
        }
        throw new InvalidArgumentException("No strategy found for type: {$type->value}");
    }
}

// Concrete Strategies
class JsonTransformer implements TransformerInterface { /* ... */ }
class XmlTransformer implements TransformerInterface { /* ... */ }
class CsvTransformer implements TransformerInterface { /* ... */ }

Strategy Selection Flow

1. Client requests transformation with Type::JSON
2. TransformerProvider iterates through strategies
3. First strategy that supports JSON is selected
4. Selected strategy performs the transformation
5. Result is returned to client

πŸš€ Quick Start

Prerequisites

  • PHP 8.1+
  • Docker & Docker Compose
  • Make

Installation

# Clone the repository
git clone https://github.com/nullodyssey/first-match-strategy-pattern.git
cd first-match-strategy-pattern

# Start the development environment
make start

# Install dependencies
make composer c="install"

# Run tests to verify everything works
make test

πŸ“– Usage Examples

Basic Usage

use NullOdyssey\FirstMatchStrategy\TransformerProvider;
use NullOdyssey\FirstMatchStrategy\Strategy\{JsonTransformer, XmlTransformer, CsvTransformer};
use NullOdyssey\FirstMatchStrategy\Type;

// Create transformer with strategies
$transformer = new TransformerProvider([
    new JsonTransformer(),
    new XmlTransformer(),
    new CsvTransformer()
]);

// Sample data
$userData = [
    'id' => 123,
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'active' => true
];

// Transform in different formats
$jsonResult = $transformer->transform($userData, Type::JSON);
// Output: {"id":123,"name":"John Doe","email":"john@example.com","active":true}

$xmlResult = $transformer->transform($userData, Type::XML);
// Output: <root><id>123</id><name>John Doe</name>...</root>

$csvResult = $transformer->transform($userData, Type::CSV);
// Output: id,name,email,active\n123,"John Doe","john@example.com",1

Using the Facade

use NullOdyssey\FirstMatchStrategy\DataTransformer;
use NullOdyssey\FirstMatchStrategy\Type;

$transformerService = new DataTransformer();
$data = ['name' => 'Jane Smith', 'role' => 'Developer'];

// Simple one-liner transformations
$transformerService($data, Type::JSON);  // Triggers JSON transformation
$transformerService($data, Type::XML);   // Triggers XML transformation
$transformerService($data, Type::CSV);   // Triggers CSV transformation

Adding Custom Strategies

use NullOdyssey\FirstMatchStrategy\TransformerInterface;
use NullOdyssey\FirstMatchStrategy\Type;

// Extend the Type enum first
enum Type: string {
    case JSON = 'json';
    case XML = 'xml';
    case CSV = 'csv';
    case YAML = 'yaml';  // New type
}

// Create custom strategy
class YamlTransformer implements TransformerInterface {
    public function transform(array $data): string {
        return yaml_emit($data);
    }
    
    public function supports(Type $type): bool {
        return $type === Type::YAML;
    }
}

// Use with existing transformer
$transformer = new TransformerProvider([
    new JsonTransformer(),
    new XmlTransformer(),
    new CsvTransformer(),
    new YamlTransformer()  // Add your custom strategy
]);

$result = $transformer->transform($data, Type::YAML);

πŸ§ͺ Running Tests

# Run all tests
make test

# Run specific test file
make sh
./vendor/bin/phpunit tests/Unit/TransformerProviderTest.php

# Run with coverage
make sh
./vendor/bin/phpunit --coverage-html coverage

πŸ“Š Real-World Applications

E-commerce Platform

// Order transformation in multiple formats for different systems
$orderTransformer = new TransformerProvider([
    new JsonTransformer(),     // For REST APIs
    new XmlTransformer(),      // For SOAP services
    new CsvTransformer(),      // For accounting software
    new PdfTransformer()       // For customer invoices
]);

$order = ['id' => 'ORD-123', 'total' => 99.99, 'items' => [...]];
$receipt = $orderTransformer->transform($order, Type::PDF);

Analytics Dashboard

// Report generation in multiple formats
$reportTransformer = new TransformerProvider([
    new ExcelTransformer(),    // For business users
    new CsvTransformer(),      // For data analysis
    new JsonTransformer(),     // For API consumption
    new ChartTransformer()     // For visualizations
]);

$analyticsData = ['revenue' => 10000, 'users' => 500, 'conversion' => 2.5];
$report = $reportTransformer->transform($analyticsData, Type::EXCEL);

πŸ› οΈ Development Commands

# Docker & Environment
make build          # Build Docker images
make start          # Build and start containers
make down           # Stop containers
make sh             # Connect to PHP container

# Dependencies
make composer       # Run composer commands
make vendor         # Install production dependencies

# Code Quality
make test           # Run PHPUnit tests
make phpstan        # Run static analysis
make phpcs          # Run code style fixer
make quality        # Run both phpstan and phpcs

🎯 Learning Objectives

After exploring this project, you'll understand:

  • βœ… Strategy Pattern fundamentals
  • βœ… When and why to use it
  • βœ… First-match selection algorithm
  • βœ… SOLID principles in practice
  • βœ… PHP 8.1+ features (enums, readonly classes)
  • βœ… Test-driven development
  • βœ… Design pattern trade-offs

🀝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ‘¨β€πŸ’» Author

nullodyssey


Happy coding! πŸš€

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors