PHP думает ООП: отправка и получение сообщения: я правильно понимаю? - PullRequest
2 голосов
/ 15 августа 2011

См. Обновления в конце:

Текущая кодовая база имеет 1,4 тыс. Строк чисто процедурного кода, который отправляет SMS-сообщения (имеет бизнес-логику, подключение к БД и все в одном гигантскомif условно вложено с бесчисленным количеством if с, без функций, полным литералов, подлинным кандидатом DailyWTF?).И я решил укусить пулю и переписать всю эту чертову штуку с нуля.
Дело в том, что это будет мой первый опыт ООП.Я прочитал как можно больше об ООД и передовой практике и решил начать с чего-то простого.Я хочу реализовать отправку / получение сообщений (в первую очередь текстовых / SMS, но MMS, электронная почта должны быть включены в будущем).Поэтому я написал следующее как мой первый коммит

interface MessageInterface {
    public function setType($type); public function getType();
    public function setContent($content); public function getContent();
    public function sendMessage(); //add more functionalities later
}
class Message implements MessageInterface {
    private $_type; private $_content;
    public function setType($type) { $this->_type = $type; }
    public function getType() { return $this->_type; }
    public function setContent($content) {
        if ($this->_type = 'text') {
            $this->_content = $content;
            return TRUE; // report success
        } else { return FALSE; } // report failure
    }
    public function getContent() { return $this->_content; }
    public function sendMessage() {
        if ($this->_type == 'text') {
            print "Sending ".$this->getContent()." as ".$this->getType()." message\n";
            //do the actual implementation later
            return TRUE;  // report success
        } else { return FALSE; } // report failure
    }
}
$msg = new Message();
$msg->setType('text');
print $msg->getType() . "\n"; //text
$result = $msg->setContent('Hello World!');
if($result)
    $result2 = $msg->sendMessage(); //Sending Hello World! as text message
if($result2)
    print 'Hurray ! Mission accomplished !!';

Я не думаю, что правильно применяю концепцию полиморфизма.Я чувствую, что if не должно быть там, верно?Возможно, они необходимы для setContent(), но как насчет sendMessage()?Поэтому я решил разделить отправляющую часть на свою собственную class SendMessage implements SendMessageInterface.который будет иметь свои собственные переменные для $server, $protocol и методы для отправки электронной почты / текста и т. д. Но во время написания этого класса я понял, что эти if s снова подкрадываются как if($msg->getType() == 'text') условные выражения.Чтобы добавить к этому, я создаю новый класс, который отделяет action часть моего объекта , что меня смущает (например, class door должен отвечать за реализацию close() иopen() методы).

Теперь либо я принимаю, что if всегда будут там (что похоже на поражение всей цели полиморфизма), либо я должен что-то делать неправильно .
Отс точки зрения пользователя, я представляю что-то вроде:

$msg = new Message();
$msg->setType('email'); //or 'text' or 'mms' etc.
$msg->setContent($content); //e.g. $content=array('subject'=>'foo','body'=>'bar')
$msg->sendMessage();
//if the last line is not possible, then perhaps
//$sender = new SendMessage($msg);
//$sender->send();

что мне здесь не хватает?нельзя ли достичь $msg->sendMessage();?Будут ли / мне нужны различные сообщения классов (MessageEmail, MessageText и т. Д.)?Должен ли я отделить SendMessage (и, может быть, $msg->sendMessage(); позвонить?)

// и это когда я даже не думал о получении сообщения !Боже, помоги мне !!:(Обновление от 15 августа 2011 года. Подумав обо всех аспектах текущей кодовой базы, я определил следующие части, которые мне нужно будет реализовать.

a. Message Class(es) (type, content, sender, receiver, DateTime of send/receive etc.)
Responsibilities: 
creating and modifying messages
ascribing consistent and appropriate characteristics of a message
b. Send Class(es) (protocol, header info, server/operator to use)
Responsibilities:
Sending messages
Changing the state of Message (for setting send DateTime of Message)
e. Database Class(es) (id, content, to, from, time etc.)
Responsibilities:
Represent Message for storage.
CRUD (Create, Read, Update, Delete) actions on this representation for DBMS.
e. Interfaces (MAX_MESSAGE_LENGTH, TIMEOUT etc. )
Responsibilities:
Provide interface for communication between various modules.

