我已经开始重构一个小应用程序来使用一个小的 DI 容器而不是 $注册表::getstuff();在我的类(class)中调用我将它们注入(inject)容器中。
这提出了 2 个问题,
Q1 -> 我扩展 Pimple DI class并创建一个容器,该容器具有特定于每个需要 DI 的对象的依赖项。然后我提供对象 the whole shebang ,并在将 DI 的对象分配给我正在构建的对象的类属性的构造函数中对其进行 decrontruct。
我应该在 new object() 调用中分离对象吗?我只是觉得这样更容易,但看到我现在是一个单人团队,我只想确认我有正确的方法。
Q2 -> 我发现如果我在一些主要类上执行此操作,则我一直传递的 $registry 对象将不再需要,这是使用 DI 的正常结果吗,不再注册?我可能在容器中注入(inject)了一两个单例,但看起来这就是我所需要的,甚至那些也可以很容易地被消除,因为 DI 有一个返回相同对象实例的 share() 属性,有效地消除了需要对于单例。这是摆脱需要注册表/单例的应用程序的方法吗,因为如果是这样,就太简单了。
最佳答案
Q2:
如果您遍历整个 $registry 对象....那么您的 Registry 并不是真正意义上的 Registry(如 Fowler描述了它)。
Registry 或多或少是一个具有 get/set 方法的全局对象(“众所周知”)。 在 PHP 中,Registry 实现的两个常见原型(prototype)是
作为单例
class RegistryAsSingleton
{
public static function getInstance (){
//the singleton part
}
public function getStuff ()
{
//some stuff accessed thanks to the registry
}
}
到处都是静态方法
class RegistryAsStatic
{
public static function getStuff()
{
}
}
到处传递您的 Registry 使其成为一个对象:一个除了提供对其他对象的引用之外没有更大用途的容器。
您的 DI 容器(使用您在 OP 中建议的 Pimple)本身就是一个注册表:它是众所周知的并且使您能够从任何地方获取组件。
是的,我们可以说您的 DI 容器将通过执行相同的功能来消除注册表的要求和必要性。
BUT(总有一个但是)
Registry are always guilty until proven innocent (Martin Fowler)
如果您使用DI 容器 来替换您的注册表,这可能是错误的。
例如:
//probably a Wrong usage of Registry
class NeedsRegistry
{
public function asAParameter(Registry $pRegistry)
{
//Wrong dependency on registry where dependency is on Connection
$ct = $pRegistry->getConnection();
}
public function asDirectAccess ()
{
//same mistake, more obvious as we can't use another component
$ct = Registry::getInstance()->getConnection();
}
}
//probably a wrong replacement for Registry using DI Container
class NeedsContainer
{
public function asAParameter(Container $pRegistry)
{
//We are dependent to the container with no needs,
//this code should be dependent on Connection
$ct = $pContainer->getConnection();
}
public function asDirectAccess ()
{
//should not be dependent on container
$ct = Container::getInstance()->getConnection();
}
}
为什么会这样?因为你的代码并没有比以前少依赖,它仍然依赖于一个没有提供明确目标(我们这里可能会想到接口(interface))的组件(注册中心或容器)
注册表模式在某些情况下很有用,因为它是一种定义组件或数据(例如全局配置)的简单且相当便宜的方法。
通过移除依赖项来重构上述示例而不依赖 DI 的方法是:
class WasNeedingARegistry
{
public function asAParameter (Connection $pConnection)
{
$pConnection->doStuff();//The real dependency here, we don't care for
//a global registry
}
}
//the client code would be like
$wasNeedingARegistry = new WasNeedingARegistry();
$wasNeedingARegistry->setConnection($connection);
当然,如果客户端代码不知道连接,这可能是不可能的,这可能是您最初可能结束使用注册表的原因。
现在 DI 开始发挥作用
使用 DI 让我们的生活更美好,因为它会处理依赖关系,并使我们能够在准备好使用的状态下访问依赖关系。
在您的代码中的某处,您将配置您的组件:
$container['connection'] = function ($container) {
return new Connection('configuration');
};
$container['neededARegistry'] = function ($container) {
$neededARegistry = new NeededARegistry();
$neededARegistry->setConnection($container['connection']);
return $neededARegistry;
};
现在您拥有了重构代码所需的一切:
// probably a better design pattern for using a Registry
class NeededARegistry
{
public function setConnection(Connection $pConnection)
{
$this->connection = $pConnection;
return $this;
}
public function previouslyAsDirectAccess ()
{
$this->connection->doStuff();
}
}
//and the client code just needs to know about the DI container
$container['neededARegistry']->previouslyAsDirectAccess();
“客户端”代码应尽可能隔离。客户端应该负责并注入(inject)自己的依赖项(通过 set- 方法)。客户端不应该负责处理其依赖项的依赖项。
class WrongClientCode
{
private $connection;
public function setConnection(Connection $pConnection)
{
$this->connection = $pConnection;
}
public function callService ()
{
//for the demo we use a factory here
ServiceFactory::create('SomeId')
->setConnection($this->connection)
->call();
//here, connection was propagated on the solely
// purpose of being passed to the Service
}
}
class GoodClientCode
{
private $service;
public function setService(Service $pService)
{
//the only dependency is on Service, no more connection
$this->service = $pService;
}
public function callService ()
{
$this->service->setConnection($this->connection)
->call();
}
}
DI 容器将使用已正确配置其连接的服务配置 GoodClientCode
至于单例方面,是的,它将使您摆脱它们。 希望这有帮助
关于php - 让 DI 容器替换全局 $registry 对象是一种好习惯吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6122214/
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功
假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl
我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/
我有以下内容:text.gsub(/(lower)(upper)/,'\1\2')我可以将\2替换为大写吗?类似于:sed-e's/\(abc\)/\U\1/'这在Ruby中可行吗? 最佳答案 查看gsub文档:str.gsub(模式){|匹配|block}→new_str在block形式中,当前匹配字符串作为参数传入,$1、$2、$`、$&、$'等变量将被适当设置。block返回的值将替换为每次调用的匹配项。"alowerupperb".gsub(/(lower)(upper)/){|s|$1+""+$2.upcase}
我有一个gem,它有一个根据Rails.env的不同行为的方法:defself.envifdefined?(Rails)Rails.envelsif...现在我想编写一个规范来测试这个代码路径。目前我是这样做的:Kernel.const_set(:Rails,nil)Rails.should_receive(:env).and_return('production')...没关系,只是感觉很丑。另一种方法是在spec_helper中声明:moduleRails;end而且效果也很好。但也许有更好的方法?理想情况下,这应该有效:rails=double('Rails')rails.sho
我希望用户从一个模型的三个选项中选择一个。即我有一个模型视频,可以被评为正面/负面/未知目前我有三列bool值(pos/neg/unknown)。这是处理这种情况的最佳方式吗?为此,表单应该是什么样的?目前我有类似的东西但显然它允许多项选择,而我试图将它限制为只有一个..怎么办? 最佳答案 如果要使用字符串列,让我们说rating。然后在你的表单中:#...#...它只允许一个选择编辑完全相同但使用radio_button_tag: 关于ruby-on-rails-Rails单选按钮-模