精通Python设计模式
写在前面的话
初衷:日久健忘,有此笔记可略复查,践行DRY原则(Don’t Repeat Yourself)。
模式 即是 一类问题成熟的解决方案,可复用编程解决方案。
通俗来说,就是“套路”,自古套路得人心,所有的应用都是基于生活的抽象,IT应用也不例外。
设计模式是基于面向对象系统中重复出现的设计问题。之于个人,一个老派的coder出身的ITer,对面向对象的把握也是泛泛了解,当下从面向对象(OO)—> 面向构件(OC)—> 面向服务(SO),各种层级具有一定抽象。
目标:强化面向对象思维,用OO视角去思考问题。
面向对象设计
面向对象设计(OOD) 遵循“SOLID(稳定的)”原则。
单一职责原则(Single Responsibility Principle):一个类只负责一个功能,是实现“高内聚、低耦合”方针;
开闭原则(Open Closed Principle): 对扩展开放,对修改关闭。
里氏替换原则(Liskov Substitution Principle):基类出现的地方,子类一定能替换。程序中尽量使用基类类型对 对象进行定义,运行时候再确定子类的类型,用子类对象来替换基类对象。
接口隔离原则(Interface Segregation Principle):使用多个接口,比使用单一接口好的多,保证接口纯洁性,降低耦合度。
依赖倒置原则(Dependence Inversion Principle):是开闭原则基础,针对接口编程,依赖于抽象 不依赖于具体。
迪米特法则(Law of Demeter):最小知道原则,一个实体应当尽量减少与其他实体之间的相互作用,使得模块相对独立。
模式分类
三类:创建型、结构型、行为型。
创建型:指用来创建的对象的一系列模式的统称;
结构型:主要用来表示对象与类之间的组合关系;
行为型:表示类与对象或对象与对象之间的交互关系。
常用的 大致 分为 23种。
第一章 : 工厂模式(Factory)
顾名思义,典型的创建型模式,包含 工厂 和 抽象工厂 两种。
客户端(调用对象的代码) 在不知道对象来源(对象由哪个类产生)的情况下,要求创建一个对象。思想出发点是:通过将创建对象的代码 与 使用对象的代码解耦,简化对象创建过程。
1. 工厂方法
工厂方法:基于单一的函数来处理对象创建任务。通过传参数,体现意图,创建需要的对象。每个工厂方法从逻辑上将具有相同点的对象创建过程划分为一组。有链接数据库的工厂方法,有创建图形的工厂方法。
用例:1. 塑料玩具制造,材料相同,使用不同模具生产不同的玩具。
2. Django 框架中的 表单 就是基于工厂方法,支持创建不同的种类的字段。
# 示例 from django import forms class PersonForm(forms.Form): name = forms.CharField(max_length=100) birth_date = forms.DateField(required=Flase)
2. 抽象工厂
抽象工厂 是一般化的工厂方法,可以理解为 工厂过程的 泛化(generalization)。
一个抽象工厂 是 一些工厂方法 逻辑的 集合,这其中,每一个工厂方法负责生成一种不同的对象。
用例:1. 汽车制造方面,机械冲压不同车型的零件(车门、发动机盖子、挡泥板等)。
如何知道使用 工厂方法 还是 抽象工厂 方法?
答: 通常从简单的工厂方法开始。若发现需要多个工厂方法,且将这些方法组合起来创建一系列对象是有意义的,就用抽象工厂。
第二章:建造者模式(Builder)
如需创建 一个由多个部分组成的对象,而且创建过程需要一步一步地完成,只有创建了所有部分,该对象才是完整的。这种情况下 建造者模式 可以提供帮助。
建造者模式:将复杂对象的构建与其表示分离。参与者包含两方面:建造者(builder)和指挥者(director)。
建造者:负责创建复杂对象的各个部分的组件。例如:HTML页面生成器,中的部分有:标题、主体、脚注等;
指挥者:使用 建造者实例 控制构建过程的组件。如调用建造者函数来设置标题,头部等等。
用例:
- 快餐店场景。不同的汉堡(经典、芝士等)和不同包装(大杯、中杯、超大杯),制作汉堡和包装过程相同,区别表现形式不同。这种情况下 指挥者是 收银员,建造者 是负责处理订单的员工。
- 组装电脑。 有CPU、内存、硬盘、电源、主板、键盘、显示器 不同的组合。这种情况下,组装工程师 是 指挥者。
与工厂模式 区别:工厂模式实在单个步骤中创建对象,而建造者是在多个步骤中创建对象,几乎总是使用指挥者。
实现:
核心组成几个类:
- Builder :抽象建造者,定义多个通用方法及构建方法;
- ConcreteBuilder:具体建造者,可有多个(产品高低中配流程建造);
- Director:指挥者 ,具体控制整个组合过程,将需求交给建造者,由建造者去创建对象;
- Product: 具体产品角色。
第三章 :其他创建型模式
1. 原型模式
情况1:当创建对象比较复杂,为提高对象实例创建的效率;情况2:需要保存对象的状态,以便未来某个时间点通过副本恢复。以上两种情况需要使用 克隆 技术,基于现有对象创建对象时,原型模式非常有用。
其思想是使用一个具有某个对象完整结构的副本来生成新的对象。
当现有对象需要保持不变,而通过创建它的精确副本,以便更改副本的某些部分时候,原型模式应用意义在此。
# 该模式为python内置特性 # clone()函数 # 参数为一个对象,返回副本 A2 = copy.deepcopy(A1)
用例:日常DevOps团队管理对个网站时候,需要共同维护一些数据。
浅拷贝: 只会拷贝一些基本数据类型的副本(int、string、boolean), 复杂数据类型不会拷贝,仅拷贝引用;
深拷贝:基于serializeable 序列化读取二进制流实现。此处涉及 序列化 和 反序列化操作。
2. 单例模式
单例模式 提供了一种实现类的方法,从该类中只能创建一个对象,因此成为 单例。
例如为程序存储和维护一个全局状态。
单例模式 将类的实例化限制为一个对象,需要一些机制来防止类的多次实例化,并防止克隆。
用例:通用:控制对共享资源的并发访问、发号器(自增序号生成器)、Redis连接对象等。
分类:懒汉和饿汉模式。懒汉模式:所谓懒加载,延迟创建对象,再用的时候再创建;加载cls时候,对象就创建好了,是为饿汉模式。
实现:Python3中使用 元类 技巧。首先为单例模式实现一个元类,它是用于实现单例模式的类的类(或类型)。
元类实现:通过构造函数私有化,懒汉模式时需保证多线程安全(类似Synchronized)加锁,基于双重检查锁定 DCL(Double-Checked-Locking)。
# 假设一个网页抓取类 class URLFetcher(metaclass=SingletonType): pass
第四章 :适配器模式
适配器属于结构型设计模式,提出了一种组合对象以创建新功能的方法。
适配器模式,使得两个或多个不兼容的接口兼容,允许将具有不同接口的血多对象调整为一个统一的接口。或可以理解为:调整已经存在(实现)的接口的对象,以实现另一个接口,这也称之为 接口适应。
用例: 到国外,欧美充电器接口(iterface_a()),不兼容 大陆的 充电器接口(iterface_b()) ,这时候需要将 a 接口 转换为 b接口。软件上,Python中的Pyramid Web框架利用适配器使现有想能够符合特定的API。
实现:
# 定义一个适配器类 # __init__()方法的obj参数就是我们想要修改的对象 # adapted_methods 是一个字典,它包含与客户端调用的方法 和应该调用方法匹配的键值对 class Adapter: def __init__(self, obj, adapted_methods): self.obj = obj self.__dict__.update(adapted_methods) def __str__(self): return str(self.obj)
第五章 :装饰器模式
装饰器模式:通常用于扩展函数、方法或类行为的技术,可以在不适用继承的情况下扩展对象的行为,降低耦合;透明,跟适配器 显式方式不一样。
用例:枪上面 安装 消音器,或者相机 安装不同的镜头。
实现:它获取函数对象func_in 作为输入,并返回另一个函数对象 func_out。
# Python 的functools.wraps() 为创建装饰器时候提供方便 # 创建一个memoize()装饰器 # 接受一个需要缓存的函数fn作为输入,使用一个名为cache的dict作为缓存的数据容器。 import functools def memoize(fn): cache = dict() @functools.wraps(fn) def memoizer(*args): if args not in cache: cache[args] = fn(*args) return cache[args] return memoizer
第六章:桥接模式
桥接模式 是预先设计,以便将实现与抽象解耦;
用例:在多个对象中共享实现时候,网络上的信息产品共享,说明书等,软件中像设备驱动程序。
实现: 1 为抽象的接口 定义类(Cls)
2 为实现者定义ClsFetcher类
3 定义多个实现类 implementation
第七章:外观模式
外观设计: 帮助我们隐藏系统内部复杂特性,并通过一个简化的接口,只向客户端公开需要的内容。
用例: 计算机启动过程的封装;汽车/摩托车钥匙;银行客服等;
实现:
# 客户端调用 透明,内部修改透明; # 实现一个简单的 微内核 操作系统 # 功能有文件服务器、进程服务器、内核服务器 from enum import Enum from abc import ABCMeta,abstractmethod class User: pass class Process: pass class File: pass # 定义server 类 State = Enum('State', 'new running sleeping restart zombie') class Server(metaclass=ABCMeta): @abstractmethod def __init__(self): pass def __str__(self): return self.name @abstractmethod def boot(self): pass @abstractmethod def kill(self, restart=True): pass # 定义 FileServer 类 class FileServer(Sever): def __init__(self): self.name = 'FileSever' self.state = State.new def boot(self): print(f'boot the {self.name}') ''' 启动文件服务器操作 ''' self.state = State.running def kill(self,restart=True): print(f'Kill {self}') '''终止文件服务器所需要的操作''' self.state = State.restart if restart else State.zombie def create_file(self, user, name, permissions): '''检查访问权限、用户权限的有效性''' print(f" trying to create the file '{name}' for user '{user}' with permissions {permissions} ") # 定义 ProcessServer # 方法同上 def ProcessServer(Server): pass # 定义客户端调用外观 class OperatingSystem: '''外观''' def __init__(self): self.fs = FileServer() self.ps = ProcessServer() def start(self): [i.boot() for i in (self.fs,self.ps)] def create_file(self, user, name, permissions): return self.fs.create_file(user, name, permissions) def create_process(self, user, name): return self.ps.create_process(user, name) def main(): os = OperatingSysterm() os.start() os.create_file('foo', 'hello', '-rw-r-r') os.create_process('bar', 'ls /tmp') if __name__ == '__main__': main()
第八章: 其他结构型模式
还存在其他结构型模式:享元模式、MVC模式、代理模式。
1. 享元模式
WHY: OOP’System 可能会由于对象创建的开销面临性能问题。这在嵌入式系统、PDA等移动设备中非常明显。
WHAT:享元模式可以让相似的对象共享资源来最小化内存的使用。 通过在相似对象之间引入数据共享技术来提高性能。一个享元就是一个共享对象,包含状态独立,不可变的数据(内部数据)。
用例:游戏中的 队伍中的士兵,森林中的树木等;
WHERE:应用程序需要使用大量的对象,对象之间存在相似度;应用程序不依赖于对象ID;对象太多,以至于存储/渲染代价太大。
HOW: 示例见书本P57页,停车场范例。
2. MVC模式
关注点分离(SOC)原则是软件工程设计原则之一。背后思想应用程序分成不同的部分,每一个部分处理一个单独的关注点。分层设计中的层就是关注点的一个实例。
MVC:Model-模型+View-视图+Controller-控制器的简称,实现了模型与其表示之间的解耦。通常来说MVC是架构模式,涉及面较设计模式宽泛;惯用法—-> 设计模式—->架构模式 三层递进。
Model是应用的核心组件,代表信息的源头,包含应用程序的业务逻辑、数据状态和规则。View是模型的可视化表示,是数据的表现形式。Controller是模型与试图之间的连接器或粘合剂,M和V之间的通信需要通过C进行。
基于MVC衍生出很多不同的模式:如MVT(Python的WEB框架Django)、MVA(模型-视图-适配器)、MVP(模型-视图-演示者)等。带来好处是,分工明确,维护容易。
用例:盖房子,水电工、瓦工等,餐馆里服务员接受订单,饭菜厨师做。
实现: 智能的模型、纤薄的控制器和傻瓜式的视图。
示例:P67页 打印引用语例子
3. 代理模式
**代理模式(Proxy)**:顾名思义,代理被访问的对象或访问的对象,该模式用于在访问实际对象之前要执行相关的操作。有如下四种:
- 远程代理:作为实际存在于不同地址空间(网络服务器)中的对象的本地表示。
- 虚拟代理:使用延迟初始化将计算开销大的对象的创建延迟到需要的时候进行。
- 保护/防护代理,控制对于敏感对象的访问。
- 智能代理/引用代理:对象被访问时候执行额外的操作,如引用计数或线程安全检查。
用例:正向代理(VPN),代理的对象是客户端,服务器不知道是谁在访问;
**反向代理(**Nginx), 代理的对象是访问的服务器,客户端不知道访问的是哪个服务器。
第九章: 职责链模式
职责链模式:类似于计算机网络,当希望用多个关联的对象满足单个请求,或者事先不知道某个特定的请求应该由对象链上的哪个来处理时候,就会使用职责链模式。
应用程序层面,关注点是 对象 和 请求流上。客户端只知道第一个处理元素,不知道所有处理元素的引用,每个处理元素知道它的下一个相邻节点,也即后继元素,而不知道其他元素。类似于单链表,不允许双向导航,实现了发送方(客户端) 与 接收方(处理元素)之间的解耦。
思想:松耦合系统背后的思想是简化维护的过程,同时更容易理解系统是如何工作;
用例:1.自动取款机/贩卖机,当纸币从单一入口进去,不同的纸币被分配到合适的容器中。2.软件领域:Java的servlet过滤器是在HTTP请求到达目标之前执行的代码片段。在使用servlet过滤器时候,有一个过滤器链。每个过滤器执行不同的操作(用户身份验证、日志记录、数据压缩等),并将请求转发到下一个过滤器,知道链耗尽,或者出现错误时中断,例如身份验证连续验证三次失败。3. 财务审批工作流。
实现:一个简单的基于事件的系统 BOOK P76页。
第十章: 命令模式
命令模式(COMMAND):该模式将操作(撤销、恢复、复制、粘贴)封装为对象,不必直接执行一个命令,它可以按照您的意愿执行。调用该命令的对象 与 知道如何执行该命令的对象解耦。调用程序不需要知道该命令的任何细节。若有必要,可以对多个对象进行分组,以允许调用程序按顺序执行,这在实现多级撤销命令时候非常有用。
用例:1. 餐馆吃饭,向服务员点菜。点单的单据就是命令的一个例子。写好订单,服务员放入厨师执行的单据队列中。每个单据都是独立,可以用来执行许多不同的命令。2. GUI按钮和菜单项。3.事务行为和日志。4. 宏。
实现:见BOOK P87页
第十一章: 观察者模式
观察者模式(Observer): 当需要在另一个状态变化更新一组对象时,该模式有应用的必要。其描述单个对象(发布者,也成为主题或可观察对象) 和一个或多个对象(订阅者,也成为观察者)之间的发布-订阅关系。
用例:1. 对于MVC框架来说,发布者是模型,订阅者是视图,MVC模式中 往往 多种视图使用同一模型的数据,当修改模型时候,需要更新两个视图。2. 拍卖场景,竞买人是订阅者,拍卖人为主题,更新竞买价格,并将新价格广播给所有的竞买人。3. RSS订阅和社交媒体的关注。4.事件驱动系统:事件是主题,监听器是订阅者。
思想:与关注点分离原则思想相同,即增加发布者和订阅者之间的解耦。
实现:见BOOK P92页
第十二章: 状态模式
面向对象编程(OOP) 聚焦于维护相互作用的对象的状态。解决问题时候,有限状态机(通常称为状态机) 是一个非常方便的状态转换建模工具。
状态机是一台抽象机器,它具有两个关键属性–状态和转换。状态是系统的当前(激活)状态。状态机的一个很好的特性是可以表示为图(称为状态图),其中每个状态是一个节点,每个转换是两个节点之间的一条线。
用例:交通灯、自动售货机。在软件工程领域,事件驱动的系统也是一个例子。在事件驱动系统中,从一个状态的转换出发一个事件/消息。如电脑游戏,当主角接近怪物时,怪物可能从守卫状态变换为攻击状态。
Thomas Jaeger
的话:# 状态设计模式能够在上下文中对无线数量的状态进行完全封装,以提高可维护性和灵活性。
实现:
# Python 中复用 state_machine 模块 中State、Event、acts_as_state_machine # 安装:pip install state_machine # 使用装饰器 @before、@after # 具体示例:BOOK P104
第十三章: 其他行为型模式
1. 解释器模式
思想:非初学者用户和领域专家能够使用简单的语言,在处理应用程序时候获得更高的效率。
用例:音乐家是解释器的一种模式。乐谱音乐的语言,音乐家是这种语法的解释者。
目标:向专家(通常不是程序员)提供正确的编程抽象,以使其具有生产力。解释器同时也适应于物联网(IoT)。
解释器模式用于向高级用户和领域专家提供类似于编程的框架,但不暴露编程语言的复杂性。这是通过实现DSL来完成的。内部DSL构建在宿主编程语言之上并依赖于它,而外部DSL是从头实现的,不依赖于现有编程语言。解释器只与内部DSL相关。
实现:见BOOK P113页
# 许多Python模板引擎使用内部DSL。 # PyT是用于生成(X)HTML的高性能Python DSL # 虽然解析通常不是由解释器模式处理的,但实现部分,我们使用Pyparser创建了一个控制智能房屋的DSL,并且发现使用一个好的解析工具可以简化使用模式匹配来解释结果的过程。
2. 策略模式
思想:策略模式提倡使用多种算法来解决问题。它的杀手级特性是:在运行透明地切换算法(客户端代码不知道这种变化)。在成本、事件、方便等条件之间存在权衡取舍。
用例:排序(顺序、二分)、加密、日志等。
实现:见BOOKP119页。
3. 备忘录模式
思想: 有时候需要轻松获取对象的内部状态快照,以便在需要时候使用它来恢复对象。
组成: 备忘录:一个包含基本状态存储和检索能力的简单对象。
发起人:一个获取和设置备忘录实例值得对象。
管理者:一个可以存储和检索以前创建的所有备忘录实例的对象。
与 命令模式 有很多相似之处。
用例:诸如用语的词典,不停的更新,若了解该语言过去某个时候是如何使用的,就要查看旧版本。为用户提供某种撤销 和恢复功能。另一种用法实现 一个带有确认/取消按钮的UI的对话框。
实现:
# 完整示例:BOOK P124页 # 复用pickle 工具包或 模块来实现一个简单的 引用 from pickle import dumps,loads class Quote: def __init__(self,text,author): pass def save_state(self): pass def restore_state(self,memnto): pass def __str__(self): pass
4. 迭代器模式
WIKI 百科定义:
迭代器是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。迭代器模式将算法与容器解耦。迭代器已经成为Python语言特性之一,在上下文中广泛使用。
用例: 1. 在教室里,老师给每个学生发课本;
2.餐馆服务员在一张桌子旁为客人服务,并为每个人点菜。
当有如下行为时候,使用迭代器模式:
1. 简化集合中的导航操作; 2. 在任意点获取集合中的下一个对象; 3. 在完成对集合的遍历后停止。
Python 中容器或序列类型有:列表、字符串、元组、字典、集合等;
实现:
# 自定义,需要实现 __iter()__ 和 __next()__ 方法 # 一个迭代器 类 # 一个容器对象类的实例 # 通过实例属性 设置 容器实例对象 初始化迭代器对象。 # 完整示例见 BOOK P127页
5. 模板模式
思想: 在实现具有结构相似性的算法时,我们使用模板来消除冗余代码。
用例:同一家公司的工人来说的日常工作非常接近于模板的设计模式。所有的工人都或多或少遵循相同日程,但日程的各个部分有很大不同。
实现:
# 实现一个 广告牌 模板算法 def generate_banner(msg,style): print('---start of banner ---') print(style(msg)) print('---end of banner ---nn') def dot_style(msg): pass def admire_style(msg): pass def cow_style(msg): pass def main(): styles = [dots_style,admire_style,cow_style] msg = 'happy coding' [generate_banner(msg,style) for style in styles] if __name__ == "__main__": main()
第十四章 响应式编程中的观察者模式
观察者模式,一般用于在给定对象的状态发生变化时通知一组对象。这种类型的传统观察者应用发布-订阅原则,允许我们对一些对象更改事件作出反应。
当必须处理的许多事件并且其中一些事件相互依赖时,传统的方法可能会导致复杂、难以维护的代码。而 响应式编程 的范例 为我们提供了一个有趣的选择,简单地说,响应式编程对许多事件、事件流作出反应,并保持整洁。
用例: Excel中表格之间的关联,一个单元格内容修改,所关联的公式的单元格内容也会变化。Python中RxPY是一个响应式编程概念的实现。
集合管道:集合管道是一种编程模式,在这种模式中,您将一些计算组织为一系列操作,这些操作将收集一个操作的输出,并将其输入到下一个操作中。 如在处理数据时候,对序列对象进行map(映射)、reduce(规约)、groupby(聚合)等操作。
核心:对数据流和事件流做出反应,就像在自然界看到的水流一样。
实现:见BOOK P136页。
第十五章 微服务与面向云的模式
微服务:来源于工程团队不断的时间,以确保服务器、服务交付和操作有更高的响应性和伸缩性。该模式可以将应用程序构建为一组松散耦合的协作服务。在这样的体系结构样式中,应用程序可能由订单管理服务、客户管理服务等服务组成,并独立地开发和部署服务。类似于现代建筑风格。
用例:注入很多的异步微服务开发平台,解决分布式数据管理问题,专注于业务逻辑。
一般构建如下特征的应用程序时候,需考虑微服务体系结构的设计:
- 支持不同客户端,桌面或移动客户端等;
- 有一个供第三方使用的API;
- 必须使用消息传递与其他应用程序通信;
- 通过访问数据库、与其他系统通信以及返回正确类型的响应(JSON、XML等);
- 有对应于应用程序的不同功能区域的逻辑组件。
实现:微服务往往跟容器技术结合使用,如Docker,这方面类似于Python开发中每个项目对应不同的 虚拟环境 (VirtualEnv)。
分类:
1. 重试模式 2. 断路器模式 3. 旁路缓存模式 4. 节流模式
1. 重试模式
重试 是微服务 和 基于云的基础设施环境中需要日益增多的一种方法。在这些环境中,组件彼此协作,但不是由相同的团队开发、部署和操作的。
WHY: 日常操作中,程序可能经历所谓的瞬时故障或失败,这意味着一些小问题看起来像故障,但其实并不是由程序本身导致的,而是由于网络或外部服务器/服务性能限制造成的。
建议使用重试模式,以减轻在于外部组件或服务通信时候,由于网络故障或服务器过载而识别的瞬时故障信息。
重试模式,属于容错机制范畴(还有超时),往往通过参数来调整重试策略,比如 固定等待 (两次重试之间等待一个固定时间)或 指数退避(随着时间的推移,以增量的方式增加重试时间的长度)
2. 断路器模式
WHY:使用断路器,可以将脆弱的函数调用(或与外部服务的集成点)包装在一个特殊的断路器对象中,用于监视故障。一旦故障达到某一阀值,断路器就会跳闸,进一步调用都会返回错误,从而保护调用。
断路器也是一种容错机制,Python中 可以使用PyBreak库在应用程序中添加断路器。
3. 旁路缓存模式
WHY: 对于不经常修改的数据和不依赖于存储中的一组条目(多个键)的一致性的数据存储,旁路模式非常有用。一般有两种情况:
1. 需获取数据时候,若从缓存中找到,就返回,没找到,再从数据库中读取,将读取项放入缓存并返回它。类似于 程序运行 内存加载。 2. 更新数据项时候,在数据库写入该项,并从缓存中删除相应的数据项。
4. 节流模式
WHY: 服务是一种资源,访问可用性资源往往是有代价的,所以必要情况下,资源应采取一定的访问控制保护机制,并确保服务不会因为某个特定的租户而不堪重负, 防止被用户过度使用。
用例:对于某些WEB API 服务,将总访问量限制在N次/每天;限制已验证用户的读写次数。
后记
“设计模式是被发现的,而不是被发明的”。
—Alex Martelli 另一位Python的重要贡献者。
作为一本“简约”自学参考书籍,书本的章节清晰,入点比较大众化,适合一般阅读。
之所以对此书做了笔记,本因是个人作为从“过程编程”时代的过来人,对面向对象的抽象概念了解不是很透; 直接因素是个人在2020年度的 软考高级考试–系统分析师中,面向对象方面失分较多,最终在案例上差了3分,甚觉哀感。所以忙活了手指头,摘记了此书的一部分,供以后随手参阅。