简体   繁体   中英

Factory design pattern implementation doubts

I'm trying to create a bot for Telegram messenger while trying to learn OOP. I'm really lost on how to approach the problem. I have a Message entity, with all the getters and setters, this I think it's pretty straigthforward. My problem is that I want to create two (or more) types of factories

1) a simple message where you just feed the factory with the chat_id you want to send the message and the text, that could be something like this:

<?php

namespace Telegram\Domain\Factory;

use Telegram\Domain\Entity\Message;

class MessageRaw extends MessageAbstract {
    public function createMessage($chat_id, $text) {
        $message = new Message();
        $message->setChatId($chat_id);
        $message->setText($text);

        return $message;
    }
}

where MessageAbstract is

<?php

namespace Telegram\Domain\Factory;

abstract class MessageAbstract {
    abstract public function createMessage($chat_id, $text);
}

2) A message with a keyboard (Telegram gives you the possibility to include a custom keyboard when you send a message). I have the problem here, the keyboard is given as an array, so this would be one more argument to a createMessage.

So my problem is, should I always give the $keyboard argument whether it is a simple message or a message with keyboard? Or are these two types of messages different enough so that they should be created from different classes (I think not)? Or maybe I shouldn't do it in a factory, but with setters and getters?

TLDR: How to create an object with different number of arguments in a fancy way, something like this

$MessageRaw = new MessageRaw($chat_id, $text);
$MessageNumericKeyboard = new MessageNumericKeyboard($chat_id, $text); //numeric keyboard is standard so can be set in the createMessage Function
$MessageCustomKeyboard = new MessageCustomKeyboard($chat_id, $text, ['A', 'B']); //should it be done like this?

I agree with @CD001 regarding subtyping / extending so I won't repeat his answer, but you can still use a factory pattern by identifying the required type using dependency injection and returning the appropriate object.

You can either include a dedicated parameter for this dependency on the factory method, or you can use method overloading to inject it and only check on those specific types (if there are additional classes that might be returned outside of the two indicated).

Sticking with the factory pattern will really help you expand this down the road without too much additional work, cutting corners now will only lead to pain later.


EDIT:

Injection (nb: I've included a type param to cover one possible expansion technique via string, but this could just as easily be an injected type object as well, up to you. Also included option of injecting your Message attributes at the same time for the message constructor, so you get back a fully instantiated object rather than an empty DTO)

class MessageFactory {
    public static function get($type,$attributes=NULL,$keyboard=NULL) {
        if ($keyboard && !($keyboard instanceof MessageKeyboardInterface)) {
            // ... trigger some exception here
        } elseif (!$keyboard) {
            $keyboard = new BasicKeyboard(); 
        }
        switch($type):
        case('standard'):
            return new StandardMessage($attributes,$keyboard);
            break;
        case('short'):
            return new ShortMessage($attributes,$keyboard);
            break;
        case('long'):
            return new LongMessage($attributes,$keyboard);
            break;
        endswitch;
    }   
}

This way all of your Message objects can use the same interface, can use custom or basic keyboards, and can be expanded easily. You could of course expand the $attributes and utilize your individual setters after instantiation, or loop over them in the constructor if you have many. I personally don't like a ton of parameters in the contsructor and would rather loop over an array of optional params.

This looks like a better fit for common or garden sub-typing rather than using a Factory pattern, for instance:

class Message {

    // e.g.
    protected $chatId;
    protected $text;

    // constructor
    public function __construct($chatId, $text) {
        $this->setChatId($chatId);
        $this->setText($text);
    }

    // ... stuff
}

final class MessageCustomKeyboard extends Message {

    // e.g.
    private $_keyboard;

    // constructor overrides parent
    public function __construct($chatId, $text, array $keyboard) {
        parent::__construct($chatId, $text);
        $this->setKeyboard($keyboard);
    }

    // ... stuff
}

$message = new Message(1, "Hello World");
$messageCustomKeybaord = new MessageCustomKeyboard(2, "Hello again", array("a", "b"));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM