day17-Python设计模式
一.单例模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
1.使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc
文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
class Singlen(object): def foo(self): pass sing = Singlen()
将上面的代码写入到singlen文件中,使用的时候直接导入对象就可以了。
2.使用装饰器
def Singleton(cls): _instance = {} # 创建一个存储对象的字典 def _singleton(*args, **kargs): # 判断对象是否已经被创建 if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls] return _singleton @Singleton class TestDemo(object): a = 1 def __init__(self, x=0): self.x = x a1 = TestDemo(2) print(id(a1)) # 22331520 a2 = TestDemo(3) print(id(a2)) # 22331520
3.使用类
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj)
4.基于__new__()方法实现
import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): pass def __new__(cls, *args, **kwargs): # 判断类是否有_instance属性 if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): # 创建实例 Singleton._instance = object.__new__(cls) return Singleton._instance obj1 = Singleton() obj2 = Singleton() print(obj1,obj2) def task(arg): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
5.使用元类
- 类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)
- 对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法
5.1元类例子
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): pass obj = Foo() # 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。 obj() # 执行Foo的 __call__ 方法
5.2 实现单例模式
import threading class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType,cls).__call__(*args, **kwargs) return cls._instance class Foo(metaclass=SingletonType): def __init__(self,name): self.name = name obj1 = Foo('name') obj2 = Foo('name') print(obj1,obj2)
二.工厂模式
说道工厂,第一反应是可以生产东西,也就是我们可以通过工厂类创建产品。一个函数,传入需要创建的产品类型,输出的结果是我们需要的类型。
import random import abc # 两种类型的课程 class BasicCourse(object): """ 基础课程 """ def get_labs(self): return "basic_course: labs" def __str__(self): return "BasicCourse" class ProjectCourse(object): """ 项目课 """ def get_labs(self): return "project_course: labs" def __str__(self): return "ProjectCourse" # 两种类型的虚拟机 class LinuxVm(object): """ Linux 虚拟机 """ def start(self): return "Linux vm running" class MacVm(object): """ Mac OSX 虚拟机 """ def start(self): return "Mac OSX vm running" class Factory(metaclass=abc.ABCMeta): """ 抽象工厂类, 现在工厂类不仅能创建课程,还能创建虚拟机了 """ @abc.abstractmethod def create_course(self): pass @abc.abstractmethod def create_vm(self): pass class BasicCourseLinuxFactory(Factory): """ 基础课程工厂类 """ def create_course(self): return BasicCourse() def create_vm(self): return LinuxVm() class ProjectCourseMacFactory(Factory): """ 项目课程工厂类 """ def create_course(self): return ProjectCourse() def create_vm(self): return MacVm() def get_factory(): """ 随机获取一个工厂类 """ return random.choice([BasicCourseLinuxFactory, ProjectCourseMacFactory])() if __name__ == '__main__': factory = get_factory() course = factory.create_course() vm = factory.create_vm() print(course.get_labs()) print(vm.start())
三.策略模式
在现实中,一个问题可能有多种显示,比如一个商场的商品做活动,可能有各种可能,如原价,8折,满100送10快等。策略模式它定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户.
class CashSuper(object): ''' 现金收费抽象类 ''' def accept_cash(self,money): pass class CashNormal(CashSuper): ''' 正常收费子类 ''' def accept_cash(self,money): return money class CashRebate(CashSuper): ''' 打折收费子类 ''' def __init__(self,discount=1): self.discount = discount # 折扣 def accept_cash(self,money): return money * self.discount class CashReturn(CashSuper): ''' 返利收费子类 ''' def __init__(self,money_condition=0,money_return=0): self.money_condition = money_condition self.money_return = money_return def accept_cash(self,money): if money >= self.money_condition: return money - (money / self.money_condition) * self.money_return return money #具体策略类 class Context(object): def __init__(self,csuper): self.csuper = csuper def GetResult(self,money): return self.csuper.accept_cash(money) if __name__ == '__main__': money = input("原价: ") strategy = {} strategy[1] = Context(CashNormal()) strategy[2] = Context(CashRebate(0.8)) strategy[3] = Context(CashReturn(100,10)) mode = int(input("选择折扣方式: 1) 原价 2) 8折 3) 满100减10: ") if mode in strategy: csuper = strategy[mode] else: print("不存在的折扣方式") csuper = strategy[1] print("需要支付: ",csuper.GetResult(money))
四.观察者模式
所谓的观察者模式,就是说当一个对象发生变化时,观察者能及时得到通知并且更新。观察者模式在许多的地方都很有用。定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。发生改变的对象称为观察目标,被通知的对象称为观察者 。一个观察目标可以对应多个观察者
import abc class Subject(object): ''' 被观察对象的基类 ''' def __init__(self): # 观察者列表 self._observers = [] def attach(self,observer): ''' z注册一个观察者 :param observer: :return: ''' if observer not in self._observers: self._observers.append(observer) def detach(self,observer): ''' 删除一个观察者 :param observer: :return: ''' try: self._observers.remove(observer) except ValueError: pass def notify(self): ''' 让所有的观察者执行观察者的更新方法 :return: ''' for observer in self._observers: observer.update(self) class Course(Subject): ''' 课程对象,被观察者 ''' def __init__(self): super(Course, self).__init__() self._message = None @property def message(self): return self._message @message.setter def message(self,msg): ''' 设置message属性 :param msg: :return: ''' self._message = msg self.notify() class Observer(object): """ 观察者抽象类 """ __metaclass__ = abc.ABCMeta @abc.abstractmethod def update(self, subject): pass class UserObserver(Observer): """ 用户观察者 """ def update(self, subject): print("User observer: %s" % subject.message) class OrgObserver(Observer): """ 机构观察者 """ def update(self, subject): print("Organization observer: %s" % subject.message) if __name__ == '__main__': # 初始化一个用户观察者 user = UserObserver() # 初始化一个机构观察者 org = OrgObserver() # 初始化一个课程 course = Course() # 注册观察者 course.attach(user) course.attach(org) # 设置course.message,这时观察者会收到通知 course.message = "two observers" # 注销一个观察者 course.detach(user) course.message = "single observer"
在上面的代码中,最重要的就是Subject类了,它实现了观察者模式中大部分功能。作为一个被观察的对象,Subject实现了注册观察者,注销观察者和通知观察者的功能。接着我们基于Subject创建了我们的课程Course类,并且当我们设置Course.message属性时,Course对象会通知到所有观察者。可以看出,观察者模式使被观察的对象(主题)和观察者之间解耦了。
五.命令模式
顾名思义,命令模式就是对命令的封装。所谓封装命令,就是将一系列操作封装到命令类中,并且命令类只需要对外公开一个执行方法execute,调用此命令的对象只需要执行命令的execute方法就可以完成所有的操作。这样调用此命令的对象就和命令具体操作之间解耦了。更进一步,通过命令模式我们可以抽象出调用者,接收者和命令三个对象。调用者就是简单的调用命令,然后将命令发送给接收者,而接收者则接收并执行命令,执行命令的方式也是简单的调用命令的execute方法就可以了。发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。下面让我们使用 Python 来实现命令模式。
import abc class VmReceiver(object): """ 命令接收者,真正执行命令的地方 """ def start(self): print("Virtual machine start") def stop(self): print("Virtual machine stop") class Command(object): """ 命令抽象类 """ __metaclass__ = abc.ABCMeta @abc.abstractmethod def execute(self): """ 命令对象对外只提供 execute 方法 """ pass class StartVmCommand(Command): """ 开启虚拟机的命令 """ def __init__(self, recevier): """ 使用一个命令接收者初始化 """ self.recevier = recevier def execute(self): """ 真正执行命令的时候命令接收者开启虚拟机 """ self.recevier.start() class StopVmCommand(Command): """ 停止虚拟机的命令 """ def __init__(self, recevier): """ 使用一个命令接收者初始化 """ self.recevier = recevier def execute(self): """ 真正执行命令的时候命令接收者关闭虚拟机 """ self.recevier.stop() class ClientInvoker(object): """ 命令调用者 """ def __init__(self, command): self.command = command def do(self): self.command.execute() if __name__ == '__main__': recevier = VmReceiver() # 创建命令接收者对象 # 开启虚拟机对象的创建 start_command = StartVmCommand(recevier) # 执行命令对象 client = ClientInvoker(start_command) client.do() # 执行命令 # 停止虚拟机对象的创建 stop_command = StopVmCommand(recevier) client.command = stop_command client.do()
六.模板方法模式
提到模板,不难想到文档模板、简历模板等。其实模板方法模式中的模板就是这个意思,在模板方法模式中,我们先定义一个类模板,在这个类中,我们定义了各种操作的顺序(轮毂或者说是骨架),但是并不实现这些操作,这些操作由子类来操作。
import abc class Eating(object): ''' 吃饭模板基类 ''' __metaclass__ = abc.ABCMeta def eating(self): ''' 吃饭方法中,确定要执行哪些操作 :return: ''' self.xicai() self.aoxifan() self.chaocai() print('开始吃饭!!') @abc.abstractmethod def xicai(self): '''定义洗菜类的抽象基类''' pass @abc.abstractmethod def aoxifan(self): '''熬稀饭基类''' pass @abc.abstractmethod def chaocai(self): pass class TomEating(Eating): ''' tom想要吃饭,必须要实现吃饭的步骤 ''' def xicai(self): print('tom is xicai') def aoxifan(self): print('tom is aoxifan') def chaocai(self): print('tom is chaocai') class JohnEating(Eating): ''' tom想要吃饭,必须要实现吃饭的步骤 ''' def xicai(self): print('John is xicai') def aoxifan(self): print('John is aoxifan') def chaocai(self): print('John is chaocai') if __name__ == '__main__': john = JohnEating() john.eating() tom = TomEating() tom.eating()
七.代理模式
代理模式在生活中比比皆是。比如你通过代理上网,比如你不会去华西牛奶生产地直接买牛奶,而是到超市这个代理购买牛奶,这些例子中都存在着代理模式。所谓代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的访问。通过代理,我们可以对访问做一些控制。在开发网站的过程中,针对一些频繁访问的资源,可以使用缓存。
from time import sleep class Redis(object): """ 用于模拟 redis 服务 """ def __init__(self): """ 使用字典存储数据 """ self.cache = dict() def get(self, key): """ 获取数据 """ return self.cache.get(key) def set(self, key, value): """ 设置数据 """ self.cache[key] = value class Image(object): """ 图片对象,图片存在七牛云存储中,我们只保存了一个地址 """ def __init__(self, name): self.name = name @property def url(self): sleep(2) return "https://dn-syl-static.qbox.me/img/logo-transparent.png" class Page(object): """ 用于显示图片 """ def __init__(self, image): """ 需要图片进行初始化 """ self.image = image def render(self): """ 显示图片 """ print(self.image.url) redis = Redis() class ImageProxy(object): """ 图片代理,首次访问会从真正的图片对象中获取地址,以后都从 Redis 缓存中获取 """ def __init__(self, image): self.image = image @property def url(self): addr = redis.get(self.image.name) if not addr: addr = self.image.url print("Set url in redis cache!") redis.set(self.image.name, addr) else: print("Get url from redis cache!") return addr if __name__ == '__main__': img = Image(name="logo") proxy = ImageProxy(img) page = Page(proxy) # 首次访问 page.render() print("") # 第二次访问 page.render()
八.组合模式
什么是组合模式?按照定义来说,组合模式是将对象组合成树形结构表示,使得客户端对单个对象和组合对象的使用具有一致性。组合模式的使用通常会生成一颗对象树,对象树中的叶子结点代表单个对象,其他节点代表组合对象。调用某一组合对象的方法,其实会迭代调用所有其叶子对象的方法。
使用组合模式的经典例子是 Linux 系统内的树形菜单和文件系统。在树形菜单中,每一项菜单可能是一个组合对象,其包含了菜单项和子菜单,这样就形成了一棵对象树。在文件系统中,叶子对象就是文件,而文件夹就是组合对象,文件夹可以包含文件夹和文件,同样又形成了一棵对象树。同样的例子还有员工和领导之间的关系
import abc class Worker(object): """ 工作抽象类 """ __metaclass__ = abc.ABCMeta def __init__(self, name): self.name = name @abc.abstractmethod def work(self): # 工作的抽象基类 pass class Employe(Worker): """ 员工类 """ __metaclass__ = abc.ABCMeta def work(self): print("Employ: %s start to work " % self.name) class Leader(Worker): """ 领导类 """ def __init__(self, name): self.members = [] super(Leader, self).__init__(name) def add_member(self, employe): if employe not in self.members: self.members.append(employe) def remove_member(self, employe): if employe in self.members: self.members.remove(employe) def work(self): print("Leader: %s start to work" % self.name) for employe in self.members: employe.work() if __name__ == '__main__': employe_1 = Employe("employe_1") employe_2 = Employe("employe_2") leader_1 = Leader("leader_1") leader_1.add_member(employe_1) leader_1.add_member(employe_2) employe_3 = Employe("employe_3") leader_2 = Leader("leader_2") leader_2.add_member(employe_3) leader_2.add_member(leader_1) leader_2.work()
九.外观模式
所谓外观模式,就是将各种子系统的复杂操作通过外观模式简化,让客户端使用起来更方便简洁。比如你夏天晚上出门时,要关闭电灯,关闭电视机,关闭空调,如果有了一个总开关,通过它可以关闭电灯,电视机和空调,你出门的时候关闭总开关就行了。
class User(object): """ 用户类 """ def is_login(self): return True def has_privilege(self, privilege): return True class Course(object): """ 课程类 """ def can_be_learned(self): return True class Lab(object): """ 实验类 """ def can_be_started(self): return True class Client(object): """ 客户类,用于开始一个实验 """ def __init__(self, user, course, lab): self.user = user self.course = course self.lab = lab def start_lab(self): """ 开始实验,需要一系列的判断:用户是否登陆,课程是否可以学习,实验是否可以开始。判断非常繁琐! """ if self.user.is_login() and self.course.can_be_learned() and self.lab.can_be_started(): print("start lab") else: print("can not start lab") class FacadeLab(object): """ 新的Lab类,应用了面向对象模式 """ def __init__(self, user, course, lab): self.user = user self.course = course self.lab = lab def can_be_started(self): if self.user.is_login() and self.course.can_be_learned() and self.lab.can_be_started(): return True else: return False class NewClient(object): """ 新的客户类,使用外观模式 """ def __init__(self, facade_lab): self.lab = facade_lab def start_lab(self): """ 开始实验,只需要判断 FacadeLab 是否可以开始 """ if self.lab.can_be_started: print("start lab") else
- 企业级Docker Registry开源工具Harbor的用户使用指南
- 后渗透工具Koadic:你真的了解我吗?
- Docker1.12尝试
- 颁奖乌龙就算了,怎么还性别歧视,好莱坞电影怎么了?数据笑而不语
- Python中关于集合(set)的思考
- 构建属于自己的原生docker images
- Docker-client for python使用指南
- Ansible基本配置以及使用示例
- redis超时原因系统性排查
- overlayfs存储驱动的使用以及技术探究
- 分页解决方案 之 分页算法——Pager_SQL的详细使用方法和注意事项
- 利用虚拟硬盘(把内存当作硬盘)来提高数据库的效率(目前只针对SQL Server 2000)可以提高很多
- 分页解决方案 之 分页算法——Pager_SQL的思路和使用方法
- 让你的笔记本更快一点——我的笔记本的性能测试和虚拟硬盘(把内存当成硬盘)的使用感觉
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 切面编程(环绕通知与前后置通知区别)
- Spring在代码中获取bean的几种方式
- Spring 一个接口多个实现类怎么注入
- ASP.NET MVC Controller的激活
- js 逗号表达式
- spring动态调用方法
- Spring AOP动态代理原理与实现方式
- 基于注解多数据源解决方案
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
- 你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?
- java阻塞队列得实现
- 谈谈如何利用 valgrind 排查内存错误
- 用java写一个死锁
- Runnable和Thread比较
- 使用@ConditionalOnProperty注解