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?
In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.
Here are two additional consequences of the Factory Method pattern: Provides hooks for subclasses. Creating objects inside a class with a factory method is always more flexible than creating an object directly. Factory Method gives subclasses a hook for providing an extended version of an object.
The factory design pattern is used when we have a superclass with multiple sub-classes and based on input, we need to return one of the sub-class. This pattern takes out the responsibility of the instantiation of a class from the client program to the factory class.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With