Я считаю, что моей основной причиной путаницы было смешивание интерфейсов с полиморфизмом ( см. Комментарий ) Что вы думаете об этом?Обновление от 16 августа 2011 года
В основном я использовал интерфейсы для наложения функциональности.Вот краткая версия файла 'interfaces.php'

interface MessageInterface {
    //omitting getters for clarity
    public function setType($type);
    public function setSender(IdentityInterface $sender);
    public function setReceiver(IdentityInterface $receiver);
    public function setSendGateway(GatewayInterface $sendGateway);
}
interface IdentityInterface {
    public function setName($name);
    public function setAddress($address);
}
interface GatewayInterface {
    public function setProtocol($protocol);
    public function send(IdentityInterface $sender, IdentityInterface $receiver, ContentInterface $content);
}

. Реализации класса просты (ничего особенного, так как я еще не включил class GatewaySMPP implements GatewayInterface в мой основной класс Message, который выглядит так:

class Message implements MessageInterface {
    private $_type; private $_content;
    private $_sender; private $_receiver;
    private $_sendGateway; //private $_receiveGateway; private $_dataStorage;
    public function __construct(
        $type = NULL, $content = NULL,
        IdentityInterface $sender = NULL,
        IdentityInterface $receiver = NULL,
        GatewayInterface $sendGateway = NULL
    ) {
        $this->setType($type); $this->setContent($content);
        ($sender === NULL)
            ? $this->setSender(new Identity())
            : $this->setSender($sender);
        ($receiver === NULL)
            ? $this->setReceiver(new Identity())
            : $this->setReceiver($receiver); //similarly for $setSendGateway etc.
    }
    //setters and getters, omitting for clarity
    public function send(...) { //testing pending
        $this->_sendGateway->send($this->getSender(), $this->getReceiver(), $this->getContent ...)
    }

Самое интересное было реализовать GatewaySMPP, который включал множество операций с сокетами и проверку ответов. Мне просто нужно написать оболочку public function send() для private function _send{PDU,SM} методов.

Пока я былРазмышляя об интеграции GatewaySMPP, я понял, что буду открывать / закрывать сокеты для соединения SMPP для каждой операции отправки сообщения. Это хорошо для упражнения / тестирования, но на практике я думаю, что мне может понадобиться изменить свою логику так, чтобы существующее соединение Вопрос в том, как? Вот текущая логика в следующем порядке:

class GatewaySMPP {
    private $_socket,$_port,$_host //etc.
    public function __construct($host,$port,$user,$passwd) {
        $this->_socket = FALSE;
        $this->_host = $host; //initialize other private variables
    }
    public function init() {
        if($this->_socket !== FALSE) return FALSE; //socket already in use
        $this->_socket = fsockopen($this->_host, $this->_port ...)
        //prepare bind statement for initiating SMPP connection and fwrite to socket
        $this->_sendPDU(BIND, $data)
    }
    public function send($receiver, $sender, $message, ...) {
        //use private functions which do actual socket operations
        $this->_sendSM($receiver, $sender, $message, ...)
    }
    public function end() {
        if($this->_socket === FALSE) return; //socket already closed
        this->_sendPDU(UNBIND, ''); //omitting response check
        $result = fclose($this->_socket); //omitting response check
    }

В. Проблема, с которой я сталкиваюсь, заключается в том, что каждый объект GatewaySMPP будет иметь свой собственный $_socket, поэтому я подумал о создании одиночного шлюза GatewaySMPP ( shudders ) или использовании некоторой глобальной переменной / состояния для отслеживания сокетов для повторного использования.Идея, которая приходит мне в голову, заключается в том, что потребитель этих классов использует следующую логику.1. Создайте и используйте один $objGatewaySMPP для всех $objectMessage[] 2. objGatewaySMPP->init(); 3. foreach($objMessage[] as $msg) $msg->send(); 4. objGatewaySMPP->end();.Это все еще оставляет проблему одновременных звонков разными пользователями класса?Предложения / комментарии, пожалуйста.

Ответы [ 4 ]

3 голосов
/ 15 августа 2011

Может быть, попробовать что-то вроде этого.Это быстрая попытка, но вы всегда должны стараться минимизировать дублирование кода.

<?php

// Message Types

abstract class Message 
{
    private $content; // for email this is the body of the email / for sms it is the 140 characters
    private $sendService;

    public function __construct(SendService $sendService){
        $this->sendService = $sendService;
    }

    public function send($recipient)
    {
        $this->sendService->send($recipient, $this);
    }

}

class EmailMessage extends Message
{
    private $subject;
    private $header;
    //setters and getters / maybe a constructor
}

class SMSMessage extends Message
{
    private $from;
    //setters and getters / maybe a constructor
}


//Services for sending messages

interface SendService
{
    function send(Recipient $recipient, $message);
}

class EmailSendService implements SendService
{
    function send($recipient, EmailMessage $message){
        // you can use only the attributes from the recipient that you need (email address)
        // you can be sure that the message has a header and a subject because you are enforcing
        // the type allowed to be passed to this function
        // do email sending stuff
    }           
}

class SMSSendService implements SendService
{
    function send($recipient, SMSMessage $message){
        // you can use only the attributes from the recipient that you need (tel number)
        // do sms sending stuff
    }           
}

// Defines a 'user' that can be used for both messge types
class Recipient
{
    private $email;
    private $tel;
    private $name;
    //setters and getters
}


// how you would use the above

// 1 - set up recipient - in the real world you would probably have something that would provide this
// to you, like a database lookup
$recipient = new Recipient();
$recipient->setEmail('abc@def.com');
$recipient->setName('Herp Derp');
$recipient->setTel('07770000000000');

// 2 - get a service for sending your message 
$sendService = new SMSSendService();

// 3 - create your message by passing it a service which it can use to send itself
$message = new SMSMessage($sendService);

// 4 - set attributes of your message and then send (passing a recipient to send to)
$message->setContent('lorem ipsum herp derp doop');
$message->send($recipient);
1 голос
/ 15 августа 2011

С интерфейсами вы можете сделать это, не расширяя другой класс. И это здорово.

Я попробую сказать в коде (потому что мой английский хуже моего PHP)

<?php
Interface IMessage
{
    public function Send();
}

class EMail implements IMessage
{
    private $content;
    private $to;
    private $subject;

    public function __construct($to, $subject, $content)
    {
        $this->to = $to;
        $this->subject = $subject;
        $this->content = $content;
    }

    public function Send()
    {
        mail($this->to, $this->subject, $this->content);
    }
}

class SMS implements IMessage
{
    private $num;
    private $from;
    private $message;

    public function __construct($num, $message, $from = '')
    {
        $this->num = $num;
        $message = substr(trim($message), 0, 140);
        $from = empty($from) ? $num : $from;
    }

    public function Send()
    {
        //...
    }
}
0 голосов
/ 16 августа 2011

Перейти на другой язык.(без шуток).Поддержка ООП в PHP совершенно отсутствует, и я не хотел бы пытаться программировать в ней что-либо, кроме задач, близких к сети.

0 голосов
/ 15 августа 2011

Учитывая, что метод setContent будет использоваться только для текстовых типов (я полагаю, это потому, что вы выполняете условную проверку), кажется логичным разделить класс каким-то образом, возможно, на базовый класс Message а дети аля SMSMessage и MMSMessage. В SMSMessage вы можете определить SetContent(), а затем, возможно, AttachImage(), например, MMSMessage. Другой подход - определить SetContent() как абстрактное в сообщении базового класса, а затем вынудить наследников определить этот метод - то есть, если вы планируете выполнить некоторую логику в этом методе.

С другой стороны, процедурный код не обязательно плохой, но в вашем случае он звучит серьезно. Если стоит рефакторинг всего, то это уже другая история.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...