最后查看更新:
当前的代码库有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();
?我/我是否需要不同的消息类(MessageEmail
,MessageText
等)?我应该将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日更新的简短版本
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();
。这仍然留下了类的不同用户并发调用的问题?建议/意见请。
答案 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 Message
和SMSMessage
。在MMSMessage
中,您可以定义SMSMessage
,然后可能SetContent()
,例如AttachImage()
。另一种方法是在基类Message中将MMSMessage
定义为抽象,然后强制继承者定义该方法 - 即如果您计划在该方法中执行某些逻辑。
另一方面,程序代码不一定是坏人,但在你的情况下,听起来确实很严重。如果值得重构,那就是另一个故事了。
答案 3 :(得分:-4)
转到另一种语言。 (不开玩笑)。 PHP的OOP支持非常缺乏,我不想尝试在其中编写任何其他与Web相关的任务。