1、使用模板模式和工厂模式的混合设计开发各种邮件客户端发送邮件。

2、模板模式的目的:能保证快速开发各种邮箱客户端,子类只需要重写模板类邮箱的抽象方法即可。之后再开发任何邮箱就只要加一个类,写3行代码就可以。

工厂模式的目的:能隐藏创建具体对象的细节,只需从工厂类的方法中就能得到各种邮箱客户端。例如被别的模块import时候,只需要import这个工厂类就可以了,不需要去import几十种具体的邮箱客户端。

  1. # coding=utf8
  2. """
  3. 使用模板模式、工厂模式的混合设计,开发各种邮件客户端
  4. """
  5. import sys
  6.  
  7. reload(sys)
  8. sys.setdefaultencoding('utf8')
  9.  
  10. import re
  11. import unittest
  12. from abc import ABCMeta, abstractmethod
  13. import smtplib
  14. from email.mime.text import MIMEText
  15.  
  16. class MailClient(object):
  17. """一个通用的邮件客户端,作为模板模式的模板类。
  18. 该类为抽象类,不可以进行实例化。
  19. _set_smtp方法为抽象方法,子类必须重写这个方法.
  20. """
  21. __metaclass__ = ABCMeta
  22.  
  23. def __init__(self, msg_from, passwd):
  24. """
  25. :param msg_from: 发件人邮箱地址
  26. :param passwd: 发件人邮箱密码,qq邮箱使用授权码是16个字母,而不是自己的邮箱密码。
  27. """
  28. self._mail_name = None
  29. self._msg_from = msg_from
  30. self._passwd = passwd
  31. self._smtp = None
  32. self._set_smtp()
  33. self.__login()
  34.  
  35. @abstractmethod
  36. def _set_smtp(self):
  37. pass
  38.  
  39. def __login(self):
  40. try:
  41. self._smtp.login(self._msg_from, self._passwd)
  42. except smtplib.SMTPAuthenticationError as e:
  43. raise smtplib.SMTPAuthenticationError(535, '{}邮箱登录失败\n'.format(self._mail_name) + e.message)
  44.  
  45. def send_mail(self, msg_to, subject, content):
  46. """
  47. 发送邮件
  48. :param msg_to: 所有收件人的邮箱地址列表,类型为字符串列表,单个接收人也可以用字符串
  49. :param subject :邮件主题
  50. :param content:邮件内容
  51. :return :邮件是否发送成功
  52. :type msg_to:list
  53. :type subject:str
  54. :type content:str
  55. """
  56. msg = MIMEText(content, _charset='utf8')
  57. msg['Subject'] = subject
  58. msg['From'] = self._msg_from
  59. msg['To'] = msg_to
  60. self._smtp.sendmail(self._msg_from, msg_to, msg.as_string())
  61. print '邮件发送成功,发送的邮件主题是: {} 。请去邮箱检查邮件'.format(subject)
  62. return True
  63.  
  64. def __str__(self):
  65. return '登录帐号为: {0} 的{1}邮箱客户端'.format(self._msg_from, self._mail_name)
  66.  
  67. def quit(self):
  68. try: # 如果没登陆成功就关闭连接,会出错,try一下
  69. self._smtp.quit()
  70. except:
  71. pass
  72.  
  73. def __del__(self):
  74. self.quit()
  75.  
  76. class QQMailClient(MailClient):
  77. def _set_smtp(self):
  78. self._mail_name = 'qq'
  79. self._smtp = smtplib.SMTP_SSL("smtp.qq.com", 465)
  80.  
  81. class Wangyi163MailClient(MailClient):
  82. def _set_smtp(self):
  83. self._mail_name = ''
  84. self._smtp = smtplib.SMTP_SSL("smtp.163.com", 465)
  85.  
  86. class MailNameException(Exception):
  87. """不支持的邮箱异常类"""
  88. def __init__(self, msg_from):
  89. err = '你设置的邮箱账号是 {} ,不支持此邮箱'.format(msg_from)
  90. super(MailNameException, self).__init__(err)
  91.  
  92. class MailClientFactory(object):
  93. @staticmethod
  94. def get_mail_client(msg_from, passwd):
  95. """
  96. :param msg_from:发件人邮箱地址
  97. :param passwd:邮箱密码
  98. :return: 具体的邮箱对象
  99. :type msg_from:str
  100. :type passwd:str
  101. :rtype :MailClient
  102. """
  103. if re.match('[0-9a-zA-Z_]+@qq\.com', msg_from):
  104. return QQMailClient(msg_from, passwd)
  105. elif re.match('[0-9a-zA-Z_]+@163\.com', msg_from):
  106. return Wangyi163MailClient(msg_from, passwd)
  107. else:
  108. raise MailNameException(msg_from)
  109.  
  110. class MailTest(unittest.TestCase):
  111. def test_163(self):
  112. """测试163邮箱发送两个邮件"""
  113. wangyi163_mail_client = MailClientFactory.get_mail_client('m13148804506@163.com', '')
  114. print wangyi163_mail_client
  115. wangyi163_mail_client.send_mail('909686716@qq.com', '测试网易163邮箱发送主题1', '测试网易163邮箱发送内容1')
  116. result = wangyi163_mail_client.send_mail('909686716@qq.com', '测试网易163邮箱发送主题2', '测试网易163邮箱发送内容2')
  117. self.assertEqual(result, True, msg='163邮件发送失败')
  118.  
  119. def test_qq(self):
  120. """测试两个qq号发送邮件"""
  121. qq_123456_client = MailClientFactory.get_mail_client('123456@qq.com', 'ssoodruxniyfxxxx')
  122. print qq_123456_client
  123. qq_789000_client = MailClientFactory.get_mail_client('789000@qq.com', 'ssoodruxniyfxxxx')
  124. print qq_789000_client
  125. result = qq_123456_client.send_mail('m13148804506@163.com', '测试qq邮箱发送主题3', '测试qq邮箱发送内容3')
  126. self.assertEqual(result, True, msg='qq邮件发送失败')
  127. result = qq_789000_client.send_mail('m13148804506@163.com', '测试qq邮箱发送主题4', '测试qq邮箱发送内容4')
  128. self.assertEqual(result, True, msg='qq邮件发送失败')
  129.  
  130. def test_126(self):
  131. """测试一个不支持的邮箱"""
  132. self.assertRaises(MailNameException, MailClientFactory.get_mail_client, 'm13148804506@126.com', '')
  133.  
  134. if __name__ == "__main__":
  135. unittest.main()

