【问题标题】:Optionable pattern?可选模式?
【发布时间】:2013-09-12 18:39:33
【问题描述】:

我正在尝试想出一种方法,让任何(或至少一组)班级都有选择的能力。

这是我想要的通用接口

interface OptionableInterface
{
    public function getOption($key);
    public function setOption($key, $value);
    public function getOptions();
    public function setOptions($options, $merge);
    public function removeOption($key);
}

我曾考虑过使用上述接口实现一个具体类,然后根据需要对其进行扩展,但由于 PHP 没有多重继承,这可能是个问题。

另一种方法是使用装饰器模式。但我不确定这是否是装饰器模式的正确用法。

有什么想法吗?我现在坚持使用 PHP 5.2(也许以后可以更改为 5.3,在这种情况下我可以使用特征)。

【问题讨论】:

  • 这被称为mixin - 你在类中包含你的扩展而不实际继承它:stackoverflow.com/questions/6876925/…
  • traits 在 PHP 5.4 中首次发布。因此,如果您只升级到 PHP 5.3,您将无法使用它们
  • 为什么不直接明确选项,这样我就不必每次想使用可选对象来查找它可以采用的选项时都查找您的文档?将使代码更具可读性。废弃那个界面。
  • 是的,对不起,我记错了 5.4 是:P 我想我没有任何特点,因为我的公司不会很快升级到 5.4
  • 所有这些类都很难弄清楚。没有人可以通过查看您的类的 API 知道他们可以采取哪些选项。因此,不要让一个通用但不说话的 OptionableInterface 给你的类提供有意义的设置器,比如 setFoo(42) 而不是 setOption('foo', 42);

标签: php design-patterns


【解决方案1】:

首先,你要小心in order not to violate the Single-Responsibility Principle。做某事并考虑到选项的对象应该将这些选项作为参数。

interface CookieOptionsInterface
{
    public function setPath($path);
    public function getPath();
    public function setTtl();
    public function getTtl();

    //...The rest if needed 
}


class Cookie
{
    protected $cookieOptions;
    
    public function __construct(CookieOptionsInterface $cookieOptions)
    {
       $this->cookieOptions = $cookieOptions;
    }

    public function write(array $pair)
    {
        foreach ($pair as $key => $value) {
              setcookie($key, $value, $this->cookieOptions->getTtl() + time(), $this->cookieOptions->getPath());
        }
    }

    // .. The rest
}

需要注意的核心点:

  • Cookie 应该完全不知道默认选项。它只做一件事——Cookie CRUD 操作

  • CookieOptionsInterface 应该完全不知道 Cookie 类。它只做一件事——抽象选项访问

  • 不会破坏封装

  • 它遵循依赖注入(因此您可以轻松地模拟$cookieOptions

  • 它遵循单一职责原则

  • 由于CookieOptionsInterface 完全解耦,您可以继承自(从而减少其他选项的代码重复,例如CookieBar

【讨论】:

  • 这看起来不错,让我用我的代码试试看是否还有问题
  • “Cookie 应该完全不知道默认选项”:不,不应该,因为它们是 cookie 的组成部分。因此,提供 API 来在 Cookie 上设置这些值是非常好的。当 cookie 具有 write() 和 setName() 等时,不会违反 SRP,因为它们属于一起。他们是一项责任的一部分。通过注意到 write 方法使用了这些选项,您可以很容易地看到 Cookie 具有很高的内聚性。拥有 CookieOptions 的唯一好处是在 Cookie 之间共享默认选项。
  • 我听到了你在说什么,因为我过去的想法和你过去的完全一样。如果您有setName(),那么您还应该实现getName() 以满足POLS。重点?你最终会在Cookie 类中创建一堆setter 和geters。由于 setter 和 getter 服务于 data container 的职责,并且由于 Cookie 基本上服务于 CRUD 操作,因此它们应该被解耦。顺便说一句,这个想法不是我的,它来自这里:sitepoint.com/the-single-responsibility-principle
  • 我并不是说当你有一个 Setter 时你应该有一个 Getter。一点也不。在大多数情况下,您都希望避免使用 Tell Don't Ask。我要说的是,选项中的数据构成了 Cookie 作为一个有凝聚力的整体(加上名称/值对)的身份。它们不是一些孤立的数据部分。 Cookie 不仅仅使用它们。 他们。为这些设置 Setter 是可以的,因为这些将成为构成改变的一个原因的“操作集”的一部分。它们一起改变。
  • 所以你的 CookieOptions 真的把一个单一的职责分成了两个。如果您想在其他 Cookie(享元)中重用 CookieOptions 也没关系,但不必遵循 SRP。我们可以争辩说,将 Cookie 写入标头是一项单独的责任。这将保证一个 CookieWriter ,而不是拆分有凝聚力的 Cookie 数据。但是凭借 InformationExpert,编写器方法确实 属于 Cookie。在旁注中,butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod 可能比您在站点上找到的任何关于 SOLID 的内容都要好
【解决方案2】:

您可以使用组合来代替继承或接口。也许是这样的:

class SomethingThatHasOptions {
  public $options;
  public function __construct () {
    $this->options = new OptionProvider ();
  }
}

class OptionProvider {
    public function getOption($key) { ... }
    public function setOption($key, $value) { ... }
    public function getOptions() { ... }
    public function setOptions($options, $merge) { ... }
    public function removeOption($key) { ... }
}

然后你可以像这样使用它:

$optionable = new SomethingThatHasOptions;
$optionable->options->setOption('foo', 42);

【讨论】:

  • 谢谢!这行得通,但我正在尝试找到一种更优雅的方法:) 如果找不到更好的方法,可能会回到你的答案。
  • $optionable->options->... 一个对象应该完全控制它的状态和实现
  • @DaveJust:是的。最好将选项作为依赖项注入。
猜你喜欢
  • 1970-01-01
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 2016-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多