我知道有注册表、singleton等模式。
是否有处理对象的设计模式,这些对象不应该是全局可用的,而只能在局部区域中使用,这样我就可以为同一角色拥有不同的对象?例如,如果我使用单例,我在整个程序中有相同的对象,这不是我想要的。
处理对象创建的常见模式是"四人帮"创建模式。
引用维基百科:
创造性模式是为您创建对象的模式,而不是让您直接实例化对象。这使您的程序在决定需要为给定情况创建哪些对象时具有更大的灵活性。
- 抽象工厂组对象工厂有一个共同的主题
- 生成器通过分离构造和表示来构造复杂的对象
- Factory方法在不指定要创建的确切类的情况下创建对象
- Prototype通过克隆现有对象来创建对象
- Singleton将类的对象创建限制为只能创建一个实例
要了解创建实例的一般经验,请查看维基百科关于GRASP:的文章中的创建者模式
一般来说,如果以下一项或多项适用,则B类应负责创建a类实例:
- B的实例包含或组合聚合A的实例
- B的实例记录A的实例
- B的实例密切使用A的实例
- B的实例具有A实例的初始化信息,并在创建时传递该信息
GoF创意模式对此进行了补充。例如,如果您使用Factory模式,Factory可能会保存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创建的a具有相同的寿命,那就另当别论了。
您也可以将创建委托给Factory:,而不是让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中得到了链接——如何将依赖关系传递给深度嵌套的对象?。基本思想是在工厂或构建器中或通过依赖注入容器预先组装所有可以合理组装的东西。
TL;DR
如果要在本地作用域中创建对象(因为它依赖于运行时信息),请将创建逻辑封装到工厂或构建器中,并将这些逻辑注入到保存运行时信息的对象中,并在需要时从那里将信息委托给工厂和构建器。