3、由此可见,在一个项目中设计模式不是固定的只能用一种,而是可以多种模式混合设计。

1、此项目也可以使用 策略模式来实现,策略类中设置邮箱服务器,Context类中登录和发送邮件,和此文比只有很小的区别。策略模式实现可以参考 http://www.cnblogs.com/ydf0509/p/8527515.html

2、由于这个邮箱项目比较简单,这也可以不使用设计模式,不需要增加多个类,只需要一个类就可以实现了。工厂模式的缺点是每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。过度设计这并不是什么好事。

3、也可以用面向过程来实现,缺点是在进行多实例操作时候,小红 小明 小黄登录qq邮箱,需要在类之外设置变量,如果类的属性非常多,比如20个,小红 、小黄 、小明需要在类外设置60个变量来保存这些属性,oop只要3个对象,每个对象有20个属性。oop在状态维护、保存、序列化反序列化上、被别的模块调用更简单、命名空间更清洁、继承这些方面有很大优势。

看到有的人是一个函数就实现发邮件,虽然简单,把设置服务、登录、发送邮件、关闭连接放一起了,那样做每发一个邮件都执行连接服务器进行登录发邮件关闭连接这个过程,有一些额外的开销。

python 使用模板模式和工厂模式的混合设计开发各种邮件客户端发送邮件的更多相关文章

  1. [python实现设计模式]-3.简单工厂模式-触宝开放平台

    预备知识: 开放封闭原则(Open-Closed Principle OCP) Software entities(classes,modules,functions etc) should open ...

  2. javascript模式 (3)——工厂模式和装饰模式

    上节我们讲解了单例模式,这节我们将继续讲解工厂模式和迭代器模式 工厂模式: 工厂模式的目的是为了方便的创建对象(可以在不知道构造的情况下),通过静态方法来实现,在java或c#等静态编译语言中需要通过 ...

  3. Java中的GOF23(23中设计模式)--------- 工厂模式(Factory)

    Java中的GOF23(23中设计模式)--------- 工厂模式(Factory) 在给大家介绍工厂模式之前,我想和大家聊聊面向对象的那点事,在这里,引入三个概念. 开闭原则(Open Close ...

  4. Java设计模式之(工厂模式)--简单工厂模式--工厂方法模式--抽象工厂模式

    工厂模式: 工厂模式可以分为三类: 1)简单工厂模式(Simple Factory) 2)工厂方法模式(Factory Method) 3)抽象工厂模式(Abstract Factory) 简单工厂模 ...

  5. Java中设计模式之工厂模式-4

    一.工厂模式由来 1)还没有工厂时代:假如还没有工业革命,如果一个客户要一款宝马车,一般的做法是客户去创建一款宝马车,然后拿来用. 2)简单工厂模式:后来出现工业革命.用户不用去创建宝马车.因为客户有 ...

  6. JS 简单工厂模式,工厂模式(二)

    一.什么是工厂模式: 工厂模式就是用来创建对象的一种最常用的设计模式,我们不暴露创建对象的具体逻辑,而是将逻辑封装到一个函数中,那么,这个函数 就可以被视为一个工厂.那么,在实际项目中,我们是不是可以 ...

  7. .Net简单工厂模式,工厂模式,抽象工厂模式实例

    1.定义   简单工厂模式:是由一个工厂对象决定创建出哪一种产品类的实例.简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现. 工厂模式:定义一个用于创建对象的接口, ...

  8. C#设计模式——简单工厂模式、工厂模式和抽象工厂模式

    一:简单工厂模式 1:描述:简单工厂模式是由一个工厂对象根据接收到的消息决定要创建哪一个类的对象事例. 2:优点:工厂类中有相关逻辑判断,可以根据需要动态创建相关的对象事例,而客户端只需要告诉工厂类创 ...

  9. Java模式—简单工厂模式

    简单工厂模式:是由一个工厂对象决定创建出哪一种产品类的实例,简单工厂模式是工厂模式家族中最简单实用的模式. 目的:为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. ...

