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

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

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

# coding=utf8
"""
使用模板模式、工厂模式的混合设计,开发各种邮件客户端
"""
import sys reload(sys)
sys.setdefaultencoding('utf8') import re
import unittest
from abc import ABCMeta, abstractmethod
import smtplib
from email.mime.text import MIMEText class MailClient(object):
"""一个通用的邮件客户端,作为模板模式的模板类。
该类为抽象类,不可以进行实例化。
_set_smtp方法为抽象方法,子类必须重写这个方法.
"""
__metaclass__ = ABCMeta def __init__(self, msg_from, passwd):
"""
:param msg_from: 发件人邮箱地址
:param passwd: 发件人邮箱密码,qq邮箱使用授权码是16个字母,而不是自己的邮箱密码。
"""
self._mail_name = None
self._msg_from = msg_from
self._passwd = passwd
self._smtp = None
self._set_smtp()
self.__login() @abstractmethod
def _set_smtp(self):
pass def __login(self):
try:
self._smtp.login(self._msg_from, self._passwd)
except smtplib.SMTPAuthenticationError as e:
raise smtplib.SMTPAuthenticationError(535, '{}邮箱登录失败\n'.format(self._mail_name) + e.message) def send_mail(self, msg_to, subject, content):
"""
发送邮件
:param msg_to: 所有收件人的邮箱地址列表,类型为字符串列表,单个接收人也可以用字符串
:param subject :邮件主题
:param content:邮件内容
:return :邮件是否发送成功
:type msg_to:list
:type subject:str
:type content:str
"""
msg = MIMEText(content, _charset='utf8')
msg['Subject'] = subject
msg['From'] = self._msg_from
msg['To'] = msg_to
self._smtp.sendmail(self._msg_from, msg_to, msg.as_string())
print '邮件发送成功,发送的邮件主题是: {} 。请去邮箱检查邮件'.format(subject)
return True def __str__(self):
return '登录帐号为: {0} 的{1}邮箱客户端'.format(self._msg_from, self._mail_name) def quit(self):
try: # 如果没登陆成功就关闭连接,会出错,try一下
self._smtp.quit()
except:
pass def __del__(self):
self.quit() class QQMailClient(MailClient):
def _set_smtp(self):
self._mail_name = 'qq'
self._smtp = smtplib.SMTP_SSL("smtp.qq.com", 465) class Wangyi163MailClient(MailClient):
def _set_smtp(self):
self._mail_name = ''
self._smtp = smtplib.SMTP_SSL("smtp.163.com", 465) class MailNameException(Exception):
"""不支持的邮箱异常类"""
def __init__(self, msg_from):
err = '你设置的邮箱账号是 {} ,不支持此邮箱'.format(msg_from)
super(MailNameException, self).__init__(err) class MailClientFactory(object):
@staticmethod
def get_mail_client(msg_from, passwd):
"""
:param msg_from:发件人邮箱地址
:param passwd:邮箱密码
:return: 具体的邮箱对象
:type msg_from:str
:type passwd:str
:rtype :MailClient
"""
if re.match('[0-9a-zA-Z_]+@qq\.com', msg_from):
return QQMailClient(msg_from, passwd)
elif re.match('[0-9a-zA-Z_]+@163\.com', msg_from):
return Wangyi163MailClient(msg_from, passwd)
else:
raise MailNameException(msg_from) class MailTest(unittest.TestCase):
def test_163(self):
"""测试163邮箱发送两个邮件"""
wangyi163_mail_client = MailClientFactory.get_mail_client('m13148804506@163.com', '')
print wangyi163_mail_client
wangyi163_mail_client.send_mail('909686716@qq.com', '测试网易163邮箱发送主题1', '测试网易163邮箱发送内容1')
result = wangyi163_mail_client.send_mail('909686716@qq.com', '测试网易163邮箱发送主题2', '测试网易163邮箱发送内容2')
self.assertEqual(result, True, msg='163邮件发送失败') def test_qq(self):
"""测试两个qq号发送邮件"""
qq_123456_client = MailClientFactory.get_mail_client('123456@qq.com', 'ssoodruxniyfxxxx')
print qq_123456_client
qq_789000_client = MailClientFactory.get_mail_client('789000@qq.com', 'ssoodruxniyfxxxx')
print qq_789000_client
result = qq_123456_client.send_mail('m13148804506@163.com', '测试qq邮箱发送主题3', '测试qq邮箱发送内容3')
self.assertEqual(result, True, msg='qq邮件发送失败')
result = qq_789000_client.send_mail('m13148804506@163.com', '测试qq邮箱发送主题4', '测试qq邮箱发送内容4')
self.assertEqual(result, True, msg='qq邮件发送失败') def test_126(self):
"""测试一个不支持的邮箱"""
self.assertRaises(MailNameException, MailClientFactory.get_mail_client, 'm13148804506@126.com', '') if __name__ == "__main__":
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. Maven项目编译后classes文件中没有.xml问题

    在做spring+mybatiss时,自动扫描都配置正确了,却在运行时出现了如下错误.后来查看target/classes/.../dao/文件夹下,发现只有mapper的class文件,而没有xml ...

  2. SpringBoot2 启动报错 Failed to auto-configure a DataSource

    今天Spring Boot 2.0正式版发布,寻思着搭个小demo尝试一下Spring Boot的新特性,使用idea创建项目.在选择组件时添加了mysql.mybatis 然后在第一次启动的时候启动 ...

  3. Memcache/Memcached的PHP操作手册(纯手稿版)

    Memcache和Memcached 其实是一个东西,只是php中要是用的扩展不一样, 2009年左右有人丰富memcache的用法和性能,编写了一个libmemcached是独立第三方client ...

  4. POST数据时400错误

    第一种解决办法是关闭Csrf public function init(){ $this->enableCsrfValidation = false; } 第二种解决办法是在form表单中加入隐 ...

  5. JS Window对象操作思维导图

  6. qualcomm compile instructions

    qualcomm编译指令 Compile the Entire Android Software source build/envsetup.sh lunch msm8909-userdebug ma ...

  7. Batch normalization:accelerating deep network training by reducing internal covariate shift的笔记

    说实话,这篇paper看了很久,,到现在对里面的一些东西还不是很好的理解. 下面是我的理解,当同行看到的话,留言交流交流啊!!!!! 这篇文章的中心点:围绕着如何降低  internal covari ...

  8. (转) s-video vs. composite video vs. component video 几种视频格式详细说明和比较

    之前对着几种视频格式认识不是很清晰,所以看数据手册的时候,看的也是稀里糊涂的. 因为项目中需要用到cvbs做视频输入,在元器件选型上,看到tw2867的数据手册上,有这么一句话: The TW2867 ...

  9. bioperl 自动化下载genbank 中的序列

    当我们想要从genbank 中下载序列的时候,总需要点击右上角的download 按钮,选择对应的格式,然后通过浏览器进行下载,这样反复的点击很费时间了 其实可以通过bioperl 自动化的完成下载: ...

  10. Eclipse文件首部自动加 作者时间

    Window -> Preferences -> Java -> Code Style -> Code templates -> (in right-hand pane) ...