2017编程提高第1节课(作业)
LanyuanXiaoyao's Blog ヽ(✿゚▽゚)ノ

2017编程提高第1节课(作业)


问题

重构一个简单邮件发送,使之符合单一职责原则(SRP)

解题

分析

我们知道的是单一职责原则简单来说就是一个类只负责一件事,所以我们的任务是把含有多件事的类拆分开,所以先来看一下原代码中的结构和流程

原始流程
这里用思维导图简单地表示了一下类结构和业务流程

流程

这个邮件处理的流程比较简单,如上图所示,即读取三个数据

  1. 商品降价数据
  2. 邮件发送服务器数据
  3. 需要发送的用户数据

然后根据以上三个信息发送邮件即可,当然其中还涉及一些数据的处理,但这三个获取数据的步骤+发送邮件就构成了整个逻辑,重构将以实现这四件事为中心来重构

类结构

从途中可以明显看到,所有的获取数据和处理都放在了PromotionMail这个类中,显然不符合单一职责原则,所以这个类中的方法必须拆分,我们首先可以按照刚刚对流程的分析对这个类进行一个简单的分类

main(String[])

PromotionMail(File, boolean)

// 获取商品降价数据
readFile(File)
setProductID(String)
getproductID()
setProductDesc(String)
setLoadQuery()

// 获取/设置邮件发送服务器信息
setSMTPHost()
setAltSMTPHost()
setFromAddress()

// 获取需要发送用户数据
loadMailingList()

// 发送邮件
setMessage(HashMap)
configureEMail(HashMap)
sendEMails(boolean, List)

可以看到除了main方法和PromotionMail方法外,其余的方法都可以被归类,这样就可以按照各自的职责进行重构了

重构

由于第一次做重构,也不知道怎么的重构方式才对,于是就直接套用在写JSP的结构来进行分层,即

root
|_pojo
|_service
|_util

为了简洁,下面贴出的代码均省去了setter/getter和异常的代码

获取商品降价数据

在pojo中构造Product这个bean用来表示商品这种对象,内容为

public class Product {
	private String productID;
	private String productDesc;
}

然后在ProductService中读取配置文件获取商品降价信息

public class ProductService {
	public List<Product> getProductDescList(File file) {
		List<Product> plist = new ArrayList<Product>();
		BufferedReader br = null;
		br = new BufferedReader(new FileReader(file));
		String temp = br.readLine();
		String[] data = temp.split(" ");

		Product p = new Product();
		p.setProductID(data[0]);
		p.setProductDesc(data[1]);

		plist.add(p);
		return plist;
	}
}

获取/设置邮件发送服务器信息

增加一个MailServiceConfiguration这个类作为服务器信息的bean

public class MailServiceConfiguration {
	private String SMTPHost;
	private String AltSMTPHost;
	private String FromAddress;
}

获取需要发送用户数据

同样增加一个Userbean来表示用户

public class User {
	private String name;
	private String email;
}

然后是获取相应用户信息的UserService

public class UserService {
	public List<User> getSendMailUser(Product product) {
		// 我觉得构造sql语句这种和数据库交互的操作应该放在dao中进行的,比如在dao中有一个类似findByProductID(String pattern)的方法,那这里就简单处理了,只是为了返回一个userlist的结果
		String sql = "Select name from subscriptions " + "where product_id= '" + product.getProductID() + "' "
				+ "and send_mail=1 ";
		return DBUtil.query(sql);
	}
}

发送邮件

然后就是最后一步,发送邮件,“向需要发送邮件的客户发送邮件”这件事本来就是PromotionMail这个类中应该干的事情,所以肯定是写在原来的位置,所以根据别的类获取的数据,构造发送邮件的配置即可,于是重构了sendEMails方法

protected void sendEMails(boolean debug, MailServiceConfiguration configuration, List<Product> plist)
			throws IOException {
		System.out.println("开始发送邮件");
		Iterator<Product> piterator = plist.iterator();
		while (piterator.hasNext()) {
			Product product = piterator.next();
			List<User> ulist = userService.getSendMailUser(product);
			Iterator<User> uiterator = ulist.iterator();
			while (uiterator.hasNext()) {
				User user = uiterator.next();
				Mail mail = new Mail("您关注的产品降价了",
						"尊敬的 " + user.getName() + ", 您关注的产品 " + plist.get(0).getProductDesc() + " 降价了,欢迎购买!",
						user.getEmail());
				MailUtil.sendEmail(debug, configuration, mail);
			}
		}
	}

于是相应的,原来调用的传参方式也作相应修改

public PromotionMail(File file, boolean mailDebug) throws Exception {
		MailServiceConfiguration configuration = new MailServiceConfiguration()
				.setAltSMTPHost(ConfigurationKeys.SMTP_SERVER)
				.setSMTPHost(ConfigurationKeys.ALT_SMTP_SERVER)
				.setFromAddress(ConfigurationKeys.SMTP_SERVER);
		List<Product> plist = productService.getProductDescList(file);
		sendEMails(mailDebug, configuration, plist);
	}

其他

当然其他的类也会有相应的修改,比如负责发送邮件的类MailUtil,把发送邮件的相关处理都集中到这个类中,尤其是把发送邮件的异常相关处理转移到这里

public class MailUtil {

	private static void sendEmail(String toAddress, String fromAddress, String subject, String message, String smtpHost,
			boolean debug) {
		// 略
	}
	
	public static void sendEmail(boolean debug, MailServiceConfiguration configuration, Mail mail) {
		try {
			if (mail.getToAddress().length() > 0)
				sendEmail(mail.getToAddress(), configuration.getFromAddress(), mail.getSubject(), mail.getMessage(),
						configuration.getSMTPHost(), debug);
		} catch (Exception e) {
			try {
				sendEmail(mail.getToAddress(), configuration.getFromAddress(), mail.getSubject(), mail.getMessage(),
						configuration.getAltSMTPHost(), debug);

			} catch (Exception e2) {
				System.out.println("通过备用 SMTP服务器发送邮件失败: " + e2.getMessage());
			}
		}
	}

小结

到此,重构的工作基本完成,运行通过,经过重构之后的结构如下图

SRP
可以看到结构已经清晰了不少,已经按照四个主要步骤将功能分割开,原本臃肿的PromotionMail类现在只剩下3个方法,除去main方法,就只剩下处理发送邮件逻辑的PromotionMailsendEMails

public class PromotionMail {

	UserService userService = new UserService();
	ProductService productService = new ProductService();

	public static void main(String[] args) throws Exception {
		File file = new File("src/main/java/com/coderising/ood/srp_restructure_1/product_promotion.txt");
		boolean emailDebug = false;
		PromotionMail pe = new PromotionMail(file, emailDebug);
	}

	public PromotionMail(File file, boolean mailDebug) throws Exception {
		// 略
	}

	protected void sendEMails(boolean debug, MailServiceConfiguration configuration, List<Product> plist){
		// 略
	}
}

至此,基本实现了单一指责原则(自认为(´_`))

总结

暂略,先让老师看看再说


评论