设计模式精解:单例、工厂、策略模式实战


设计模式精解:单例、工厂、策略模式实战

设计模式是面向对象设计中反复出现的问题的通用解决方案。掌握设计模式不仅能帮助你写出更灵活、更可维护的代码,还能让你在与团队成员讨论架构时使用共同的语言。本文将深入讲解三种最常用的设计模式——单例模式、工厂模式和策略模式,并通过PHP和JavaScript的实战代码展示它们的应用场景。

一、单例模式

单例模式确保一个类只有一个实例,并提供全局访问点。在需要全局状态管理的场景中非常有用。

经典应用场景:数据库连接、配置管理、日志记录器、缓存管理器

// PHP - 数据库连接单例
class Database
{
    private static ?Database $instance = null;
    private PDO $pdo;
    
    private function __construct()
    {
        $this->pdo = new PDO(
            "mysql:host=localhost;dbname=myapp",
            "root",
            "password",
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
    
    public static function getInstance(): Database
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function getConnection(): PDO
    {
        return $this->pdo;
    }
    
    // 防止克隆和反序列化
    private function __clone() {}
    public function __wakeup() { throw new Exception("Cannot unserialize"); }
}

// 使用
$db = Database::getInstance()->getConnection();
$stmt = $db->query("SELECT * FROM users");

二、工厂模式

工厂模式将对象的创建逻辑封装起来,调用者不需要知道具体创建了哪个类的实例。它分为三种:简单工厂、工厂方法和抽象工厂。

// 支付系统 - 工厂模式
interface PaymentGateway
{
    public function pay(float $amount): array;
    public function refund(string $transactionId): array;
}

class AlipayGateway implements PaymentGateway
{
    public function pay(float $amount): array
    {
        // 调用支付宝API
        return ["gateway" => "alipay", "amount" => $amount, "status" => "success"];
    }
    
    public function refund(string $transactionId): array
    {
        return ["gateway" => "alipay", "refund_id" => $transactionId, "status" => "success"];
    }
}

class WechatGateway implements PaymentGateway
{
    public function pay(float $amount): array
    {
        return ["gateway" => "wechat", "amount" => $amount, "status" => "success"];
    }
    
    public function refund(string $transactionId): array
    {
        return ["gateway" => "wechat", "refund_id" => $transactionId, "status" => "success"];
    }
}

// 工厂类
class PaymentFactory
{
    public static function create(string $type): PaymentGateway
    {
        return match($type) {
            "alipay" => new AlipayGateway(),
            "wechat" => new WechatGateway(),
            default => throw new InvalidArgumentException("不支持的支付方式: $type")
        };
    }
}

// 使用 - 调用者不需要知道具体实现
$gateway = PaymentFactory::create("alipay");
$result = $gateway->pay(99.99);

三、策略模式

策略模式定义一系列算法,将每个算法封装起来,并使它们可以互相替换。它让算法的变化独立于使用算法的客户端。

// 价格计算策略
interface PricingStrategy
{
    public function calculate(float $originalPrice): float;
}

class RegularPricing implements PricingStrategy
{
    public function calculate(float $originalPrice): float
    {
        return $originalPrice;
    }
}

class DiscountPricing implements PricingStrategy
{
    public function __construct(private float $discount) {}
    
    public function calculate(float $originalPrice): float
    {
        return $originalPrice * $this->discount;
    }
}

class MemberPricing implements PricingStrategy
{
    public function calculate(float $originalPrice): float
    {
        return $originalPrice * 0.8;  // 会员八折
    }
}

// 上下文类
class Order
{
    private PricingStrategy $pricingStrategy;
    
    public function setPricingStrategy(PricingStrategy $strategy): void
    {
        $this->pricingStrategy = $strategy;
    }
    
    public function calculateTotal(float $price): float
    {
        return $this->pricingStrategy->calculate($price);
    }
}

// 使用 - 运行时切换策略
$order = new Order();

// 普通用户
$order->setPricingStrategy(new RegularPricing());
echo $order->calculateTotal(100);  // 100

// 促销打七折
$order->setPricingStrategy(new DiscountPricing(0.7));
echo $order->calculateTotal(100);  // 70

// 会员价格
$order->setPricingStrategy(new MemberPricing());
echo $order->calculateTotal(100);  // 80

四、三种模式的对比

模式目的解决的问题应用场景
单例控制实例数量避免重复创建资源密集型对象数据库连接、配置、日志
工厂封装创建逻辑解耦对象创建与使用支付网关、通知渠道
策略封装算法族消除条件分支,支持运行时切换价格计算、排序算法

设计模式不是银弹,不要为了使用模式而使用。当你在代码中看到大量if-else或switch-case分支时,可以考虑策略模式;当创建对象的逻辑变得复杂时,考虑引入工厂模式;当你需要全局唯一的资源管理时,使用单例模式。合理运用设计模式,可以让代码更加灵活、可测试和可维护。


0.069858s