PHP思考OOP:发送和接收消息:我做对了吗?

时间:2011-08-14 21:51:22

标签: php oop interface polymorphism

最后查看更新:

当前的代码库有1.4k行的纯程序代码,它发送短信(具有业务逻辑,数据库连接,以及一个巨大的if条件中的所有条件嵌套了无数的if s,没有功能,充满文字,真正的DailyWTF?候选人)。我决定咬紧牙关,从头开始重写整个该死的东西 问题是,这将是我的第一次OOP体验。我尽可能多地阅读关于OOD和良好实践的内容,并决定从简单的事情开始。我想实现消息的发送/接收(主要是文本/短信,但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再次作为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();?我/我是否需要不同的消息类(MessageEmailMessageText等)?我应该将SendMessage分开(并且可能会$msg->sendMessage();调用它吗?)

//这是我甚至没想过收到消息的时候!神救救我 !! :(

<小时/> 2011年8月15日更新: 在考虑了当前代码库的所有方面后,我确定了以下需要实现的部分。

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.

我认为我混淆的主要原因是混合接口与多态(see comment)您对此有何看法?

<小时/> 2011年8月16日更新
我主要使用接口来强加功能。这是'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
    }

Q值。我面临的问题是,GatewaySMPP的每个对象都有自己的$ _socket,所以我考虑使用GatewaySMPP单例( shudders )或使用一些全局/状态变量来跟踪套接字以便重用。我想到的一个更好的想法是,如果这些类的使用者使用以下逻辑。 1.为所有$objGatewaySMPP 2创建并使用单$objectMessage[] objGatewaySMPP->init(); 3. foreach($objMessage[] as $msg) $msg->send(); 4. objGatewaySMPP->end();。这仍然留下了类的不同用户并发调用的问题?建议/意见请。

4 个答案:

答案 0 :(得分:3)

也许尝试这样的事情。这是一个快速尝试,但您应该始终尽量减少代码重复。

<?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 :(得分:1)

使用接口,您可以在不扩展其他类的情况下完成。这很棒。

我会尝试在代码中说(因为我的英语比我的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()
    {
        //...
    }
}

答案 2 :(得分:0)

考虑到方法setContent将仅用于文本类型(我假设这是因为你正在进行条件检查),以某种方式分解类似乎是合乎逻辑的,也许是基类{ {1}}和儿童ala MessageSMSMessage。在MMSMessage中,您可以定义SMSMessage,然后可能SetContent(),例如AttachImage()。另一种方法是在基类Message中将MMSMessage定义为抽象,然后强制继承者定义该方法 - 即如果您计划在该方法中执行某些逻辑。

另一方面,程序代码不一定是坏人,但在你的情况下,听起来确实很严重。如果值得重构,那就是另一个故事了。

答案 3 :(得分:-4)

转到另一种语言。 (不开玩笑)。 PHP的OOP支持非常缺乏,我不想尝试在其中编写任何其他与Web相关的任务。