【问题标题】:Duplicate conditional statements in the factory工厂中重复的条件语句
【发布时间】:2016-10-28 11:57:37
【问题描述】:

如何摆脱它们?我想知道是否有解决此问题的模式或其他东西。基本上我需要根据另一个类的 type 属性来实例化一个具体的子类,即如果 type=1 然后 new A,否则如果 type=2 然后 new B 等等。我最终在具有 type 属性的类:

/**
 * Get a ticket decorator based on the ticket type
 * @return ReferralService\TicketDecorator
 * @throws Exception
 */
public function getTicketDecorator(): ReferralService\TicketDecorator
{
    if (!$this->code) {
        throw new Exception("Couldn't create a ticket wrapper based on the type without a code");
    }

    /**
     * The debug service
     * @var Debug\Service $debugService
     */
    $debugService = app(Debug\Service::class);
    $debugService->setDebug(config('referral.debug'));

    switch ($this->code) {
        case self::TYPE_FEEDBACK:
            return new ReferralService\TicketDecorator\FeedbackTicketDecorator($debugService);
            break;
        case self::TYPE_BIRTHDAY:
            return new ReferralService\TicketDecorator\BirthdayTicketDecorator($debugService);
            break;
        case self::TYPE_NEW_PARTNER:
            return new ReferralService\TicketDecorator\PartnerTicketDecorator($debugService);
            break;
        default:
            throw new Exception(sprintf("Couldn't instantiate a ticket decorator based on the %s type", $this->code));

    }
}

/**
 * Instantiate a private page based on the ticket type
 * @param ReferralService\Service $service
 * @param Referrer $referrer
 * @param Ticket $ticket
 * @return ReferralService\Page\PrivatePage
 * @throws Exception
 */
public function getPrivatePage(ReferralService\Service $service, Referrer $referrer, Ticket $ticket): ReferralService\Page\PrivatePage
{
    if (!$this->code) {
        throw new Exception("Couldn't create a private page based on the type without a code");
    }

    switch ($this->code) {
        case self::TYPE_FEEDBACK:
            return new ReferralService\Page\PrivatePage\EmailReference($this->service, $referrer, $ticket);
            break;
        case self::TYPE_BIRTHDAY:
            return new ReferralService\Page\PrivatePage\Birthday($this->service, $referrer, $ticket);
            break;
        case self::TYPE_NEW_PARTNER:
            return new ReferralService\Page\PrivatePage\Partner($this->service, $referrer, $ticket);
            break;
        default:
            throw new Exception(sprintf("Could't find a page for the type", $this->code));
    }
}

工厂中的每个方法都测试类型字段,这对我来说看起来很笨拙。我以为每种类型都有一个单独的子类,并使用没有条件语句的工厂方法,但我不能用 Laravel 模型这样做。

【问题讨论】:

  • 它不漂亮,但它没有任何问题。这是拥有工厂的一种非常易读、合法的方式。我不会担心太多
  • 您已经可以删除所有无用的break;returns 之后)。
  • @Casimir 对,我已经习惯了一直使用 break 语句
  • @Andrew 是的,除非我有更优雅的东西,否则我将保持原样。谢谢!
  • 我实际上没有,因为您写的内容非常实用且切中要害。正如我所说,我会接受它,这是合法的。

标签: php design-patterns factory factory-pattern


【解决方案1】:

replace conditionals by polymorphism 是一种非常常见的重构模式。

您可以只实现一个工厂来创建专门的TicketTypes,并在票证类型上实现工厂方法来创建具体的TicketDecoratorPrivatePage

但是,请记住,这确实会在 TicketType 和它创建的具体类之间引入耦合。如果最好避免这种耦合,请坚持您的初始设计。

【讨论】:

  • 为什么有人想避免这种耦合?
  • @Sergey 主要看抽象的层次和模块之间的耦合。例如。如果您有 ProductType 位于 Product 模块中,但该类型用于选择 Shipping 模块中的运输策略,那么您可能希望避免将 Product 耦合到 Shipping,因为它是 Shipping应该可能耦合到Product。不是最好的例子,但希望它对你有意义。
  • 是的,谢谢。我可能会在 TicketType 类中创建一个工厂方法,该方法会挑选出一个具体类来覆盖抽象工厂,该抽象工厂使用多态性生成具体页面和装饰器。它与您的答案有点不同,但仍然几乎相同。
  • @Sergey 这里抽象工厂的优势是什么?
  • 原来的条件语句被放置在一个 Eloquent ORM 模型中。如果我对每个表执行多个模型,Eloquent 将无法正确管理它们,例如将查询结果转换为集合时。在我的情况下,抽象工厂似乎是更简单快速的解决方案,我不必触摸和思考 Eloquent。
【解决方案2】:

嗯,这是我的解决方案。我已经将TicketType(带有条件语句的原始帖子中的类)与所有内容分离,因此它不知道页面/装饰器。

同时,我创建了具体的票证类别:BirthdayTicketPartnerTicket

我在Ticket 类中放置了一个工厂方法,该方法根据票证的类型生成票证类的具体实例 - BirthdayTicketPartnerTicket 等。具体票证类产生我需要的东西(页面,装饰器)使用上面@plalx建议的多态性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-10
    • 2019-12-31
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多