我知道有注册表、单例等模式。
是否有用于处理不应全局可用但仅在本地区域中的对象的设计模式,以便我可以为同一角色拥有不同的对象?例如,如果我使用单例,我在整个程序中都有相同的对象,这不是我想要的。
我知道有注册表、单例等模式。
是否有用于处理不应全局可用但仅在本地区域中的对象的设计模式,以便我可以为同一角色拥有不同的对象?例如,如果我使用单例,我在整个程序中都有相同的对象,这不是我想要的。
处理对象创建的常见模式是四组创建模式。
引用维基百科:
创建模式是为您创建对象的模式,而不是让您直接实例化对象。这使您的程序在决定需要为给定情况创建哪些对象时具有更大的灵活性。
对于将责任放在哪里创建实例的一般经验法则,请查看Wikipedia 关于 GRASP 的文章中的 Creator 模式:
一般来说,如果以下一个或多个适用,则 B 类应负责创建 A 类的实例:
- B 的实例包含或组合聚合 A 的实例
- B 的实例记录 A 的实例
- B 的实例密切使用 A 的实例
- B 的实例具有 A 实例的初始化信息并在创建时传递它。
GoF 创建模式对此进行了补充。例如,如果您使用工厂模式,工厂可能会保存 A 实例的初始化信息,并将在创建时传递它:
class BMWFactory implements CarFactory
{
// properties, ctors and stuff …
public function createCar($color)
{
return new Bmw(
$color,
// … more arguments
);
}
}
另一方面,这也意味着集合可能被允许创建新实例,因为 B 然后“包含或复合聚合 A 的实例”,这将导致类似
class Cars
{
// properties, ctors and stuff …
public function createCar($color)
{
return new Car(/* … */);
}
}
$bmws = new Cars($config['bmw']);
$bmws[] = $bmws->createCar('alpine white');
我们可以争论当创建的对象具有独立的生命周期时,让您的集合创建对象而不是仅仅存储它们是否是一个好主意。如果 B 创建的 As 具有相同的生命周期,那是另一回事。
除了让 B 创建 As,您还可以将创建委托给工厂:
class Cars
{
// properties, ctors and stuff …
public function createCar($color)
{
return $this->carFactory->createNewCar(/* … */);
}
}
$bmws = new Cars(new BmwFactory);
$bmws[] = $bmws->createCar('alpine white');
您应该始终仔细考虑在new
代码中使用何处的原因是因为它引入了从 B 到 A 的强耦合。注入工厂放松了这种耦合,特别是因为我们编写了一个接口而不是具体的类。
强耦合使得在单元测试中模拟协作者变得很困难。引用 Miško Hevery 关于单元测试的如何思考“新”操作符
但依赖注入如此重要的原因在于,在单元测试中,您希望测试应用程序的一小部分。要求是您可以独立于整个系统构建应用程序的那个小子集。如果您将应用程序逻辑与图形构造(新操作符)混合使用,那么除了应用程序中的叶节点之外,任何东西都无法进行单元测试。
在他的 Google Talk 中进一步解释了将创建与应用程序逻辑分离的想法,该问题在我的相关答案Dependency Hell 中进行了链接——如何将依赖关系传递给深度嵌套的对象?. 基本思想是在工厂或构建器中或通过依赖注入容器预先组装您可以合理组装的所有内容。
如果您想在本地范围内创建对象(因为它依赖于运行时信息),请将创建逻辑封装到工厂或构建器中,并将它们注入到保存运行时信息的对象中,然后将信息从那里委托给工厂和构建器需要。