随机推荐

  1. Web Application Stress 对网站进行压力测试

    打开Microsoft web Application Stress工具按下图顺序操作: 1. 2.点击Record 3. 4.在浏览器中输入要测试的URI地址 5.在设置中设置参数,如发起请求线程数 ...

  2. 2012Google校园招聘笔试题

    1.已知两个数字为1~30之间的数字,甲知道两数之和,乙知道两数之积,甲问乙:“你知道是哪两个数吗?”乙说:“不知道”.乙问甲:“你知道是哪两个数吗?”甲说:“也不知道”.于是,乙说:“那我知道了”, ...

  3. JQ 点击指定文本框显示div。点击其他区域隐藏DIV

    <input id="username" type="text" style="width:90%;margin-top: 40px;" ...

  4. 关于源码输出,浏览器不解析Html标签

    有时候根据需要我们需要看到浏览器上源码效果如: 但是我如果在html中输入 <a href = 'http://www.baidu.com'>百度</a>那么问题来了,总是显示 ...

  5. 深入浅出LVM on linux

    什么是LVM? 什么是LVM?LVM(Logical Volume Manager)逻辑卷管理,是一种将一个或多个硬盘的分区在逻辑上集合,相当于一个大硬盘来使用,当硬盘的空间不够使用的时候,可以继续将 ...

  6. Quick solution to java.lang.NoClassDefFoundError: org/openqa/selenium/HasInputDevices error

    In case if you face this problem, one of the possible solutions that will work for you is to make su ...

  7. 第三百九十节,Django+Xadmin打造上线标准的在线教育平台—Django+cropper插件头像裁剪上传

    第三百九十节,Django+Xadmin打造上线标准的在线教育平台—Django+cropper插件头像裁剪上传 实现原理 前台用cropper插件,将用户上传头像时裁剪图片的坐标和图片,传到逻辑处理 ...

  8. C#跳转网页7种方法

    1.Response.Redirect(http://www.baidu.com,false); 目标页面和原页面可以在2个服务器上,可输入网址或相对路径.后面的bool值为是否停止执行当前页. 跳转 ...

  9. EasyUI的combobox组件Chrome浏览器不兼容问题解决办法

    EasyUI版本:jQuery EasyUI 1.4.1 Chrome浏览器版本:41.0.2272.101 m 问题描述 在Chrome浏览器下,下拉框选择选项之后,选择的值在下拉框中不显示,重新选 ...

  10. Git的杀手级功能之 一 远程仓库

    Git的杀手级功能之一:远程仓库 Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上. 一.注册GitHub账号,然后和本地Git仓库来关联免费获得Git远程仓库来学校git的远程仓 ...