设计模式是软件工程中可复用问题的通用解决方案。虽然设计模式与语言无关,但具体实现时会受到语言特性的深刻影响。C++、Java和PHP在面向对象机制上的差异(多重继承、接口、访问控制、虚函数、静态方法等)导致同一设计模式的实现方式各不相同。本文将分析工厂模式、单例模式、观察者模式等在三种语言中的典型实现差异。
工厂模式用于封装对象创建逻辑。在Java中,工厂通常是一个返回抽象接口类型的静态方法,利用多态返回具体子类。Java的反射可以用于创建通用工厂。C++中实现工厂模式需要处理对象的所有权,通常返回std::unique_ptr或std::shared_ptr。由于C++没有接口关键字,工厂通常返回抽象基类指针。C++的模板也可以实现“策略工厂”,在编译期决定创建哪种对象。PHP的工厂模式与Java类似,但PHP支持动态类名,可以使用变量作为类名($class = 'MyClass'; new $class();),使得工厂实现更简洁。PHP 8的命名参数和属性进一步简化了工厂配置。
单例模式确保一个类只有一个实例。Java实现单例有多种方式:饿汉式(静态成员)、懒汉式(双重检查锁)、静态内部类、枚举单例。枚举单例被认为是线程最安全且防止反射攻击的方式。C++中的单例实现需考虑线程安全和内存释放。Meyers单例(C++11起)使用局部静态变量,编译器保证线程安全,且自动销毁。C++单例还需注意禁用拷贝和赋值。PHP的单例通常通过私有构造函数、私有静态实例和静态getInstance方法实现,由于PHP单线程模型,无需考虑线程安全。但PHP中的单例在每次请求时都会创建新实例(除非使用Swoole等常驻内存环境),因此传统PHP Web应用中的单例实际作用域仅是单次请求。
观察者模式用于事件驱动架构。Java中使用接口(Observer和Observable已废弃),现在更推荐使用PropertyChangeListener或自定义事件监听器。Java的lambda表达式使得观察者注册更加简洁。C++中观察者模式可以使用函数对象(std::function)、信号槽库(如Boost.Signals2)或简单的虚函数。由于C++的析构函数确定性,需要注意观察者生命周期(使用weak_ptr防止悬垂引用)。PHP的观察者模式可以利用SplSubject和SplObserver接口,也可以利用数组存储回调(callable类型)。PHP 8的Closure::fromCallable和属性使得观察者实现更灵活。
策略模式定义一系列可互换的算法。Java中策略通常定义为接口,具体策略实现接口,上下文持有接口引用。可以使用lambda简化单一方法接口。C++的策略模式可以通过函数指针、std::function或模板(编译期策略)实现。模板策略在运行时无法切换,但零开销。PHP的策略模式与Java类似,支持接口和匿名函数。PHP的callable类型可以是函数名、数组[$obj, 'method']或闭包,非常灵活https://chancin.com。
装饰器模式动态添加职责。Java中装饰器需要实现与被装饰对象相同的接口,通过组合包装。C++中由于缺少反射,手动实现装饰器较繁琐,但可以通过继承和模板辅助。PHP装饰器模式常结合接口和魔术方法(__call)实现动态转发。PHP的trait特性也可以用于代码复用,但不同于装饰器。
适配器模式转换接口。Java和PHP都通过实现目标接口并在内部持有被适配者引用完成。C++中适配器可使用多重继承(同时继承目标接口和被适配者类),但需要注意二义性。
代理模式控制访问。Java有动态代理(Proxy.newProxyInstance)可以运行时生成代理类,C++没有内置动态代理,需要手动编写代理类或使用字节码技术(不常见)。PHP也有类似动态代理的实现,通过魔术方法call、get等,非常方便实现远程代理或延迟加载。
从这些对比可以看出,Java的设计模式实现通常最为规范(接口+注解);C++由于缺乏接口和反射,实现相对底层,但模板提供替代方案;PHP则利用动态特性使某些模式更简洁,但也可能导致隐式依赖。理解语言特性对模式实现的影响,有助于写出更符合语言惯例的代码。