Factory design pattern implementation doubts
Asked Answered
F

2

6

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?
Forfend answered 16/11, 2015 at 15:43 Comment(4)
I don't see a use for MessageRaw here, why don't you use Message directly? Anyway if you insist on using this, then try to always send the entire object which means you send new Message() instead of $chat_id, $text (first it would allow you to typehint the variable; and 2nd it would give you access to all available methods on that object).Latimer
@Latimer I'm sorry, I'm not following you. I'm just setting an object, I'm not sending anything (yet). Later I'll send it but I want to set the Keyboard (if necessary) in a nice way, and don't know whether this has to be done while creating the objectForfend
Ahmad is referring to Dependency Injection : code.tutsplus.com/tutorials/…Transcalent
@Transcalent are you suggesting something like "new Message()"; for simple messages and new KeyboardMessage(new Keyboard) for messages with a Keyboard?Forfend
P
4

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.

Plasterwork answered 16/11, 2015 at 16:47 Comment(3)
OK, this seems to me like the right answer because I want to use Factories and I think it fits this situation. Now is time to turn all your text into code, but that's how we learn right?Forfend
@Forfend Paying it forward :)Plasterwork
Yeah - a Factory Pattern best fits when "stamping out" different types of objects with a common Interface - a ProductFactory could create lots of different types of products but they might all have a common Interface covering things like SKU, price, weight and so on... good EDIT on this answer ;)Transcalent
T
2

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"));
Transcalent answered 16/11, 2015 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.