微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

php – 具有静态类的适配器模式

我正在寻找一种在PHP 5.x中使用静态类实现Adapter模式的好方法.

我希望使用它的一个例子是Pythonos.path.join()的对应物.
我会有两个适应器,一个Windows和一个Linux适配器类.

我认为将这些类实现为静态类是合理的,因为它们没有“上下文”.他们不需要存储任何状态并且每次我需要时创建一个实例似乎是多余的 – 因此我正在寻找一种干净的方式来实现它.

让我们考虑以下虚假实现:

    static public function join(){
        $parts = func_get_args();
        $joined = array(MY_MAGICALLY_PASSED_DEFAULT_PATH_PREFIX);
        foreach($parts as $part){
            $part = self::$adaptee->cleanPath($path);
            if(self::$adaptee->isAbsolute($part)){
                $joined = array($part);
            }
            else{
                $joined[] = $part;
            }
        }
        return implode(PATH_SEParaTOR, $joined);
    }

您将注意到的第一件事是它假设一个名为adaptee的初始化静态成员,该成员将保留必要的,依赖于操作系统的实现细节.

这需要我有一个任意命名的类似静态构造函数函数,我会在声明类之后立即调用它. (用这种方法困扰我的另一件事).

当然,我可以在每个方法调用上初始化一个本地$adaptee变量,但这似乎是不合适的,我将不得不在需要适配器的每个其他静态函数中复制它.

现在……对于PHP的类实现细节:它们不是第一类对象,所以我不能只将类作为参数传递.在这个例子中,它需要我创建Adaptee作为非静态(这个术语是什么?)类,然后实例化它并最终将它分配给Adapter类的静态$adaptee成员变量.

也许这就是我所拥有的这种奇怪而完全主观的想法……但我真的觉得这样做是不合适的.您对更好的实施有什么想法吗?

我已经有的另一个想法是,存储适配器的类名,而是使用call_user_func,但我觉得使用这种方法并不太舒服.

更新

我可能没有正确描述,所以我将尝试在更新中解释这一点:

我不是在寻找如何获得底层操作系统,但我想有一个简洁的方法,一个静态类的行为不同,取决于操作系统是Linux,Windows,FreeBSD还是其他东西.

我想到了适配器模式,但由于我没有静态构造函数,我无法真正初始化类.一种方法是在每次公共静态方法调用开始时初始化它(或者只检查它是否被初始化).

另一种可能性是,创建一个类似静态构造函数方法,并在声明后立即调用它.这可能就是诀窍,但我只是想知道还有什么其他的,可能更为重要的方法来实现这一目标.

至于我最初的例子:
它应该是一个实用函数,它不需要真正保留任何类型的状态,所以我不是在寻找任何类型的Path-Object.我想要的是一个Path工厂函数,它返回一个字符串,而不必在每次调用时区分不同的操作系统. “library”-thing导致我为我的相关实用程序函数创建一个静态类作为伪命名空间,以及需要支持适配器模式的不同实现细节.现在我正在寻找一种优雅的方式,将两者结合起来.

解决方法:

当你让它们静止时,你会用脚射击自己.你不能注入静态类,所以你总是会耦合到全局范围,因为你会在任何地方硬编码静态调用,维护它们将成为一场噩梦.你也不能模仿它们(好吧,PHPUnit可以,但它只能启用测试代码,否则将是不可测试的).

只需创建一个实例并使用常规函数并省去一些担忧.使用静力学没有任何优势.而且性能影响完全可以忽略不计.

我可能会为适配器和适配器创建一个接口来实现

interface IPathAdapter
{
    public function cleanPath($path);
    public function isAbsolutePath($part);
    // more …
}

然后做一些类似的事情

class Path implements IPathAdapter
{
    protected $_adapter;

    public function __construct(IPathAdapter $adapter)
    {
        $this->_adapter = $adapter;
    }

    public function cleanPath($path)
    {
        $this->_adapter->cleanPath($part);
    }

    public function isAbsolutePath($part)
    {
        $this->_adapter->isAbsolutePath($part);
    }

    // more …

    public function join(){
        $parts = func_get_args();
        $joined = array($this->getScriptPath());
        foreach($parts as $part){
            $part = $this->cleanPath($path);
            if ($this->isAbsolutePath($part)){
                $joined = array($part);
            } else{
                $joined[] = $part;
            }
        }
        return implode($this->getPathSeparator(), $joined);
    }
}

因此,当我想使用Path时,我必须这样做

$path = new Path(new PathAdapter_Windows);

如果你不能注入适配器,我可能会去你已经建议的路由,并将Adapter类名作为参数传递,然后在Path中实例化它.或者我将适当的适配器的检测完全留给Path类,例如让它检测操作系统,然后实例化所需的内容.

如果你想自动检测,请查看Does PHP have a function to detect the OS it’s running on?.我可能会编写一个单独的类来处理标识,然后使它成为Path类的依赖项,例如:

public function __construct(IDetector $detector = NULL)
{
    if($detector === NULL){
        $detector = new OSDetector;
    }
    $this->_detector = $detector; 
}

我注射的原因是因为它允许我改变实施,例如在UnitTests中模拟Detector,但也可以忽略在运行时注入.然后它将使用认的OSDetector.使用检测器,检测操作系统并在Path或专用工厂中的某处创建适当的适配器.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