今天一大早,经理就把老罗和小杨召集起来,说:今天的主要任务是在我们的平台中加上用户注册的功能。用户填写基本资料,其中包含手机号,要确保是一个真实的手机,所以要用验证码,注册成功后,平台发一封邮件到他的邮箱里面。老罗,你负责调研、编写手机验证码,发邮件的部分。小杨,你负责界面和业务流程部分。说完,大家就各自开工了。
下午的时候,老罗对小杨说:发邮件发短信的功能,我都帮你写好了,分别在utils.MailSender和utils.SMSSender里面,已经push到gitlab上了。
“知道了,我一会儿调一下试试。”小杨自己的部分还没有做完,头也不抬的回应着。
过了一会儿,小杨对老罗喊了一声:“老罗,发邮件报错了,空指针,我发给你报错信息。“
“空指针?我自己测试过的啊!”
老罗,根据报错信息中提示的错误行数找到相应的代码研究起来,然后又运行了一遍他的测试程序,发现并没有错,然后就到小杨座位上想看看是怎么回事。他看到小样的代码,下意识地喊了一句:“你调错了,少写一行代码,我说怎么我那边不报错。”随后,老罗打开自己的代码,还跟小杨解释说
这是发送工具接口,就一个方法
public interface Sender {
void send(String from,String to,String subject,String content);
}
这是发短信工具的实现类,SMSSender,我用的淘宝的工具类
import com.taobao.api.ApiException;
import com.taobao.api.DefaultTaobaoClient;
import com.taobao.api.TaobaoClient;
import com.taobao.api.request.AlibabaAliqinFcSmsNumSendRequest;
import com.taobao.api.response.AlibabaAliqinFcSmsNumSendResponse;
public class SmsSender implements Sender{
public static final String SMS_SEND_URL = "http://gw.api.taobao.com/router/rest";
public static final String APPKEY = "xxxx";
public static final String SECRET = "xxxxxxxx";
public static final String SIGN = "测试";
@Override
public void send(String from, String to, String subject, String content){
TaobaoClient client = new DefaultTaobaoClient(SMS_SEND_URL, APPKEY, SECRET);
AlibabaAliqinFcSmsNumSendRequest req = new AlibabaAliqinFcSmsNumSendRequest();
//req.setExtend("123456");
req.setSmsType("normal");
req.setSmsFreeSignName(SIGN);
req.setSmsParamString(content);
req.setRecNum(to);
req.setSmsTemplateCode("10010");
AlibabaAliqinFcSmsNumSendResponse rsp = null;
try {
rsp = client.execute(req);
} catch (ApiException e) {
e.printStackTrace();
}
}
}
这是邮件工具的实现类,MailSender
package factory;
import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.Message.RecipientType;
import java.util.Calendar;
import java.util.Properties;
/**
* Created by user on 2016/8/18.
*/
public class MailSender implements Sender {
Properties props = new Properties();
public Properties config(String smtpHost,String starttls,String auth,String password){
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.starttls.enable",starttls);//使用 STARTTLS安全连接
props.put("mail.smtp.auth", auth);// 使用验证
props.put("mail.smtp.password", auth);// 密码
return props;
}
@Override
public void send(String from,String to,String subject,String content) {
Session mailSession = Session.getInstance(props,new MyAuthenticator(from,props.getProperty("mail.smtp.password")));
Transport transport = null;
try {
transport = mailSession.getTransport("smtp");
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
InternetAddress fromAddress = null;
InternetAddress toAddress = null;
try {
fromAddress = new InternetAddress(from);
toAddress = new InternetAddress(to);
} catch (AddressException e) {
e.printStackTrace();
}
MimeMessage message = new MimeMessage(mailSession);
try {
message.setFrom(fromAddress);
message.addRecipient(RecipientType.TO, toAddress);
message.setSentDate(Calendar.getInstance().getTime());
message.setSubject(subject);
message.setContent(content, "text/html;charset=gb2312");
transport.connect(props.getProperty("mail.smtp.host"),from, props.getProperty("mail.smtp.password"));
transport.send(message, message.getRecipients(RecipientType.TO));
} catch (MessagingException e) {
e.printStackTrace();
}
}
class MyAuthenticator extends Authenticator {
String userName="";
String password="";
public MyAuthenticator(){
}
public MyAuthenticator(String userName,String password){
this.userName=userName;
this.password=password;
}
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(userName, password);
}
}
}
“你看看你是怎么调用的”
MailSender mailSender = new MailSender();
mailSender.send("j1@tm.com","j2@tm.com","谢谢","谢谢注册");
“调用send之前应该要调用一下config方法,输入一些配置信息啊”。老罗埋怨小杨道。
"我哪里知道要先调用一下config方法"小杨有些委屈。
老罗突然意识到,小杨并没有看过自己的代码,不过还是说了一句:“调之前你看一下嘛”
“我那边功能还没做完呢。”小杨听到这句话,感到一股怒火油然而生,但还是压住了怒火,因为他感到老罗在推卸责任。
“就算是你不知道我怎么实现的,报错后也可以调试一下吧,这么简单一个代码,调试一下就明白了啊,没见过这么笨的”。
“你代码报错了还让我调试”。
“我那边没报错,到你这就报错了,你还不承认是你的问题?”。老罗非常确信的认为这个问题是小杨的问题
“你写那垃圾代码,我根本不稀罕看,说我笨?我用过spark,信不信老子弄死你”,小杨完全不想跟老罗纠缠了,语调上扬。
老罗被小杨的话震惊了,撒腿就跑到经理办公室,推开门喊道:“经理,小杨要弄死我......”。
经理看到狼狈的老罗,被突如其来的场景吓了一跳,手忙脚乱的关掉直播软件,同时问到:“什...什么?”老罗把刚才发生的一幕告诉了经理。经理跟着老罗来到了小杨的座位旁,看了看他们俩的代码,说到:“小杨,大家都是同事,干嘛要说弄死谁的呢,有话好好说嘛,老罗,你也是,都是老员工了,你任务做的快,也要好好的思考一下,能否优化呢,怎么总是不注意呢!你代码本身没什么问题,但是你忽略了一件事情,就是一个项目是团队协作完成的,要尽量的站在队友的角度看问题嘛,你调研完成了代码,但是别人没有经历过你代码完成的过程,他们并没有思考过你曾经思考过的问题,有很多细节他们是不知道的,你认为这样调用代码是理所当然的,但别人不一定是这样想的啊”
"那我总不能每次写完自己的代码,都要跟别人从头到尾讲一遍吧,我想讲,他们也不一定想听啊,就算是他们想听,也不一定能听懂啊,你说是不是",老罗看着小杨说。
小杨正想反击,经理按住他说:“呵呵,别人确实不想知道你代码的细节,不过你也不要把责任推卸给别人,你的代码稍微改一下就不会发生刚才的扯皮事件,你看好,我来改一下”。说着,经理坐在了座位上亲自动手改了起来,边改边解释。
首先,我们把MailSender改造一下,让他的配置用的props可以从外部类访问
package factory;
import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.Message.RecipientType;
import java.util.Calendar;
import java.util.Properties;
/**
* Created by user on 2016/8/18.
*/
public class MailSender implements Sender {
private Properties props = null;
public Properties getProps() {
return props;
}
public void config(String smtpHost, String starttls, String auth, String password){
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.starttls.enable",starttls);//使用 STARTTLS安全连接
props.put("mail.smtp.auth", auth);// 使用验证
props.put("mail.smtp.password", auth);// 密码
}
@Override
public void send(String from,String to,String subject,String content) {
if(props == null){
config("smtp.gmail.com","true","true","admin_password");
}
Session mailSession = Session.getInstance(props,new MyAuthenticator(from,props.getProperty("mail.smtp.password")));
Transport transport = null;
try {
transport = mailSession.getTransport("smtp");
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
InternetAddress fromAddress = null;
InternetAddress toAddress = null;
try {
fromAddress = new InternetAddress(from);
toAddress = new InternetAddress(to);
} catch (AddressException e) {
e.printStackTrace();
}
MimeMessage message = new MimeMessage(mailSession);
try {
message.setFrom(fromAddress);
message.addRecipient(RecipientType.TO, toAddress);
message.setSentDate(Calendar.getInstance().getTime());
message.setSubject(subject);
message.setContent(content, "text/html;charset=gb2312");
transport.connect(props.getProperty("mail.smtp.host"),from, props.getProperty("mail.smtp.password"));
transport.send(message, message.getRecipients(RecipientType.TO));
} catch (MessagingException e) {
e.printStackTrace();
}
}
class MyAuthenticator extends Authenticator {
String userName="";
String password="";
public MyAuthenticator(){
}
public MyAuthenticator(String userName,String password){
this.userName=userName;
this.password=password;
}
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(userName, password);
}
}
}
我们把实例化的工作放到另外一个类中,并且提供一个方法根据参数进行实例化。
public class SimpleSenderFactory {
public enum SENDER_TYPE{
MAIL,SMS
}
public static Sender product(SENDER_TYPE senderType){
if(senderType == SENDER_TYPE.SMS){
return new SmsSender();
}else if(senderType == SENDER_TYPE.MAIL){
MailSender mailSender = new MailSender();
mailSender.config("smtp.gmail.com","true","true","password");
return mailSender;
}else{
return null;
}
}
}
这样,我们把实例化的职责从使用方拿了回来。接下来看如何使用
Sender sender = SimpleSenderFactory.product(SimpleSenderFactory.SENDER_TYPE.MAIL);
sender.send("j3@tm.com","j4@tm.com","你好","欢迎");
虽然,还是要对使用方说明SimpleSenderFactory的使用方式,但无疑他屏蔽了发邮件的实现细节。SimpleSenderFactory是一个工厂类,他负责产生Sender产品,发邮件工具类和发短信工具类都是他的具体产品,SimpleSenderFactory把这些产品的实例化方法都管理起来,不过呢,将来新增一个Sender类型的产品,就需要修改SimpleSenderFactory,这也是他的一个缺点。