├── .gitignore ├── J2eeInterceptingFilterDemo.java ├── J2eeServiceLocatorPatternDemo.java ├── J2eeTransferObjectPatternDemo.java ├── README.md ├── j2ee模式-业务代表模式.py ├── j2ee模式-前端控制器模式.py ├── j2ee模式-数据访问对象模式.py ├── j2ee模式-组合实体模式.py ├── monkey_patch_pattern ├── README.md ├── a.py ├── b.py ├── run.py └── 真实的适用猴子补丁例子 │ ├── monkey_color_log.py │ └── monkey_json.py ├── monkey_print2.py ├── tests └── 单例模式使用不当.py ├── 创建型模式-享元模式-元类.py ├── 创建型模式-享元模式-装饰器版本.py ├── 创建型模式-享元模式.py ├── 创建型模式-享元模式_只运行一次init.py ├── 创建型模式-单例模式-元类.py ├── 创建型模式-单例模式-装饰器.py ├── 创建型模式-单例模式-重写new但不每次执行init方式.py ├── 创建型模式-单例模式.py ├── 创建型模式-原型模式.py ├── 创建型模式-对象池模式.py ├── 创建型模式-建造者模式.py ├── 创建型模式-抽象工厂模式.py ├── 创建型模式-简单工厂模式.py ├── 忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。 ├── 4种编程方式,描述一个人吃喝拉撒,(分男生 女生) │ ├── 使用一个入口函数来自己调用各种辅助方法,当辅助方法需要变化时候,除了复制粘贴扣字来实现,别无他法。person1.py │ ├── 全局变量加函数写法 │ │ ├── xiaohong.py │ │ ├── xiaomin.py │ │ └── xiaowang.py │ ├── 无效废物装逼滑稽类 伪oop.py │ ├── 真正正宗oop person_oop.py │ └── 频繁return和重复传参和复制全流程的写法 │ │ ├── person_f.py │ │ └── person_f_girl.py ├── README.md ├── python编程范式培训及万能oop转化公式.docx └── 没有发明革命性的新功能的类.py ├── 结构型模式-mvc模式.py ├── 结构型模式-代理模式.py ├── 结构型模式-外观模式.py ├── 结构型模式-桥接模式.py ├── 结构型模式-组合模式.py ├── 结构型模式-装饰者模式-函数装饰器.py ├── 结构型模式-装饰者模式-类装饰器.py ├── 结构型模式-装饰者模式.py ├── 结构型模式-适配器模式.py ├── 行为型模式-中介者模式.py ├── 行为型模式-发布订阅模式.py ├── 行为型模式-命令模式.py ├── 行为型模式-备忘录模式.py ├── 行为型模式-延迟计算缓存模式.py ├── 行为型模式-模板模式.py ├── 行为型模式-状态模式.py ├── 行为型模式-登记模式.py ├── 行为型模式-策略模式.py ├── 行为型模式-观察者模式-重新实现日志系统.py ├── 行为型模式-解释器模式.py ├── 行为型模式-访问者模式.py ├── 行为型模式-责任链模式.py ├── 行为型模式-过滤器模式.py ├── 行为型模式-迭代器模式-使用类.py ├── 行为型模式-迭代器模式.py └── 行为型模式-迭代器模式3.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea/ 3 | env_hotels/ 4 | henv/ 5 | venv/ 6 | *.pyc 7 | app/apis/logs/ 8 | app/logs/ 9 | *.log.* 10 | *.log 11 | *.lock 12 | *.pytest_cache* 13 | nohup.out 14 | apidoc/ 15 | node_modules/ 16 | hotelApi/ 17 | 忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式/* -------------------------------------------------------------------------------- /J2eeInterceptingFilterDemo.java: -------------------------------------------------------------------------------- 1 | /* 2 | 拦截过滤器模式(Intercepting Filter Pattern)用于对应用程序的请求或响应做一些预处理/后处理。定义过滤器,并在把请求传给实际目标应用程序之前应用在请求上。过滤器可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。 3 | 4 | 过滤器(Filter) - 过滤器在请求处理程序执行请求之前或之后,执行某些任务。 5 | 过滤器链(Filter Chain) - 过滤器链带有多个过滤器,并在 Target 上按照定义的顺序执行这些过滤器。 6 | Target - Target 对象是请求处理程序。 7 | 过滤管理器(Filter Manager) - 过滤管理器管理过滤器和过滤器链。 8 | 客户端(Client) - Client 是向 Target 对象发送请求的对象。 9 | */ 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public interface Filter { 15 | public void execute(String request); 16 | } 17 | 18 | class AuthenticationFilter implements Filter { 19 | public void execute(String request){ 20 | System.out.println("Authenticating request: " + request); 21 | } 22 | } 23 | 24 | class DebugFilter implements Filter { 25 | public void execute(String request){ 26 | System.out.println("request log: " + request); 27 | } 28 | } 29 | 30 | class Target { 31 | public void execute(String request){ 32 | System.out.println("Executing request: " + request); 33 | } 34 | } 35 | 36 | class FilterChain { 37 | private List filters = new ArrayList(); 38 | private Target target; 39 | 40 | public void addFilter(Filter filter){ 41 | filters.add(filter); 42 | } 43 | 44 | public void execute(String request){ 45 | for (Filter filter : filters) { 46 | filter.execute(request); 47 | } 48 | target.execute(request); 49 | } 50 | 51 | public void setTarget(Target target){ 52 | this.target = target; 53 | } 54 | } 55 | class FilterManager { 56 | FilterChain filterChain; 57 | 58 | public FilterManager(Target target){ 59 | filterChain = new FilterChain(); 60 | filterChain.setTarget(target); 61 | } 62 | public void setFilter(Filter filter){ 63 | filterChain.addFilter(filter); 64 | } 65 | 66 | public void filterRequest(String request){ 67 | filterChain.execute(request); 68 | } 69 | } 70 | 71 | class Client { 72 | FilterManager filterManager; 73 | 74 | public void setFilterManager(FilterManager filterManager){ 75 | this.filterManager = filterManager; 76 | } 77 | 78 | public void sendRequest(String request){ 79 | filterManager.filterRequest(request); 80 | } 81 | } 82 | 83 | public class J2eeInterceptingFilterDemo { 84 | public static void main(String[] args) { 85 | FilterManager filterManager = new FilterManager(new Target()); 86 | filterManager.setFilter(new AuthenticationFilter()); 87 | filterManager.setFilter(new DebugFilter()); 88 | 89 | Client client = new Client(); 90 | client.setFilterManager(filterManager); 91 | client.sendRequest("HOME"); 92 | } 93 | } 94 | 95 | //j2ee模式-拦截过滤器模式 96 | /* 97 | Authenticating request: HOME 98 | request log: HOME 99 | Executing request: HOME 100 | */ -------------------------------------------------------------------------------- /J2eeServiceLocatorPatternDemo.java: -------------------------------------------------------------------------------- 1 | /*服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候。考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在 JNDI 中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。以下是这种设计模式的实体。 2 | 3 | 服务(Service) - 实际处理请求的服务。对这种服务的引用可以在 JNDI 服务器中查找到。 4 | Context / 初始的 Context - JNDI Context 带有对要查找的服务的引用。 5 | 服务定位器(Service Locator) - 服务定位器是通过 JNDI 查找和缓存服务来获取服务的单点接触。 6 | 缓存(Cache) - 缓存存储服务的引用,以便复用它们。 7 | 客户端(Client) - Client 是通过 ServiceLocator 调用服务的对象。*/ 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public interface Service { 12 | public String getName(); 13 | public void execute(); 14 | } 15 | 16 | class Service1 implements Service { 17 | public void execute(){ 18 | System.out.println("Executing Service1"); 19 | } 20 | 21 | @Override 22 | public String getName() { 23 | return "Service1"; 24 | } 25 | } 26 | class Service2 implements Service { 27 | public void execute(){ 28 | System.out.println("Executing Service2"); 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | return "Service2"; 34 | } 35 | } 36 | 37 | class InitialContext { 38 | public Object lookup(String jndiName){ 39 | if(jndiName.equalsIgnoreCase("SERVICE1")){ 40 | System.out.println("Looking up and creating a new Service1 object"); 41 | return new Service1(); 42 | }else if (jndiName.equalsIgnoreCase("SERVICE2")){ 43 | System.out.println("Looking up and creating a new Service2 object"); 44 | return new Service2(); 45 | } 46 | return null; 47 | } 48 | } 49 | 50 | 51 | class Cache { 52 | 53 | private List services; 54 | 55 | public Cache(){ 56 | services = new ArrayList(); 57 | } 58 | 59 | public Service getService(String serviceName){ 60 | for (Service service : services) { 61 | if(service.getName().equalsIgnoreCase(serviceName)){ 62 | System.out.println("Returning cached "+serviceName+" object"); 63 | return service; 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | public void addService(Service newService){ 70 | boolean exists = false; 71 | for (Service service : services) { 72 | if(service.getName().equalsIgnoreCase(newService.getName())){ 73 | exists = true; 74 | } 75 | } 76 | if(!exists){ 77 | services.add(newService); 78 | } 79 | } 80 | } 81 | 82 | class ServiceLocator { 83 | private static Cache cache; 84 | 85 | static { 86 | cache = new Cache(); 87 | } 88 | 89 | public static Service getService(String jndiName){ 90 | 91 | Service service = cache.getService(jndiName); 92 | 93 | if(service != null){ 94 | return service; 95 | } 96 | 97 | InitialContext context = new InitialContext(); 98 | Service service1 = (Service)context.lookup(jndiName); 99 | cache.addService(service1); 100 | return service1; 101 | } 102 | } 103 | 104 | public class J2eeServiceLocatorPatternDemo { 105 | public static void main(String[] args) { 106 | Service service = ServiceLocator.getService("Service1"); 107 | service.execute(); 108 | service = ServiceLocator.getService("Service2"); 109 | service.execute(); 110 | service = ServiceLocator.getService("Service1"); 111 | service.execute(); 112 | service = ServiceLocator.getService("Service2"); 113 | service.execute(); 114 | } 115 | } 116 | 117 | //j2ee 服务定位器模式 118 | /* 119 | Looking up and creating a new Service1 object 120 | Executing Service1 121 | Looking up and creating a new Service2 object 122 | Executing Service2 123 | Returning cached Service1 object 124 | Executing Service1 125 | Returning cached Service2 object 126 | Executing Service2 127 | */ -------------------------------------------------------------------------------- /J2eeTransferObjectPatternDemo.java: -------------------------------------------------------------------------------- 1 | /* 2 | 传输对象模式(Transfer Object Pattern)用于从客户端向服务器一次性传递带有多个属性的数据。传输对象也被称为数值对象。传输对象是一个具有 getter/setter 方法的简单的 POJO 类,它是可序列化的,所以它可以通过网络传输。它没有任何的行为。服务器端的业务类通常从数据库读取数据,然后填充 POJO,并把它发送到客户端或按值传递它。对于客户端,传输对象是只读的。客户端可以创建自己的传输对象,并把它传递给服务器,以便一次性更新数据库中的数值。以下是这种设计模式的实体。 3 | 4 | 业务对象(Business Object) - 为传输对象填充数据的业务服务。 5 | 传输对象(Transfer Object) - 简单的 POJO,只有设置/获取属性的方法。 6 | 客户端(Client) - 客户端可以发送请求或者发送传输对象到业务对象。 7 | */ 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | class StudentVO { 13 | private String name; 14 | private int rollNo; 15 | 16 | StudentVO(String name, int rollNo){ 17 | this.name = name; 18 | this.rollNo = rollNo; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public int getRollNo() { 30 | return rollNo; 31 | } 32 | 33 | public void setRollNo(int rollNo) { 34 | this.rollNo = rollNo; 35 | } 36 | } 37 | 38 | 39 | class StudentBO { 40 | 41 | //列表是当作一个数据库 42 | List students; 43 | 44 | public StudentBO(){ 45 | students = new ArrayList(); 46 | StudentVO student1 = new StudentVO("Robert",0); 47 | StudentVO student2 = new StudentVO("John",1); 48 | students.add(student1); 49 | students.add(student2); 50 | } 51 | public void deleteStudent(StudentVO student) { 52 | students.remove(student.getRollNo()); 53 | System.out.println("Student: Roll No " 54 | + student.getRollNo() +", deleted from database"); 55 | } 56 | 57 | //从数据库中检索学生名单 58 | public List getAllStudents() { 59 | return students; 60 | } 61 | 62 | public StudentVO getStudent(int rollNo) { 63 | return students.get(rollNo); 64 | } 65 | 66 | public void updateStudent(StudentVO student) { 67 | students.get(student.getRollNo()).setName(student.getName()); 68 | System.out.println("Student: Roll No " 69 | + student.getRollNo() +", updated in the database"); 70 | } 71 | } 72 | 73 | public class J2eeTransferObjectPatternDemo { 74 | public static void main(String[] args) { 75 | StudentBO studentBusinessObject = new StudentBO(); 76 | 77 | //输出所有的学生 78 | for (StudentVO student : studentBusinessObject.getAllStudents()) { 79 | System.out.println("Student: [RollNo : " 80 | + student.getRollNo()+", Name : "+student.getName()+" ]"); 81 | } 82 | 83 | //更新学生 84 | StudentVO student =studentBusinessObject.getAllStudents().get(0); 85 | student.setName("Michael"); 86 | studentBusinessObject.updateStudent(student); 87 | 88 | //获取学生 89 | studentBusinessObject.getStudent(0); 90 | System.out.println("Student: [RollNo : " 91 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 92 | } 93 | } 94 | 95 | //j2ee 对象传输模式 96 | /* 97 | Student: [RollNo : 0, Name : Robert ] 98 | Student: [RollNo : 1, Name : John ] 99 | Student: Roll No 0, updated in the database 100 | Student: [RollNo : 0, Name : Michael ] 101 | */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 编程时候,设计新的模块或者项目,下不了笔,要思考三天三夜怎么设计,主要是没学设计模式。 3 | 4 | 编程时候,有时候脑子一瞬间感觉 灵光出现,觉得这么写台好了太合适了,这样写节约代码 扩展高,瞬间感到这是个天才想法, 5 | 主要是由于没有专门学习设计模式,只能偶尔灵机一动,形成不了一套稳定的输出,所以才会有这种突然感觉某个代码设计是天才设计这种感受。 6 | ``` 7 | 8 | ``` 9 | 不要纠结是23种还是36种了,有细分的,举个例子 观察者模式和发布订阅模式,是有了少许变化,多了解一种总没啥坏处。 10 | 为什么是36种,因为是摘自菜鸟教程或者w3cschool网站,这两个网站都是36种,包含了j2ee模式,多了解没坏处。 11 | 不愿意学oop和设计模式,天天加班写bug解bug无限复制粘贴扣字老代码写新代码,这种写法瞎忙也没用。 12 | 比如韩信3万军队歼灭70万大军,靠的不是大无畏的胡乱冲刺,而是因为韩信看了孙子兵法36计兵书,靠的是策略,所以我要称之为36种设计模式。 13 | 14 | 设计模式十分有用,是通过大量实践对比得出来的,比如和自己国王写代码的纵向对比总结,和公司n多个现有代码项目的横向对比总结得出来的结论。 15 | 16 | 使用设计模式或oop转化公式,能使几乎任意项目代码减少50%至90%行,编码速度快一倍。 17 | 扩展性和维护性提高数十倍,bug减少5倍。 18 | 如果是使用极端纯面向过程(或者加入了少量无效废物类)的方式来写python代码, 19 | 我不用看,就知道一定很low。 20 | ``` 21 | 22 | 23 | ## 一些介绍 24 | 学弟啊,你代码能不能不要写得这么烂 25 | https://www.toutiao.com/a6739759206997426696/ 26 | 27 | 面向对象和面向过程分别是什么? 28 | https://www.zhihu.com/question/28790424 29 | 30 | 如何通俗易懂地举例说明「面向对象」和「面向过程」有什么区别? 31 | https://www.zhihu.com/question/27468564 32 | 33 | 34 | 对于程序员来说,设计模式和算法哪个更重要呢? 35 | https://www.zhihu.com/question/25432487 36 | 37 | 为什么我们需要学习(设计)模式 38 | https://zhuanlan.zhihu.com/p/19835717 39 | 40 | 41 | ## 介绍神级别通用固定oop转化公式,写代码下笔时候行云流水。 42 | 43 | 使用这个公式,不需要背设计模式,不需要每次写新文件先花几天想破头皮怎么设计布局代码成为高扩展和高维护。 44 | 45 | ## 常见问题回答 46 | 47 | 1、老是纠结类和函数?类和函数有什么区别。 48 | 49 | ``` 50 | 问函数和类的区别,这简直是牛头不对马嘴的伪问题。 51 | 函数和方法可以比区别。函数和类比区别就是问 人和上班 、猪和吃东西 有什么区别一样,简直是没搞清楚比较维度。 52 | 53 | 类和模块可以比,类主要优势是多实例,多个实例的属性是互不干扰的,每个实例的多个方法都可以直接访问实例属性(成员变量)。 54 | 面向过程是一直很low的在函数间把本应该在oop中声明为成员变量的东西弄成在函数间反复传参和return。 55 | 56 | 类 + 方法 + 实例属性(成员变量) 约等于 模块 + 函数 + 全局变量 57 | 1)对于类,类的无数个实例化对象的实例属性(成员变量)是互不干扰的,天然的多实例。 58 | 2)对于模块 python的模块在当前解释器中是唯一的,import 一万次 xx.py,xx模块都是唯一的指向同一个地方, 59 | 所以使用全局变量 加 函数逇设计方式是行不通的,xx模块是唯一的,xx模块的a 变量 b变量也是惟一的, 60 | 所以老是需要设计成吧a和b弄成传参和return而不是全局变量来解决全局变量只有一份的问题。 61 | 62 | 所以如果需要多实例,类 + 方法 + 实例属性(成员变量) 远好于 模块 + 函数 + 全局变量。 63 | 模块 + 函数 + 全局变量 由于要模拟多实例,使全局变量(状态) 不唯一,大部分情况下需要写成 函数反复传参和反复return。 64 | 这种面向过程的封装的写法曲折程度毫无疑问被oop暴击了,面向过程导致写代码慢想破头皮搞一堆传参和return。何况oop不只有封装还有继承和多态。 65 | 66 | 能写类的人100%就能写函数,能写函数的人不一定能写类(说的是真oop的类,排除无效废物面向过程滑稽类),这是不可逆的。 67 | ``` 68 | 69 | 2、 不学设计模式的坏处 70 | 71 | ``` 72 | 只爱学python语法,只学怎么使用if else break, 学字典 列表怎么用,代码就很low 73 | 写代码很慢,设计慢,想的慢,打字慢 74 | 代码不可维护,没有高扩展性 75 | 写新文件要想破头皮三天三夜才能开始下笔。 76 | ``` 77 | 78 | ## !【重要】 简单例子说明极端面向过程编程非常愚蠢low 79 | 80 | 情不自禁纯粹极端面向过程编程还是c语言中毒了,从来都不使用面向对象,不用提设计模式。 81 | 82 | 有些人完全没思考过面向对象,一味的只会说大型项目才需要面向对象,小项目面向过程更合适。简直是胡说八道,不看场景只看项目大小。 83 | 84 | 比如一个简单需求,描写人,吃饭 增加体重和身高,拉尿体重降低。需求是非常非常的简单了吧,绝对是非常小的项目,单个文件就好了,来验证下是不是小项目面向过程最好。 85 | 86 | 面向过程来实现,看看代码实现多垃圾,调用时候多么麻烦。 87 | ```python 88 | # 吃饭函数 89 | def eat(name, height, weight, food_weight): 90 | """吃饭:增加体重和身高""" 91 | new_weight = weight + food_weight 92 | new_height = height + food_weight * 0.01 93 | print(f"{name} 吃了 {food_weight} 千克食物,体重: {new_weight} 千克,身高: {new_height} 厘米") 94 | return new_height, new_weight 95 | 96 | # 拉尿函数 97 | def pee(name, height, weight, pee_weight): 98 | """拉尿:减少体重""" 99 | if pee_weight > weight: 100 | pee_weight = weight 101 | new_weight = weight - pee_weight 102 | print(f"{name} 拉了 {pee_weight} 千克尿,体重: {new_weight} 千克,身高: {height} 厘米") 103 | return height, new_weight 104 | 105 | # 测试代码 106 | if __name__ == "__main__": 107 | # 小明的初始状态 108 | xiaoming_height = 170 109 | xiaoming_weight = 60 110 | 111 | # 小红的初始状态 112 | xiaohong_height = 160 113 | xiaohong_weight = 50 114 | 115 | print(f"小明 初始状态 - 身高: {xiaoming_height} 厘米,体重: {xiaoming_weight} 千克") 116 | print(f"小红 初始状态 - 身高: {xiaohong_height} 厘米,体重: {xiaohong_weight} 千克") 117 | 118 | # 小明吃饭和拉尿 119 | xiaoming_height, xiaoming_weight = eat("小明", xiaoming_height, xiaoming_weight, 2) 120 | xiaoming_height, xiaoming_weight = pee("小明", xiaoming_height, xiaoming_weight, 1) 121 | 122 | # 小红吃饭和拉尿 123 | xiaohong_height, xiaohong_weight = eat("小红", xiaohong_height, xiaohong_weight, 3) 124 | xiaohong_height, xiaohong_weight = pee("小红", xiaohong_height, xiaohong_weight, 2) 125 | ``` 126 | 127 | 面向对象来实现,看看代码实现多简单调用多方便 128 | ```python 129 | class Person: 130 | def __init__(self, name, height, weight): 131 | self.name = name 132 | self.height = height # 身高,单位:厘米 133 | self.weight = weight # 体重,单位:千克 134 | 135 | def eat(self, food_weight): 136 | """吃饭:增加体重和身高""" 137 | self.weight += food_weight 138 | self.height += food_weight * 0.01 139 | print(f"{self.name} 吃了 {food_weight} 千克食物,体重: {self.weight} 千克,身高: {self.height} 厘米") 140 | 141 | def pee(self, pee_weight): 142 | """拉尿:减少体重""" 143 | if pee_weight > self.weight: 144 | pee_weight = self.weight 145 | self.weight -= pee_weight 146 | print(f"{self.name} 拉了 {pee_weight} 千克尿,体重: {self.weight} 千克,身高: {self.height} 厘米") 147 | 148 | # 测试代码 149 | if __name__ == "__main__": 150 | # 创建小明和小红 151 | xiaoming = Person("小明", 170, 60) 152 | xiaohong = Person("小红", 160, 50) 153 | 154 | # 小明吃饭和拉尿 155 | xiaoming.eat(2) 156 | xiaoming.pee(1) 157 | 158 | # 小红吃饭和拉尿 159 | xiaohong.eat(3) 160 | xiaohong.pee(2) 161 | ``` 162 | 163 | ``` 164 | 面向对象起码有封装,极端面向过程那就实现时候,每个函数结尾需要疯狂的return 一大堆变量,然后将一大堆变量传给另外一个函数进行处理。 165 | 而面向对象不需要你把一大堆入参疯狂在各个函数传来传去,return来return去。 166 | 167 | 168 | 如果你给我说,定义一个结构体或者字典来存放 身高 体重 姓名,然后充分利用字典是可变类型,这样既减少了每个函数的入参数量, 169 | 又不需要return一大堆变量,还是可以用面向过程来写这个代码, 170 | 就避免了上面我说的疯狂传参和疯狂return的弊端,那么继承和多态阁下有如何应对呢? 171 | 172 | 比如大人吃1kg长0.1kg体重,小孩吃1kg长0.2kg体重,并且我假设eat函数调用了另外一个_eat2的私有函数来具体的执行体重增加计算, 173 | 你在面向过程时候,就要加一个 大人eat的函数 加一个 大人_eat2 的函数, 174 | 并且大人eat的函数里面调用大人_eat2的函数,在小孩eat的函数里面调用小孩_eat2的函数, 整个链路的就都要替换, 175 | 而面向对象仅仅需要增加一个大人的类,继承重写 _eat2这个私有方法就好了。 176 | 你想下纯粹极端面向过程实现公共代码时候有多low多复杂,并且调用它的时候有多麻烦。 177 | 178 | 如果你的思维是c语言中毒了,情不自禁极端面向过程编程,连入门的面向对象封装概念都没有,更别提更高阶的设计模式了。 179 | 180 | ``` 181 | 182 | #### 只需要按文档的面向过程转 oop 4步走里面的固定公式,全局变量->实例属性,函数->方法 降维转化,就可以设计出强大的代码,无需死记硬背23种设计模式。 -------------------------------------------------------------------------------- /j2ee模式-业务代表模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 15:32 4 | 5 | """ 6 | 业务代表模式(Business Delegate Pattern)用于对表示层和业务层解耦。它基本上是用来减少通信或对表示层代码中的业务层代码的远程查询功能。在业务层中我们有以下实体。 7 | 8 | 客户端(Client) - 表示层代码可以是 JSP、servlet 或 UI java 代码。 9 | 业务代表(Business Delegate) - 一个为客户端实体提供的入口类,它提供了对业务服务方法的访问。 10 | 查询服务(LookUp Service) - 查找服务对象负责获取相关的业务实现,并提供业务对象对业务代表对象的访问。 11 | 业务服务(Business Service) - 业务服务接口。实现了该业务服务的实体类,提供了实际的业务实现逻辑。 12 | 13 | 从java转化来,命名规范懒得改了。 14 | """ 15 | from abc import ABCMeta, abstractmethod 16 | from monkey_print2 import print 17 | 18 | 19 | class AbstractBusinessService(metaclass=ABCMeta): 20 | @abstractmethod 21 | def doProcessing(self): 22 | pass 23 | 24 | 25 | class EJBService(AbstractBusinessService): 26 | def doProcessing(self): 27 | print("Processing task by invoking EJB Service") 28 | 29 | 30 | class JMSService(AbstractBusinessService): 31 | def doProcessing(self): 32 | print("Processing task by invoking JMS Service") 33 | 34 | 35 | def getBusinessService(serviceType: str): 36 | if serviceType.upper() == 'EJB': 37 | return EJBService() 38 | else: 39 | return JMSService() 40 | 41 | 42 | class BusinessDelegate: 43 | def __init__(self): 44 | self.__businessService = None 45 | self.__serviceType = None 46 | 47 | def setServiceType(self, serviceType): 48 | self.__serviceType = serviceType 49 | 50 | def doTask(self): 51 | businessService = getBusinessService(self.__serviceType) 52 | businessService.doProcessing() 53 | 54 | 55 | class Client: 56 | def __init__(self, businessService: BusinessDelegate): 57 | self.businessService = businessService 58 | 59 | def doTask(self): 60 | self.businessService.doTask() 61 | 62 | 63 | if __name__ == '__main__': 64 | businessDelegate = BusinessDelegate() 65 | businessDelegate.setServiceType("EJB") 66 | 67 | client = Client(businessDelegate) 68 | client.doTask() 69 | 70 | businessDelegate.setServiceType("JMS") 71 | client.doTask() 72 | 73 | """ 74 | "D:/coding2/python36patterns/j2ee模式-业务代表模式.py:25" 16:07:31 Processing task by invoking EJB Service 75 | "D:/coding2/python36patterns/j2ee模式-业务代表模式.py:31" 16:07:31 Processing task by invoking JMS Service 76 | """ 77 | -------------------------------------------------------------------------------- /j2ee模式-前端控制器模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 15:32 4 | 5 | """ 6 | 前端控制器模式(Front Controller Pattern)是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。 7 | 8 | 前端控制器(Front Controller) - 处理应用程序所有类型请求的单个处理程序,应用程序可以是基于 web 的应用程序,也可以是基于桌面的应用程序。 9 | 调度器(Dispatcher) - 前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。 10 | 视图(View) - 视图是为请求而创建的对象。 11 | 12 | 从java转化来,命名规范懒得改了。 13 | """ 14 | from abc import ABCMeta, abstractmethod 15 | from monkey_print2 import print 16 | 17 | 18 | class HomeView: 19 | def show(self): 20 | print('显示 Home 页面') 21 | 22 | 23 | class StudentView: 24 | def show(self): 25 | print('显示 Student 页面') 26 | 27 | 28 | class Dispatcher: 29 | def __init__(self): 30 | self.student_view = StudentView() 31 | self.home_view = HomeView() 32 | 33 | def dispatch(self, request: str): 34 | if request.upper() == 'STUDENT': 35 | self.student_view.show() 36 | else: 37 | self.home_view.show() 38 | 39 | 40 | class FrontController: 41 | def __init__(self): 42 | self.__dispatcher = Dispatcher() 43 | 44 | def is_authentic_user(self): 45 | print("用户鉴权成功") 46 | return True 47 | 48 | def track_request(self, request): 49 | print("被请求页面: " + request) 50 | 51 | def dispatch_request(self, request): 52 | self.track_request(request) 53 | if self.is_authentic_user(): 54 | self.__dispatcher.dispatch(request) 55 | 56 | 57 | if __name__ == '__main__': 58 | front_controller = FrontController() 59 | front_controller.dispatch_request("HOME") 60 | front_controller.dispatch_request("STUDENT") 61 | 62 | """ 63 | "D:/coding2/python36patterns/j2ee模式-前端控制器模式.py:49" 16:54:03 被请求页面: HOME 64 | "D:/coding2/python36patterns/j2ee模式-前端控制器模式.py:45" 16:54:03 用户鉴权成功 65 | "D:/coding2/python36patterns/j2ee模式-前端控制器模式.py:20" 16:54:03 显示 Home 页面 66 | "D:/coding2/python36patterns/j2ee模式-前端控制器模式.py:49" 16:54:03 被请求页面: STUDENT 67 | "D:/coding2/python36patterns/j2ee模式-前端控制器模式.py:45" 16:54:03 用户鉴权成功 68 | "D:/coding2/python36patterns/j2ee模式-前端控制器模式.py:25" 16:54:03 显示 Student 页面 69 | 70 | """ 71 | -------------------------------------------------------------------------------- /j2ee模式-数据访问对象模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 15:32 4 | 5 | """ 6 | 数据访问对象模式(Data Access Object Pattern)或 DAO 模式用于把低级的数据访问 API 或操作从高级的业务服务中分离出来。以下是数据访问对象模式的参与者。 7 | 8 | 数据访问对象接口(Data Access Object Interface) - 该接口定义了在一个模型对象上要执行的标准操作。 9 | 数据访问对象实体类(Data Access Object concrete class) - 该类实现了上述的接口。该类负责从数据源获取数据,数据源可以是数据库,也可以是 xml,或者是其他的存储机制。 10 | 模型对象/数值对象(Model Object/Value Object) - 该对象是简单的 POJO,包含了 get/set 方法来存储通过使用 DAO 类检索到的数据。 11 | """ 12 | from abc import ABCMeta, abstractmethod 13 | from monkey_print2 import print 14 | 15 | 16 | class Student: 17 | def __init__(self, name, roll_no): 18 | self.__name = name 19 | self.__roll_no = roll_no 20 | 21 | def get_name(self): 22 | return self.__name 23 | 24 | def set_name(self, name): 25 | self.__name = name 26 | 27 | def get_roll_no(self): 28 | return self.__roll_no 29 | 30 | def set_roll_no(self, roll_no): 31 | self.__roll_no = roll_no 32 | 33 | 34 | class AbstractStudentDao(metaclass=ABCMeta): 35 | @abstractmethod 36 | def get_all_students(self): 37 | pass 38 | 39 | def get_student(self, roll_num): 40 | pass 41 | 42 | def update_student(self, student: Student): 43 | pass 44 | 45 | def delete_student(self, student: Student): 46 | pass 47 | 48 | 49 | class StudentDao(AbstractStudentDao): 50 | def __init__(self): 51 | self.students = [] 52 | student1 = Student("小明", 0) 53 | student2 = Student("小黄", 1) 54 | self.students.append(student1) 55 | self.students.append(student2) 56 | 57 | def get_all_students(self): 58 | return self.students 59 | 60 | def get_student(self, roll_num): 61 | return self.students[roll_num] 62 | 63 | def update_student(self, student: Student): 64 | # print(student.get_roll_no()) 65 | # print(student.get_name()) 66 | self.students[student.get_roll_no()].set_name(student.get_name()) 67 | print("Student: RollNo " + str(student.get_roll_no()) 68 | + ", updated in the database") 69 | 70 | def delete_student(self, student: Student): 71 | self.students.pop(student.get_roll_no()) 72 | print("Student: Roll No " + str(student.get_roll_no()) 73 | + ", deleted from database") 74 | 75 | 76 | if __name__ == '__main__': 77 | 78 | student_dao = StudentDao() 79 | 80 | # // 输出所有的学生 81 | for student in student_dao.get_all_students(): 82 | print("Student: [RollNo : " + str(student.get_roll_no()) + ", Name : " + student.get_name() + " ]") 83 | 84 | # // 更新学生 85 | student = student_dao.get_all_students()[0] 86 | student.set_name("Michael") 87 | student_dao.update_student(student) 88 | 89 | # // 获取学生 90 | student_dao.get_student(0) 91 | print("Student: [RollNo : " 92 | + str(student.get_roll_no()) + ", Name : " + student.get_name() + " ]") 93 | 94 | """ 95 | "D:/coding2/python36patterns/j2ee模式-数据访问对象模式.py:84" 17:38:06 Student: [RollNo : 0, Name : 小明 ] 96 | "D:/coding2/python36patterns/j2ee模式-数据访问对象模式.py:84" 17:38:06 Student: [RollNo : 1, Name : 小黄 ] 97 | "D:/coding2/python36patterns/j2ee模式-数据访问对象模式.py:70" 17:38:06 Student: RollNo 0, updated in the database 98 | "D:/coding2/python36patterns/j2ee模式-数据访问对象模式.py:94" 17:38:06 Student: [RollNo : 0, Name : Michael ] 99 | """ 100 | -------------------------------------------------------------------------------- /j2ee模式-组合实体模式.py: -------------------------------------------------------------------------------- 1 | """ 2 | 组合实体模式(Composite Entity Pattern)用在 EJB 持久化机制中。一个组合实体是一个 EJB 实体 bean,代表了对象的图解。当更新一个组合实体时,内部依赖对象 beans 会自动更新,因为它们是由 EJB 实体 bean 管理的。以下是组合实体 bean 的参与者。 3 | 4 | 组合实体(Composite Entity) - 它是主要的实体 bean。它可以是粗粒的,或者可以包含一个粗粒度对象,用于持续生命周期。 5 | 粗粒度对象(Coarse-Grained Object) - 该对象包含依赖对象。它有自己的生命周期,也能管理依赖对象的生命周期。 6 | 依赖对象(Dependent Object) - 依赖对象是一个持续生命周期依赖于粗粒度对象的对象。 7 | 策略(Strategies) - 策略表示如何实现组合实体。 8 | """ 9 | from monkey_print2 import print 10 | 11 | 12 | class DependentObject1: 13 | def __init__(self): 14 | self.__data = None 15 | 16 | def get_data(self): 17 | return self.__data 18 | 19 | def set_data(self, data): 20 | self.__data = data 21 | 22 | 23 | class DependentObject2: 24 | def __init__(self): 25 | self.__data = None 26 | 27 | def get_data(self): 28 | return self.__data 29 | 30 | def set_data(self, data): 31 | self.__data = data 32 | 33 | 34 | class CoarseGrainedObject: 35 | def __init__(self): 36 | self.do1 = DependentObject1() 37 | self.do2 = DependentObject2() 38 | 39 | def set_data(self, data1, data2): 40 | self.do1.set_data(data1) 41 | self.do2.set_data(data2) 42 | 43 | def get_data(self): 44 | return self.do1.get_data(), self.do2.get_data() 45 | 46 | 47 | class CompositeEntity: 48 | def __init__(self): 49 | self.cgo = CoarseGrainedObject() 50 | 51 | def set_data(self, data1, data2): 52 | self.cgo.set_data(data1, data2) 53 | 54 | def get_data(self): 55 | return self.cgo.get_data() 56 | 57 | 58 | class Client: 59 | def __init__(self): 60 | self.compositeEntity = CompositeEntity() 61 | 62 | def print_data(self): 63 | for data in self.compositeEntity.get_data(): 64 | print(f'Data: {data}') 65 | 66 | def set_data(self, data1, data2): 67 | self.compositeEntity.set_data(data1, data2) 68 | 69 | 70 | if __name__ == '__main__': 71 | client = Client() 72 | client.set_data("Test", "Data") 73 | client.print_data() 74 | 75 | client.set_data("Second Test", "Data2") 76 | client.print_data() 77 | 78 | """ 79 | "D:/coding2/python36patterns/j2ee模式-组合实体模式.py:64" 16:31:51 Data: Test 80 | "D:/coding2/python36patterns/j2ee模式-组合实体模式.py:64" 16:31:51 Data: Data 81 | "D:/coding2/python36patterns/j2ee模式-组合实体模式.py:64" 16:31:51 Data: Second Test 82 | "D:/coding2/python36patterns/j2ee模式-组合实体模式.py:64" 16:31:51 Data: Data2 83 | """ 84 | -------------------------------------------------------------------------------- /monkey_patch_pattern/README.md: -------------------------------------------------------------------------------- 1 | ## 猴子补丁模式,要演示多个文件,所以建了个文件夹。演示猴子补丁,正确的使用猴子补丁模式。 2 | ``` 3 | 猴子补丁是python特有设计模式。是一种非常强悍,非常有趣的设计模式,能使用很少的修改达到全局运行 修改。 4 | 5 | 80%的py人员把猴子补丁和gevent库居然划等号。这样想大错特错。 6 | gevent库是使用猴子补丁设计模式之一的库,比较复杂。 7 | 任何人都有权利去使用甚至创造猴子补丁,这不是gevent库的专利, 8 | 使用猴子补丁和安不安装gevent 、导不导入gevent没有半毛钱关系。 9 | 10 | 猴子补丁要 11 | 深刻理解模块天然是单例的原理 12 | 深刻理解 from a import fun ;fun() 和 import a ; a.fun()的区别。否则猴子补丁失效或部分地方失效。 13 | 要深刻理解 猴子补丁为什么最好要在运行的模块最顶行打的本质原因,不这样做可能会发生什么后果。 14 | 这里面没有什么玄学,就是python语法。 15 | 16 | 利用a.py b.py run.py,然后你需要把import 改成from import 测试对比, 17 | 以及把run里面的import b放在打猴子补丁之前和打猴子补丁之后对比运行结果。 18 | ``` 19 | 20 | 21 | -------------------------------------------------------------------------------- /monkey_patch_pattern/a.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 12:12 4 | 5 | def fun(): 6 | print('原始的 print hello') -------------------------------------------------------------------------------- /monkey_patch_pattern/b.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 12:12 4 | from monkey_patch_pattern.a import fun 5 | 6 | def funbbb(): 7 | print('funbbbb中调用fun') 8 | fun() -------------------------------------------------------------------------------- /monkey_patch_pattern/run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 12:12 4 | """重要程度 ☆☆☆☆ 5 | 利用模块只会导入一次,模块天然单例的特性,使用猴子补丁。一处修改 处处生效。 6 | 猴子补丁是python动态语言的重要设计模式,能用很少代码,大幅度改变很多地方的运行行为。 7 | 对多人合作的大项目,如果猴子补丁直接在__init__.py里面执行了,并且猴子补丁变化有很大影响,则需要告诉小伙伴,免得出现莫名其妙的疑惑。 8 | """ 9 | from monkey_patch_pattern import a 10 | 11 | 12 | from monkey_patch_pattern.b import funbbb # TODO 请注释掉该行,测试在一开头导入funbbb和在打了猴子补丁之后再导入funbbb 13 | 14 | def modify_fun(): 15 | print('修改后的 print world') 16 | 17 | 18 | a.fun = modify_fun 19 | 20 | a.fun() 21 | 22 | print('- - - - - ') 23 | # from monkey_patch_pattern.b import funbbb # TODO 请注释掉该行,请测试在一开头导入funbbb和在打了猴子补丁之后再导入funbbb 24 | 25 | funbbb() 26 | 27 | """ 28 | 如果在第19行,即打了猴子补丁后再导入funbbb,结果是: 29 | 修改后的 print world 30 | - - - - - 31 | funbbbb中调用fun 32 | 修改后的 print world 33 | 34 | 35 | 36 | 如果在开头行,即在打猴子补丁之前导入funbbb,结果是: 37 | 修改后的 print world 38 | - - - - - 39 | funbbbb中调用fun 40 | 原始的 print hello 41 | 42 | """ -------------------------------------------------------------------------------- /monkey_patch_pattern/真实的适用猴子补丁例子/monkey_color_log.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/8/1 0001 17:54 4 | """ 5 | 如果老项目没用使用Logmanager,可以打此猴子补丁,自动使项目中的任何日志变彩色和可跳转。 6 | 7 | """ 8 | 9 | import sys 10 | import os 11 | import logging 12 | 13 | 14 | class ColorHandler(logging.Handler): 15 | """ 16 | A handler class which writes logging records, appropriately formatted, 17 | to a stream. Note that this class does not close the stream, as 18 | sys.stdout or sys.stderr may be used. 19 | """ 20 | os_name = os.name 21 | terminator = '\n' 22 | bule = 96 if os_name == 'nt' else 36 23 | yellow = 93 if os_name == 'nt' else 33 24 | 25 | def __init__(self, stream=None, ): 26 | """ 27 | Initialize the handler. 28 | 29 | If stream is not specified, sys.stderr is used. 30 | """ 31 | logging.Handler.__init__(self) 32 | self.formatter = logging.Formatter( 33 | '%(asctime)s - %(name)s - "%(pathname)s:%(lineno)d" - %(funcName)s - %(levelname)s - %(message)s', 34 | "%Y-%m-%d %H:%M:%S") 35 | if stream is None: 36 | stream = sys.stdout # stderr无彩。 37 | self.stream = stream 38 | self._display_method = 7 if self.os_name == 'posix' else 0 39 | 40 | def setFormatter(self, fmt): 41 | pass # 使私自设置日志模板失效。固定使用可跳转的模板。 42 | 43 | def flush(self): 44 | """ 45 | Flushes the stream. 46 | """ 47 | self.acquire() 48 | try: 49 | if self.stream and hasattr(self.stream, "flush"): 50 | self.stream.flush() 51 | finally: 52 | self.release() 53 | 54 | def emit0(self, record): 55 | """ 56 | 前后彩色不分离的方式 57 | Emit a record. 58 | 59 | If a formatter is specified, it is used to format the record. 60 | The record is then written to the stream with a trailing newline. If 61 | exception information is present, it is formatted using 62 | traceback.print_exception and appended to the stream. If the stream 63 | has an 'encoding' attribute, it is used to determine how to do the 64 | output to the stream. 65 | """ 66 | # noinspection PyBroadException 67 | try: 68 | msg = self.format(record) 69 | stream = self.stream 70 | if record.levelno == 10: 71 | # msg_color = ('\033[0;32m%s\033[0m' % msg) # 绿色 72 | msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, 32, msg)) # 绿色 73 | elif record.levelno == 20: 74 | msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.bule, msg)) # 青蓝色 36 96 75 | elif record.levelno == 30: 76 | msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.yellow, msg)) 77 | elif record.levelno == 40: 78 | msg_color = ('\033[%s;35m%s\033[0m' % (self._display_method, msg)) # 紫红色 79 | elif record.levelno == 50: 80 | msg_color = ('\033[%s;31m%s\033[0m' % (self._display_method, msg)) # 血红色 81 | else: 82 | msg_color = msg 83 | # print(msg_color,'***************') 84 | stream.write(msg_color) 85 | stream.write(self.terminator) 86 | self.flush() 87 | except Exception: 88 | self.handleError(record) 89 | 90 | def emit(self, record): 91 | """ 92 | 前后彩色分离的方式。 93 | Emit a record. 94 | 95 | If a formatter is specified, it is used to format the record. 96 | The record is then written to the stream with a trailing newline. If 97 | exception information is present, it is formatted using 98 | traceback.print_exception and appended to the stream. If the stream 99 | has an 'encoding' attribute, it is used to determine how to do the 100 | output to the stream. 101 | """ 102 | # noinspection PyBroadException 103 | try: 104 | msg = self.format(record) 105 | stream = self.stream 106 | msg1, msg2 = self.__spilt_msg(record.levelno, msg) 107 | if record.levelno == 10: 108 | # msg_color = ('\033[0;32m%s\033[0m' % msg) # 绿色 109 | msg_color = f'\033[0;32m{msg1}\033[0m \033[7;32m{msg2}\033[0m' # 绿色 110 | elif record.levelno == 20: 111 | # msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.bule, msg)) # 青蓝色 36 96 112 | msg_color = f'\033[0;{self.bule}m{msg1}\033[0m \033[7;{self.bule}m{msg2}\033[0m' 113 | elif record.levelno == 30: 114 | # msg_color = ('\033[%s;%sm%s\033[0m' % (self._display_method, self.yellow, msg)) 115 | msg_color = f'\033[0;{self.yellow}m{msg1}\033[0m \033[7;{self.yellow}m{msg2}\033[0m' 116 | elif record.levelno == 40: 117 | # msg_color = ('\033[%s;35m%s\033[0m' % (self._display_method, msg)) # 紫红色 118 | msg_color = f'\033[0;35m{msg1}\033[0m \033[7;35m{msg2}\033[0m' 119 | elif record.levelno == 50: 120 | # msg_color = ('\033[%s;31m%s\033[0m' % (self._display_method, msg)) # 血红色 121 | msg_color = f'\033[0;31m{msg1}\033[0m \033[7;31m{msg2}\033[0m' 122 | else: 123 | msg_color = msg 124 | # print(msg_color,'***************') 125 | stream.write(msg_color) 126 | stream.write(self.terminator) 127 | self.flush() 128 | except Exception: 129 | self.handleError(record) 130 | 131 | @staticmethod 132 | def __spilt_msg(log_level, msg: str): 133 | split_text = '- 级别 -' 134 | if log_level == 10: 135 | split_text = '- DEBUG -' 136 | elif log_level == 20: 137 | split_text = '- INFO -' 138 | elif log_level == 30: 139 | split_text = '- WARNING -' 140 | elif log_level == 40: 141 | split_text = '- ERROR -' 142 | elif log_level == 50: 143 | split_text = '- CRITICAL -' 144 | msg_split = msg.split(split_text, maxsplit=1) 145 | return msg_split[0] + split_text, msg_split[-1] 146 | 147 | def __repr__(self): 148 | level = logging.getLevelName(self.level) 149 | name = getattr(self.stream, 'name', '') 150 | if name: 151 | name += ' ' 152 | return '<%s %s(%s)>' % (self.__class__.__name__, name, level) 153 | 154 | def patch_stream_handler_instead_of_color_handler(): 155 | logging.StreamHandler = ColorHandler # REMIND 这一行就是打猴子补丁,可以尝试注释掉这一行对比。 156 | """ 157 | 这里就是打猴子补丁,要在脚本最开始打猴子补丁,越早越好。 158 | 否则原来脚本中使用from logging import StreamHandler变为不了彩色的handler。 159 | 只有import logging,logging.StreamHandler的这种用法才会变彩。所以猴子补丁要趁早打。 160 | """ 161 | 162 | if __name__ == '__main__': 163 | def my_func(): 164 | """ 165 | 模拟常规使用控制台StreamHandler日志的方式。自动变彩。 166 | :return: 167 | """ 168 | from logging import StreamHandler 169 | logger = logging.getLogger('abc') 170 | print(logger.handlers) 171 | print(StreamHandler().formatter) 172 | logger.addHandler(StreamHandler()) 173 | logger.setLevel(10) 174 | print(logger.handlers) 175 | for _ in range(100): 176 | logger.debug('一个debug级别的日志' * 5) 177 | logger.info('一个info级别的日志' * 5) 178 | logger.warning('一个warning级别的日志' * 5) 179 | logger.error('一个error级别的日志' * 5) 180 | logger.critical('一个critical级别的日志' * 5) 181 | 182 | patch_stream_handler_instead_of_color_handler() # 执行猴子补丁后,项目的任意控制台日志,自动变彩色和可跳转。 183 | my_func() 184 | -------------------------------------------------------------------------------- /monkey_patch_pattern/真实的适用猴子补丁例子/monkey_json.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/10 0010 10:18 4 | import json 5 | from datetime import datetime as _datetime 6 | from datetime import date as _date 7 | 8 | class _CustomEncoder(json.JSONEncoder): 9 | """自定义的json解析器,mongodb返回的字典中的时间格式是datatime,json直接解析出错""" 10 | 11 | def default(self, obj): 12 | if isinstance(obj, _datetime): 13 | return obj.strftime('%Y-%m-%d %H:%M:%S') 14 | elif isinstance(obj, _date): 15 | return obj.strftime('%Y-%m-%d') 16 | else: 17 | return json.JSONEncoder.default(self, obj) 18 | 19 | 20 | # noinspection PyProtectedMember,PyPep8,PyRedundantParentheses 21 | def _dumps(obj, skipkeys=False, ensure_ascii=False, check_circular=True, allow_nan=True, cls=_CustomEncoder, indent=None, separators=None, 22 | default=None, sort_keys=False, **kw): 23 | if (not skipkeys and ensure_ascii and check_circular and allow_nan and cls is None and indent is None and separators is None and default is None and not sort_keys and not kw): 24 | return json._default_encoder.encode(obj) 25 | return cls( 26 | skipkeys=skipkeys, ensure_ascii=ensure_ascii, 27 | check_circular=check_circular, allow_nan=allow_nan, indent=indent, 28 | separators=separators, default=default, sort_keys=sort_keys, ).encode(obj) 29 | 30 | 31 | def monkey_patch_json(): 32 | json.dumps = _dumps 33 | 34 | 35 | if __name__ == '__main__': 36 | dictx = {'a':1,'b':_datetime.now()} 37 | monkey_patch_json() # 不打猴子补丁时候,datetime是python自定义对象,不能被json序列化,程序会出错。 38 | print(json.dumps(dictx)) -------------------------------------------------------------------------------- /monkey_print2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/5/9 19:02 4 | """ 5 | 不直接给print打补丁,自己重新赋值。 6 | 7 | """ 8 | import sys 9 | import time 10 | 11 | 12 | # noinspection PyProtectedMember,PyUnusedLocal,PyIncorrectDocstring 13 | def nb_print(*args, sep=' ', end='\n', file=None): 14 | """ 15 | 超流弊的print补丁 16 | :param x: 17 | :return: 18 | """ 19 | # 获取被调用函数在被调用时所处代码行数 20 | line = sys._getframe().f_back.f_lineno 21 | # 获取被调用函数所在模块文件名 22 | file_name = sys._getframe(1).f_code.co_filename 23 | # sys.stdout.write(f'"{__file__}:{sys._getframe().f_lineno}" {x}\n') 24 | args = (str(arg) for arg in args) # REMIND 防止是数字不能被join 25 | sys.stdout.write(f'"{file_name}:{line}" {time.strftime("%H:%M:%S")} \033[0;94m{"".join(args)}\033[0m\n') # 36 93 96 94 26 | 27 | 28 | print = nb_print # 这是打猴子补丁。 29 | 30 | if __name__ == '__main__': 31 | print(0) 32 | nb_print(123, 'abc') 33 | print = nb_print 34 | print(456, 'def') 35 | -------------------------------------------------------------------------------- /tests/单例模式使用不当.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2020/1/13 0013 12:09 4 | """ 5 | 错误使用设计模式的例子 6 | 7 | """ 8 | from redis import Redis 9 | class MyRedis: 10 | _inst = None 11 | 12 | def __new__(cls, *args, **kwargs): 13 | if not cls._inst: 14 | self = super().__new__(cls) 15 | self.__my_init__(*args, **kwargs) 16 | cls._inst = self 17 | return cls._inst 18 | 19 | 20 | def __my_init__(self,redis_db): 21 | print(f'传入的redis db是 {redis_db}') 22 | self.r = Redis(host='127.0.0.1',port=6379,db=redis_db) 23 | 24 | def set(self,key,value): 25 | self.r.set(key,value) 26 | 27 | if __name__ == '__main__': 28 | """ 29 | 单例模式使用不当,造成项目巨大漏洞,同事说怎么没看到redis生成相关的,因为单例模式一直在db5,同事以为能放到db6,db7,应该使用享元模式,但错误的使用了单例模式。 30 | """ 31 | MyRedis(5).set('a',1) 32 | MyRedis(6).set('b', 2) 33 | MyRedis(5).set('c', 3) 34 | MyRedis(6).set('d', 4) 35 | 36 | -------------------------------------------------------------------------------- /创建型模式-享元模式-元类.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/11/19 0019 12:22 4 | from monkey_print2 import print 5 | 6 | 7 | class FlyweightMetaClass(type): 8 | def __init__(cls, name, bases, dict): 9 | super(FlyweightMetaClass, cls).__init__(name, bases, dict) 10 | cls._instance_map = {} 11 | 12 | @staticmethod 13 | def _make_arguments_to_key(args, kwds): 14 | key = args 15 | if kwds: 16 | sorted_items = sorted(kwds.items()) 17 | for item in sorted_items: 18 | key += item 19 | return key 20 | 21 | def __call__(cls, *args, **kw): 22 | cache_key = f'{cls}_{cls._make_arguments_to_key(args, kw)}' 23 | if cache_key not in cls._instance_map: 24 | cls._instance_map[cache_key] = super().__call__(*args, **kw) 25 | return cls._instance_map[cache_key] 26 | 27 | 28 | class A(metaclass=FlyweightMetaClass): 29 | def __init__(self, a, b): 30 | print(f'初始化 {a},{b}') 31 | 32 | 33 | if __name__ == '__main__': 34 | A(1, 2) 35 | A(1, 2) 36 | A(1, 3) 37 | """ 38 | "D:/coding2/python36patterns/创建型模式-享元模式-元类.py:28" 14:19:44 初始化 1,2 # 这句话只会运行一次,享元模式命中了缓存。 39 | "D:/coding2/python36patterns/创建型模式-享元模式-元类.py:28" 14:19:44 初始化 1,3 40 | """ 41 | -------------------------------------------------------------------------------- /创建型模式-享元模式-装饰器版本.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 享元模式 6 | 享元模式通过为相似对象引入数据共享来最小化内存使用,提升性能,一个享元就是一个包含状态的独立的不可变数据的共享对象,依赖状态的可变数据不应是享元的一部分,因为每个对象的这种信息不相同,无法共享,如果享元需要非固有数据应该由客户端代码显示提供。 7 | 8 | 享元模式介于单例模式和不加控制得多例模式之间。非常灵活,实用性和使用场景大于单例模式。 9 | 例如创建一个数据库连接,不希望建立多个连接,但又要在同一解释器下操作好多台机器的数据库,当传参的机器的ip端口不同时候,那肯定要创建一个新的连接了,这种使用享元模式适合。 10 | """ 11 | from functools import wraps 12 | 13 | from monkey_print2 import print 14 | 15 | 16 | def flyweight(cls): 17 | _instance = {} 18 | 19 | def _make_arguments_to_key(args, kwds): 20 | key = args 21 | if kwds: 22 | sorted_items = sorted(kwds.items()) 23 | for item in sorted_items: 24 | key += item 25 | return key 26 | 27 | @wraps(cls) 28 | def _flyweight(*args, **kwargs): 29 | cache_key = f'{cls}_{_make_arguments_to_key(args, kwargs)}' 30 | if cache_key not in _instance: 31 | _instance[cache_key] = cls(*args, **kwargs) 32 | return _instance[cache_key] 33 | 34 | return _flyweight 35 | 36 | 37 | @flyweight 38 | class A: 39 | def __init__(self, identity): 40 | self.identity = identity 41 | 42 | def eat(self): 43 | print(f'{self.identity} 吃饭') 44 | 45 | 46 | if __name__ == '__main__': 47 | a1 = A('001') 48 | a2 = A('001') 49 | print(a1 == a2) 50 | a1.eat() 51 | a2.eat() 52 | a3 = A('003') 53 | print(a1 == a3) 54 | a3.eat() 55 | 56 | """ 57 | a1和a2是同一个对象, 58 | "D:/coding2/python36patterns/创建型模式-享元模式-装饰器版本.py:49" 15:48:38 True 59 | "D:/coding2/python36patterns/创建型模式-享元模式-装饰器版本.py:43" 15:48:38 001 吃饭 60 | "D:/coding2/python36patterns/创建型模式-享元模式-装饰器版本.py:43" 15:48:38 001 吃饭 61 | "D:/coding2/python36patterns/创建型模式-享元模式-装饰器版本.py:53" 15:48:38 False 62 | "D:/coding2/python36patterns/创建型模式-享元模式-装饰器版本.py:43" 15:48:38 003 吃饭 63 | """ 64 | -------------------------------------------------------------------------------- /创建型模式-享元模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 享元模式 6 | 享元模式通过为相似对象引入数据共享来最小化内存使用,提升性能,一个享元就是一个包含状态的独立的不可变数据的共享对象,依赖状态的可变数据不应是享元的一部分,因为每个对象的这种信息不相同,无法共享,如果享元需要非固有数据应该由客户端代码显示提供。 7 | 8 | 享元模式介于单例模式和不加控制得多例模式之间。非常灵活,实用性和使用场景大于单例模式。 9 | 例如创建一个数据库连接,不希望建立多个连接,但又要在同一解释器下操作好多台机器的数据库,当传参的机器的ip端口不同时候,那肯定要创建一个新的连接了,这种使用享元模式适合。 10 | 11 | 12 | 例如原理有个同事写的代码是 13 | # -*- coding: utf-8 -*- 14 | # @Author : ydf 15 | # @Time : 2020/1/13 0013 12:09 16 | """ 17 | # 错误使用单例设计模式的例子 18 | 19 | ''' 20 | from redis import Redis 21 | class MyRedis: 22 | _inst = None 23 | 24 | def __new__(cls, *args, **kwargs): 25 | if not cls._inst: 26 | self = super().__new__(cls) 27 | self.__my_init__(*args, **kwargs) 28 | cls._inst = self 29 | return cls._inst 30 | 31 | 32 | def __my_init__(self,redis_db): 33 | print(f'传入的redis db是 {redis_db}') 34 | self.r = Redis(host='127.0.0.1',port=6379,db=redis_db) 35 | 36 | def set(self,key,value): 37 | self.r.set(key,value) 38 | 39 | if __name__ == '__main__': 40 | """ 41 | 单例模式使用不当,造成项目巨大漏洞,同事说怎么没看到redis生成相关的,因为单例模式一直在db5,同事以为能放到db6,db7,应该使用享元模式,单错误的使用了单例模式。 42 | """ 43 | MyRedis(5).set('a',1) 44 | MyRedis(6).set('b', 2) 45 | MyRedis(5).set('c', 3) 46 | MyRedis(6).set('d', 4) 47 | 48 | ''' 49 | from monkey_print2 import print 50 | 51 | 52 | class A: 53 | pool = dict() 54 | 55 | def __new__(cls, identity): 56 | """ 57 | 假设相同的学号只会有1个学生 58 | :param identity: 59 | :return: 60 | 61 | 这个是享元模式,有人一看到重写 __new__就条件反射神经过敏,但凡是重写了__new__的代码他都认为是单例模式。 62 | 主要是没有掌握类的一些概念的本质,死记硬背形式造成的错误认知。 63 | 最起码要知道__new__是干啥,__init__是干啥,不知道的话就会死记硬背单例模式的形式。 64 | """ 65 | obj = cls.pool.get(identity, None) 66 | if not obj: 67 | obj = object.__new__(cls) 68 | print(f'实例化 学号为 {identity} 的学生') 69 | cls.pool[identity] = obj 70 | return obj 71 | 72 | def __init__(self, identity): 73 | self.identity = identity 74 | 75 | def eat(self): 76 | print(f'{self.identity} 吃饭') 77 | 78 | 79 | if __name__ == '__main__': 80 | A('001').eat() 81 | A('001').eat() 82 | A('002').eat() 83 | 84 | # 下面是关于这个享元模式被人说成是单例模式的反驳。如果是单例模式print(id(A('001')) == id(A('002'))) 结果会是True 85 | print(id(A('001')) == id(A('002'))) # False 86 | print(id(A('001')) == id(A('001'))) # True 87 | 88 | """ 89 | 不会多次生成 001学号的同学这个对象。 90 | "D:/coding2/python36patterns/创建型模式-享元模式.py:30" 11:46:53 实例化 学号为 001 的学生 91 | "D:/coding2/python36patterns/创建型模式-享元模式.py:38" 11:46:53 001 吃饭 92 | "D:/coding2/python36patterns/创建型模式-享元模式.py:38" 11:46:53 001 吃饭 93 | "D:/coding2/python36patterns/创建型模式-享元模式.py:30" 11:46:53 实例化 学号为 002 的学生 94 | "D:/coding2/python36patterns/创建型模式-享元模式.py:38" 11:46:53 002 吃饭 95 | "D:/coding2/python36patterns/创建型模式-享元模式.py:45" 11:46:53 False 96 | "D:/coding2/python36patterns/创建型模式-享元模式.py:46" 11:46:53 True 97 | """ 98 | -------------------------------------------------------------------------------- /创建型模式-享元模式_只运行一次init.py: -------------------------------------------------------------------------------- 1 | """ 2 | 相比这个文件, 创建型模式-享元模式.py 3 | 4 | 使 __init__ 只运行异常,阻止相同对象多次运行 __init__ 5 | """ 6 | 7 | import dataset 8 | 9 | 10 | class DatasetSink: 11 | # 类级别的实例缓存,按 db_url 存储 12 | _instances = {} 13 | _has__init_set =set() # 把执行了 __init__的对象id保存起来对比 14 | 15 | def __new__(cls, db_url): 16 | # 如果 db_url 已存在,直接返回已有实例 17 | if db_url not in cls._instances: 18 | # 创建新实例并存入缓存 19 | self = super(DatasetSink, cls).__new__(cls) 20 | cls._instances[db_url] = self 21 | return cls._instances[db_url] 22 | 23 | def __init__(self, db_url): # 相同的db_url不要每次都运行这个__init__。 24 | if id(self) not in self.__class__._has__init_set : 25 | print(f'创建连接 {db_url}') 26 | self.db = dataset.connect(db_url,ensure_schema=True) 27 | self.__class__._has__init_set.add(id(self)) 28 | 29 | def save(self, table_name:str,data:dict,): 30 | # 使用已有的连接插入数据 31 | table = self.db[table_name] 32 | table.insert(data) -------------------------------------------------------------------------------- /创建型模式-单例模式-元类.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 单例模式 6 | 适用范围三颗星,这不是最常用的设计模式。往往只能脱出而出仅仅能说出这一种设计模式,但oop根本目的是要多例, 7 | 使用oop来实现单例模式,好处包括 8 | 1 延迟初始化(只有在生成对象时候调用__init__里面时候才进行初始化) 9 | 2 动态传参初始化 10 | 否则,一般情况下,不需要来使用类来搞单例模式,文件级模块全局变量的写法搞定即可,python模块天然单例,不信的话可以测试一下,c导入a,b也导入a,c导入b,在a里面直接print hello, 11 | 运行c.py,只会看到一次print hello。 12 | 13 | """ 14 | import threading 15 | from functools import wraps 16 | 17 | from monkey_print2 import print 18 | 19 | 20 | class Singleton(type): 21 | def __init__(cls, name, bases, dict): 22 | super(Singleton, cls).__init__(name, bases, dict) 23 | cls.instance = None 24 | 25 | def __call__(cls, *args, **kw): 26 | if cls.instance is None: 27 | cls.instance = super(Singleton, cls).__call__(*args, **kw) 28 | return cls.instance 29 | 30 | 31 | class A(metaclass=Singleton): 32 | def __init__(self, identity): 33 | print('执行init') 34 | self.identity = identity 35 | 36 | def eat(self): 37 | print(f'{self.identity} 吃饭') 38 | 39 | 40 | if __name__ == '__main__': 41 | a1 = A('001') 42 | a2 = A('001') 43 | print(a1 == a2) 44 | a1.eat() 45 | a2.eat() 46 | a3 = A('003') 47 | print(a1 == a3) 48 | a3.eat() 49 | 50 | """ 51 | a1 a2 a3 三次实例化出来,但都是同一个对象。对比下享元模式。 52 | "D:/coding2/python36patterns/创建型模式-单例模式.py:36" 16:00:25 True 53 | "D:/coding2/python36patterns/创建型模式-单例模式.py:30" 16:00:25 001 吃饭 54 | "D:/coding2/python36patterns/创建型模式-单例模式.py:30" 16:00:25 001 吃饭 55 | "D:/coding2/python36patterns/创建型模式-单例模式.py:40" 16:00:25 True 56 | "D:/coding2/python36patterns/创建型模式-单例模式.py:30" 16:00:25 001 吃饭 57 | """ 58 | -------------------------------------------------------------------------------- /创建型模式-单例模式-装饰器.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 单例模式 6 | 适用范围三颗星,这不是最常用的设计模式。往往只能脱出而出仅仅能说出这一种设计模式,但oop根本目的是要多例, 7 | 使用oop来实现单例模式,好处包括 8 | 1 延迟初始化(只有在生成对象时候调用__init__里面时候才进行初始化) 9 | 2 动态传参初始化 10 | 否则,一般情况下,不需要来使用类来搞单例模式,文件级模块全局变量的写法搞定即可,python模块天然单例,不信的话可以测试一下,c导入a,b也导入a,c导入b,在a里面直接print hello, 11 | 运行c.py,只会看到一次print hello。 12 | 13 | """ 14 | import threading 15 | from functools import wraps 16 | 17 | from monkey_print2 import print 18 | 19 | 20 | def singleton(cls): 21 | """ 22 | 单例模式装饰器,新加入线程锁,更牢固的单例模式,主要解决多线程如100线程同时实例化情况下可能会出现三例四例的情况,实测。 23 | """ 24 | _instance = {} 25 | singleton.__lock = threading.Lock() # 这里直接演示了线程安全版单例模式 26 | 27 | @wraps(cls) 28 | def _singleton(*args, **kwargs): 29 | with singleton.__lock: 30 | if cls not in _instance: 31 | _instance[cls] = cls(*args, **kwargs) 32 | return _instance[cls] 33 | 34 | return _singleton 35 | 36 | 37 | @singleton 38 | class A: 39 | def __init__(self, identity): 40 | self.identity = identity 41 | 42 | def eat(self): 43 | print(f'{self.identity} 吃饭') 44 | 45 | 46 | if __name__ == '__main__': 47 | a1 = A('001') 48 | a2 = A('001') 49 | print(a1 == a2) 50 | a1.eat() 51 | a2.eat() 52 | a3 = A('003') 53 | print(a1 == a3) 54 | a3.eat() 55 | 56 | """ 57 | a1 a2 a3 三次实例化出来,但都是同一个对象。对比下享元模式。 58 | "D:/coding2/python36patterns/创建型模式-单例模式.py:36" 16:00:25 True 59 | "D:/coding2/python36patterns/创建型模式-单例模式.py:30" 16:00:25 001 吃饭 60 | "D:/coding2/python36patterns/创建型模式-单例模式.py:30" 16:00:25 001 吃饭 61 | "D:/coding2/python36patterns/创建型模式-单例模式.py:40" 16:00:25 True 62 | "D:/coding2/python36patterns/创建型模式-单例模式.py:30" 16:00:25 003 吃饭 63 | """ 64 | -------------------------------------------------------------------------------- /创建型模式-单例模式-重写new但不每次执行init方式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 单例模式 6 | 适用范围三颗星,这不是最常用的设计模式。往往只能脱出而出仅仅能说出这一种设计模式,但oop根本目的是要多例, 7 | 使用oop来实现单例模式,好处包括 8 | 1 延迟初始化(只有在生成对象时候调用__init__里面时候才进行初始化) 9 | 2 动态传参初始化 10 | 否则,一般情况下,不需要来使用类来搞单例模式,文件级模块全局变量的写法搞定即可,python模块天然单例,不信的话可以测试一下,c导入a,b也导入a,c导入b,在a里面直接print hello, 11 | 运行c.py,只会看到一次print hello。 12 | 13 | """ 14 | 15 | 16 | from monkey_print2 import print 17 | 18 | 19 | class A: 20 | """ 21 | # &&&&&这种方式重写new实现的单例模式要注意,虽然生成的对象都是同一个,但init会每次都被自动调用。py2这种写法实现的单例模式,init不会自动被调用,py3会被自动调用。 22 | 要是init里面成本很大,不希望被自动调用,可以改成另外的方式,参考其他方式的单例模式。&&&&& 23 | 修改上面这个缺点的重写new方式 24 | """ 25 | _inst = None 26 | def __new__(cls, *args,**kwargs): 27 | if not cls._inst: 28 | cls._inst = object.__new__(cls) 29 | cls._inst.__custom_init__(*args,**kwargs) # 重点在这里。 30 | return cls._inst 31 | 32 | def __custom_init__(self, identity): # 这行也是是重点。去掉了__init__方法,init会被自动调用,改成在new里面主动调用。 33 | print('执行init') 34 | self.identity = identity 35 | 36 | def eat(self): 37 | print(f'{self.identity} 吃饭') 38 | 39 | 40 | if __name__ == '__main__': 41 | a1 = A('001') 42 | a2 = A('001') 43 | print(a1 == a2) 44 | a1.eat() 45 | a2.eat() 46 | a3 = A('003') 47 | print(a1 == a3) 48 | a3.eat() 49 | 50 | """ 51 | init只会执行一次。 52 | "D:/coding2/python36patterns/创建型模式-单例模式-重写new但不每次执行init方式.py:33" 16:20:19 执行init 53 | "D:/coding2/python36patterns/创建型模式-单例模式-重写new但不每次执行init方式.py:43" 16:20:19 True 54 | "D:/coding2/python36patterns/创建型模式-单例模式-重写new但不每次执行init方式.py:37" 16:20:19 001 吃饭 55 | "D:/coding2/python36patterns/创建型模式-单例模式-重写new但不每次执行init方式.py:37" 16:20:19 001 吃饭 56 | "D:/coding2/python36patterns/创建型模式-单例模式-重写new但不每次执行init方式.py:47" 16:20:19 True 57 | "D:/coding2/python36patterns/创建型模式-单例模式-重写new但不每次执行init方式.py:37" 16:20:19 001 吃饭 58 | 59 | 60 | """ 61 | -------------------------------------------------------------------------------- /创建型模式-单例模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 单例模式 6 | 适用范围三颗星,这不是最常用的设计模式。往往只能脱出而出仅仅能说出这一种设计模式,但oop根本目的是要多例, 7 | 使用oop来实现单例模式,好处包括 8 | 1 延迟初始化(只有在生成对象时候调用__init__里面时候才进行初始化) 9 | 2 动态传参初始化 10 | 否则,一般情况下,不需要来使用类来搞单例模式,文件级模块全局变量的写法搞定即可,python模块天然单例,不信的话可以测试一下,c导入a,b也导入a,c导入b,在a里面直接print hello, 11 | 运行c.py,只会看到一次print hello。 12 | 13 | """ 14 | 15 | 16 | from monkey_print2 import print 17 | 18 | 19 | class A: 20 | """ 21 | # 这种方式重写new实现的单例模式要注意,虽然生成的对象都是同一个,但init会每次都被自动调用。py2这种写法实现的单例模式,init不会自动被调用,py3会被自动调用。 22 | 要是init里面成本很大,不希望每次都被自动调用,可以改成另外的方式,参考其他方式的单例模式。 23 | """ 24 | _inst = None 25 | def __new__(cls, identity): 26 | if not cls._inst: 27 | cls._inst = object.__new__(cls) 28 | return cls._inst 29 | 30 | def __init__(self, identity): 31 | print('执行init') 32 | self.identity = identity 33 | 34 | def eat(self): 35 | print(f'{self.identity} 吃饭') 36 | 37 | 38 | if __name__ == '__main__': 39 | a1 = A('001') 40 | a2 = A('001') 41 | print(a1 == a2) 42 | a1.eat() 43 | a2.eat() 44 | a3 = A('003') 45 | print(a1 == a3) 46 | a3.eat() 47 | 48 | """ 49 | "D:/coding2/python36patterns/创建型模式-单例模式.py:27" 16:13:31 执行init 50 | "D:/coding2/python36patterns/创建型模式-单例模式.py:27" 16:13:31 执行init 51 | "D:/coding2/python36patterns/创建型模式-单例模式.py:37" 16:13:31 True 52 | "D:/coding2/python36patterns/创建型模式-单例模式.py:31" 16:13:31 001 吃饭 53 | "D:/coding2/python36patterns/创建型模式-单例模式.py:31" 16:13:31 001 吃饭 54 | "D:/coding2/python36patterns/创建型模式-单例模式.py:27" 16:13:31 执行init 55 | "D:/coding2/python36patterns/创建型模式-单例模式.py:41" 16:13:31 True 56 | "D:/coding2/python36patterns/创建型模式-单例模式.py:31" 16:13:31 003 吃饭 57 | 58 | """ 59 | -------------------------------------------------------------------------------- /创建型模式-原型模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 原型模式 6 | 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 7 | 8 | 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。 9 | 10 | 介绍 11 | 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 12 | 13 | 主要解决:在运行期建立和删除原型。 14 | 15 | 何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。 16 | 17 | 如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。 18 | 19 | 关键代码: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。 20 | 21 | 应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。 22 | 23 | 优点: 1、性能提高。 2、逃避构造函数的约束。 24 | 25 | 缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。 26 | 27 | 使用场景: 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。 28 | 29 | 注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。 30 | """ 31 | from monkey_print2 import print 32 | 33 | import copy 34 | from collections import OrderedDict 35 | 36 | 37 | class Book: 38 | def __init__(self, name, authors, price, **rest): 39 | '''rest的例子有:出版商、长度、标签、出版日期''' 40 | self.name = name 41 | self.authors = authors 42 | self.price = price 43 | self.__dict__.update(rest) # 添加其他额外属性 44 | 45 | def __str__(self): 46 | mylist = [] 47 | ordered = OrderedDict(sorted(self.__dict__.items())) 48 | for i in ordered.keys(): 49 | mylist.append('{}: {}'.format(i, ordered[i])) 50 | if i == 'price': 51 | mylist.append('$') 52 | mylist.append('\n') 53 | return ''.join(mylist) 54 | 55 | 56 | class Prototype: 57 | def __init__(self): 58 | self.objects = dict() # 初始化一个原型列表 59 | 60 | def register(self, identifier, obj): 61 | # 在原型列表中注册原型对象 62 | self.objects[identifier] = obj 63 | 64 | def unregister(self, identifier): 65 | # 从原型列表中删除原型对象 66 | del self.objects[identifier] 67 | 68 | def clone(self, identifier, **attr): 69 | # 根据 identifier 在原型列表中查找原型对象并克隆 70 | found = self.objects.get(identifier) 71 | if not found: 72 | raise ValueError('Incorrect object identifier: {}'.format(identifier)) 73 | obj = copy.deepcopy(found) 74 | obj.__dict__.update(attr) # 用新的属性值替换原型对象中的对应属性 75 | return obj 76 | 77 | 78 | 79 | 80 | if __name__ == '__main__': 81 | b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), 82 | price=118, publisher='Prentice Hall', length=228, publication_date='1978-02-22', 83 | tags=('C', 'programming', 'algorithms', 'data structures')) 84 | 85 | prototype = Prototype() 86 | cid = 'k&r-first' 87 | prototype.register(cid, b1) 88 | b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99, length=274, publication_date='1988-04-01', edition=2) 89 | 90 | for i in (b1, b2): 91 | print(i) 92 | print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2))) 93 | -------------------------------------------------------------------------------- /创建型模式-对象池模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 16:41 4 | """ 5 | 重要的模式之一。使用这种模式可以创造数据库连接池 浏览器池等。 6 | 实现原理有两个重要的地方是,使用时候借,使用完成后归还。我使用这个创造了一个非常强大的场景。 7 | 最好是使用with语法来完成对象的借和还,减少调用处的代码。 8 | 9 | 资源受限的, 不需要可伸缩性的环境(cpu\内存等物理资源有限): cpu性能不够强劲, 内存比较紧张, 垃圾收集, 内存抖动会造成比较大的影响, 需要提高内存管理效率, 响应性比吞吐量更为重要; 10 | 数量受限的, 比如数据库连接; 11 | 创建成本高的对象, 可以考虑是否池化, 比较常见的有线程池(ThreadPoolExecutor), 字节数组池等。 12 | """ 13 | from queue import Queue 14 | from monkey_print2 import print 15 | 16 | 17 | class QueueObject(): 18 | 19 | def __init__(self, queue, auto_get=False): 20 | self._queue = queue 21 | self.object = self._queue.get() if auto_get else None 22 | 23 | def __enter__(self): 24 | if self.object is None: 25 | self.object = self._queue.get() 26 | return self.object 27 | 28 | def __exit__(self, Type, value, traceback): 29 | if self.object is not None: 30 | self._queue.put(self.object) 31 | self.object = None 32 | 33 | def __del__(self): 34 | if self.object is not None: 35 | self._queue.put(self.object) 36 | self.object = None 37 | 38 | 39 | def main(): 40 | sample_queue = Queue() 41 | sample_queue.put('yam') 42 | with QueueObject(sample_queue) as obj: 43 | print('Inside with: {}'.format(obj)) 44 | print('Outside with: {}'.format(sample_queue.get())) 45 | 46 | sample_queue.put('sam') 47 | queue_object = QueueObject(sample_queue, True) 48 | print('内部 func: {}'.format(queue_object.object)) 49 | print('外部 func: {}'.format(sample_queue.get())) 50 | 51 | if not sample_queue.empty(): 52 | print(sample_queue.get()) 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /创建型模式-建造者模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 建造者模式 6 | 7 | 建造者模式 8 | 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 9 | 10 | 一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 11 | 12 | 介绍 13 | 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 14 | 15 | 主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。 16 | 17 | 何时使用:一些基本部件不会变,而其组合经常变化的时候。 18 | 19 | 如何解决:将变与不变分离开。 20 | 21 | 关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。 22 | 23 | 应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。 24 | 25 | 优点: 1、建造者独立,易扩展。 2、便于控制细节风险。 26 | 27 | 缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。 28 | 29 | 使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。 30 | 31 | 注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。 32 | """ 33 | from monkey_print2 import print 34 | 35 | import abc 36 | 37 | 38 | # 步骤一:创建对应的产品抽象类/产品类 39 | class Building(object): 40 | def __init__(self): 41 | self.floor = None 42 | self.size = None 43 | 44 | def __repr__(self): 45 | return 'Floor: {0.floor} | size: {0.size}'.format(self) 46 | 47 | 48 | # 步骤三:创建构建者抽象类,主要是定义构建者通用属性/方法,以及继承者必须实现的功能抽象 49 | # Abstract builder 50 | class AbsBuilder(object): 51 | def __init__(self): 52 | self.building = None 53 | 54 | def new_building(self): 55 | self.building = Building() 56 | 57 | @abc.abstractmethod 58 | def build_floor(self): 59 | pass 60 | 61 | @abc.abstractmethod 62 | def build_size(self): 63 | pass 64 | 65 | 66 | # 步骤四:具体构建者类实现 67 | class HouseBuilder(AbsBuilder): 68 | def build_floor(self): 69 | self.building.floor = 'one' 70 | 71 | def build_size(self): 72 | self.building.size = '220 squre' 73 | 74 | 75 | class FlatBuilder(AbsBuilder): 76 | def build_floor(self): 77 | self.building.floor = 'seven' 78 | 79 | def build_size(self): 80 | self.building.size = '140 squre' 81 | 82 | 83 | # 步骤二:创建产品的指挥者类,即最终提供给客户端的产品的实例对象,以及组装过程 84 | class Director(object): 85 | def __init__(self): 86 | self.builder = None 87 | 88 | def construct_building(self): 89 | """ 90 | #建造者模式下,仅在需要时客户端代码才显式地请求指挥者返回最终的对象 91 | """ 92 | self.builder.new_building() 93 | self.builder.build_floor() 94 | self.builder.build_size() 95 | 96 | def get_building(self): 97 | return self.builder.building 98 | 99 | 100 | class Client(object): 101 | def build(self, build_type): 102 | if build_type == "House": 103 | director = Director() 104 | builder = HouseBuilder() 105 | director.builder = builder 106 | director.construct_building() 107 | building = director.get_building() 108 | print(building) 109 | else: 110 | director = Director() 111 | builder = FlatBuilder() 112 | director.builder = builder 113 | director.construct_building() 114 | building = director.get_building() 115 | print(building) 116 | 117 | 118 | if __name__ == "__main__": 119 | build_type = "Flat" 120 | client = Client() 121 | client.build(build_type) 122 | -------------------------------------------------------------------------------- /创建型模式-抽象工厂模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 抽象工厂模式 6 | 7 | 抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。 8 | 9 | 抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。 10 | 11 | 根据里氏替换原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。 12 | 13 | 换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。 14 | """ 15 | from monkey_print2 import print 16 | 17 | 18 | class Xiaomi5: 19 | def __init__(self): 20 | self.phone_name = '小米5' 21 | 22 | def send_msg(self): 23 | print(f'用 {self.phone_name} 发短信') 24 | 25 | 26 | class Xiaomi6: 27 | def __init__(self): 28 | self.phone_name = '小米6' 29 | 30 | def send_msg(self): 31 | print(f'用 {self.phone_name} 发短信') 32 | 33 | 34 | class XiaomFactory: 35 | @staticmethod 36 | def get_phone(phone_type): 37 | if phone_type == '5': 38 | return Xiaomi5() 39 | elif phone_type == '6': 40 | return Xiaomi6() 41 | 42 | 43 | class Apple5: 44 | def __init__(self): 45 | self.phone_name = '苹果5' 46 | 47 | def send_msg(self): 48 | print(f'用 {self.phone_name} 发短信') 49 | 50 | 51 | class Apple6: 52 | def __init__(self): 53 | self.phone_name = '苹果6' 54 | 55 | def send_msg(self): 56 | print(f'用 {self.phone_name} 发短信') 57 | 58 | 59 | class AppleFactory: 60 | @staticmethod 61 | def get_phone(phone_type): 62 | if phone_type == '5': 63 | return Xiaomi5() 64 | elif phone_type == '6': 65 | return Xiaomi6() 66 | 67 | 68 | class FactoryProducer: 69 | @staticmethod 70 | def get_factory(factory_name): 71 | if factory_name == 'xiaomi': 72 | return XiaomFactory() 73 | elif factory_name == 'apple': 74 | return AppleFactory() 75 | 76 | 77 | if __name__ == '__main__': 78 | factory = FactoryProducer.get_factory('xiaomi') 79 | xiaomi5 = factory.get_phone('5') 80 | xiaomi5.send_msg() 81 | 82 | """ 83 | "D:/coding2/python36patterns/创建型模式-抽象工厂模式.py:22" 14:38:03 用 小米5 发短信 84 | """ 85 | -------------------------------------------------------------------------------- /创建型模式-简单工厂模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 13:55 4 | """ 5 | 简单工厂模式 6 | 7 | 好处主要有: 8 | 1、将创建实例的工作与使用实例的工作分开 9 | 2、把初始化实例时的工作放到工厂里进行,使代码更容易维护。 10 | 3、使得修改代码时不会引起太大的变动,良好的扩展性。 11 | 比如,有对象A。现在要修改这个实例的方法。就会有对象B,继承A,然后重写A里面的某个方法。这时,如果没有工厂模式,那么就要把每次创建A对象的代码都改为创建B对象。这是很可怕的一件事情。 12 | 如果有工厂模式,那么,我们可以只修改工厂中创建A对象的方法,就可以完成这件事情了。更容易的,可以把这个实例的创建写在配置文件中。那么对于这种变动,只要修改配置文件就可以实现了,不需要修改工厂类。 13 | """ 14 | from monkey_print2 import print 15 | 16 | 17 | class Xiaomi5: 18 | def __init__(self): 19 | self.phone_name = '小米5' 20 | 21 | def send_msg(self): 22 | print(f'用 {self.phone_name} 发短信') 23 | 24 | 25 | class Xiaomi6: 26 | def __init__(self): 27 | self.phone_name = '小米6' 28 | 29 | def send_msg(self): 30 | print(f'用 {self.phone_name} 发短信') 31 | 32 | 33 | def get_xiaomi_phone(phone_type): 34 | if phone_type == '5': 35 | return Xiaomi5() 36 | elif phone_type == '6': 37 | return Xiaomi6() 38 | 39 | 40 | if __name__ == '__main__': 41 | phone5 = get_xiaomi_phone('5') 42 | phone5.send_msg() 43 | 44 | phone6 = get_xiaomi_phone('6') 45 | phone6.send_msg() 46 | 47 | """ 48 | "D:/coding2/python36patterns/创建型模式-简单工厂模式.py:15" 14:16:27 用 小米5 发短信 49 | "D:/coding2/python36patterns/创建型模式-简单工厂模式.py:23" 14:16:27 用 小米6 发短信 50 | """ 51 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/使用一个入口函数来自己调用各种辅助方法,当辅助方法需要变化时候,除了复制粘贴扣字来实现,别无他法。person1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | 4 | """ 5 | 实现一个人的 长身体 小便 的功能。 6 | 模拟常见的价格解析,n多个辅助函数,然后暴露一个公有函数供外部调用,这样无需在调用处写好几行调用各种证件辅助方法和保存各种证件变量。但是当某个辅助函数需要改变时候,除了全盘复制粘贴扣字,还有什么办法? 7 | 如果没办法,那就是又会变成使用无限复制粘贴low模式了。 8 | 9 | """ 10 | 11 | 12 | def _show_weight(name, weight): 13 | print(f'{name} 的体重是: {weight} 千克') 14 | 15 | 16 | def _show_height(xingmin, height): # xingmin: xingmin也是代表姓名,故意弄成形参不是name,面向过程就会有这种搞不清楚一个参数在不同函数中是不是同一个意义的大缺点。 17 | print(f'{xingmin} 的身高是: {height} 厘米') 18 | 19 | 20 | def _grow_weight(name, weight, growing_weight): 21 | weight += growing_weight 22 | print(f'{name} 的体重增加 {growing_weight} 千克') 23 | return weight # 必须把结果return到外部保存 24 | 25 | 26 | def _grow_height(xinmin, height, growing_height): 27 | print(f'{xinmin} 的身高增加 {growing_height} 厘米') 28 | height += growing_height 29 | return height # 必须把结果return到外部保存 30 | 31 | 32 | def _pee(name, weight): 33 | """ 34 | 因为小便会导致体重发生变化,必须把原来的体重传进来。 35 | """ 36 | print(f'{name} 站着小便') # REMIND 如果是女得,这个函数需要修改成 蹲着小便。 37 | return __reduce_weight_because_of_pee(name, weight) # 必须把结果return到外部保存 38 | 39 | 40 | def __reduce_weight_because_of_pee(name, tizhong): 41 | # 小便后会导致体重减少。 42 | tizhong = _grow_weight(name, tizhong, growing_weight=-0.1) # 体重的别名tizhong和weight是一样的,面向过程不使用实例属性,在很多函数中大量重复传参,形参就会可能不一致,导致看不懂代码。 43 | return tizhong 44 | 45 | 46 | def public_main_function(name, weight, height): # 写一个公有入口函数,自己调用身高增加函数 体重增加函数 上卫生间小便的函数,一般现在的代码都是这样弄得,这样在外部调用时候调用入口函数就可以了。 47 | height = _grow_height(name, height, 5) 48 | weight = _grow_weight(name, weight, 1) 49 | weight = _pee(name, weight) # 如果要实现pee函数不同之处,面向过程时候这里也要改。 50 | _show_height(name, height) 51 | _show_weight(name, weight) 52 | return weight, height 53 | 54 | 55 | if __name__ == '__main__': 56 | result = public_main_function('小明', 30, 120) 57 | print(result) 58 | """ 59 | 这样可以不在外部写很多行来调用辅助函数,和写很多中间变量在外部给多个辅助函数传来传去的。但有三个大毛病是 60 | 1.写法曲折,虽然提供了一个入口函数,但还是在入口函数里面内部反复return和传参,思路不直观。没有在一组函数中操纵全局变量的那种写法思路清晰。 61 | 2、现在要使用小红怎么办?增加一个女孩的_pee函数 (_pee_girl),还要增加一个调用女孩pee函数的 public_main_function函数(public_main_function_girl)。这样会导致函数名不一致,调用不一致,又增加调用难度。 62 | 3、如果不新增和修改函数名,假设此模块名字是person1.py那就只能新增能一个模块person2.py,然后全盘复制粘贴person1.py,然后将_pee函数里面扣字,这样调用 person1.public_main_function() 就和 person2.public_main_function() 63 | 具有调用方式上有一致性,也实现了男女的小便不同之处,但为了一个小的不同,而去复制粘贴全流程,然后扣字,这样简直不要太low,每当需要修改某个地方时候,这两个文件都需要去修改一下。复制粘贴扣字两三次还好,要是复制粘贴扣字几百次,那就很伤心了。酒店里面调度不同函数比价的文件夹里面连续重复20多个文件,所有文件代码布局外观一眼看起来具有99%相似性,就是这种原因导致写成那样的。 64 | 只有使用oop才能很好解决这几个弊端。写代码慢,写代码复杂难懂,写得无限重复相似 就是排斥oop和过分喜爱极端面向过程写法导致的。 65 | """ 66 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/全局变量加函数写法/xiaohong.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | """ 4 | 模拟无限复制粘贴扣字low模式 5 | 全局变量加函数写法,可以做到少传参 少return,看起来也很简洁,实现简单清晰。 6 | 但模块是唯一的,全局变量在模块也是唯一的,无法进行多实例方面的需求,一般都很少这样写,但有不用oop,所以会造成一般喜欢使用频繁return和大量重复传参的写法。 7 | """ 8 | 9 | name = '小红' 10 | height = 100 11 | weight = 28 12 | 13 | 14 | def show_weight(): 15 | print(f'{name} 的体重是: {weight} 千克') 16 | 17 | 18 | def show_height(): 19 | print(f'{name} 的身高是: {height} 厘米') 20 | 21 | 22 | def grow_weight(growing_weight): 23 | global weight 24 | print(f'{name} 的体重增加 {growing_weight} 千克') 25 | weight += growing_weight 26 | 27 | 28 | def grow_height(growing_height): 29 | global height 30 | print(f'{name} 的身高增加 {growing_height} 厘米') 31 | height += growing_height 32 | 33 | 34 | def pee(): 35 | """ 36 | 举个影响属性的例子,上卫生间小便,会导致体重减少。 37 | :return: 38 | """ 39 | # REMIND 女得不同,需要复制粘贴扣字整个流程。 40 | print(f'{name} 蹲着小便') 41 | _reduce_weight_because_of_pee() 42 | 43 | 44 | def _reduce_weight_because_of_pee(): 45 | global weight 46 | weight = weight - 0.1 47 | 48 | 49 | if __name__ == '__main__': 50 | show_height() 51 | show_weight() 52 | grow_height(5) 53 | grow_weight(1) 54 | show_height() 55 | show_weight() 56 | pee() 57 | show_weight() 58 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/全局变量加函数写法/xiaomin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | 4 | """ 5 | 模拟无限复制粘贴扣字low模式 6 | 全局变量加函数写法,可以做到少传参 少return,看起来也很简洁,实现简单清晰。 7 | 但模块是唯一的,全局变量在模块也是唯一的,无法进行多实例方面的需求,一般都很少这样写,但有不用oop,所以会造成一般喜欢使用频繁return和大量重复传参的写法。 8 | 9 | """ 10 | 11 | name = '小明' 12 | height = 130 13 | weight = 30 14 | 15 | 16 | def show_weight(): 17 | print(f'{name} 的体重是: {weight} 千克') 18 | 19 | 20 | def show_height(): 21 | print(f'{name} 的身高是: {height} 厘米') 22 | 23 | 24 | def grow_weight(growing_weight): 25 | global weight 26 | print(f'{name} 的体重增加 {growing_weight} 千克') 27 | weight += growing_weight 28 | 29 | 30 | def grow_height(growing_height): 31 | global height 32 | print(f'{name} 的身高增加 {growing_height} 厘米') 33 | height += growing_height 34 | 35 | 36 | def pee(): 37 | """ 38 | 举个影响属性的例子,上卫生间小便,会导致体重减少。 39 | :return: 40 | """ 41 | print(f'{name} 站着小便') 42 | _reduce_weight_because_of_pee() 43 | 44 | 45 | def _reduce_weight_because_of_pee(): 46 | global weight 47 | weight = weight - 0.1 48 | 49 | 50 | if __name__ == '__main__': 51 | show_height() 52 | show_weight() 53 | grow_height(5) 54 | grow_weight(1) 55 | show_height() 56 | show_weight() 57 | pee() 58 | show_weight() 59 | 60 | # REMIND 由于是使用的全局变量,只能完成一个单例的情况,只能完成小明这一个人,无法同时完成小明和小王,如果需要使用小王,必须复制粘贴扣字。 61 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/全局变量加函数写法/xiaowang.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | """ 4 | 模拟无限复制粘贴扣字low模式 5 | 全局变量加函数写法,可以做到少传参 少return,看起来也很简洁,实现简单清晰。 6 | 但模块是唯一的,全局变量在模块也是唯一的,无法进行多实例方面的需求,一般都很少这样写,但有不用oop,所以会造成一般喜欢使用频繁return和大量重复传参的写法。 7 | 8 | """ 9 | 10 | name = '小王' 11 | height = 140 12 | weight = 32 13 | 14 | 15 | def show_weight(): 16 | print(f'{name} 的体重是: {weight} 千克') 17 | 18 | 19 | def show_height(): 20 | print(f'{name} 的身高是: {height} 厘米') 21 | 22 | 23 | def grow_weight(growing_weight): 24 | global weight 25 | print(f'{name} 的体重增加 {growing_weight} 千克') 26 | weight += growing_weight 27 | 28 | 29 | def grow_height(growing_height): 30 | global height 31 | print(f'{name} 的身高增加 {growing_height} 厘米') 32 | height += growing_height 33 | 34 | 35 | def pee(): 36 | """ 37 | 举个影响属性的例子,上卫生间小便,会导致体重减少。 38 | :return: 39 | """ 40 | print(f'{name} 站着小便') 41 | _reduce_weight_because_of_pee() 42 | 43 | 44 | def _reduce_weight_because_of_pee(): 45 | global weight 46 | weight = weight - 0.1 47 | 48 | 49 | if __name__ == '__main__': 50 | show_height() 51 | show_weight() 52 | grow_height(5) 53 | grow_weight(1) 54 | show_height() 55 | show_weight() 56 | pee() 57 | show_weight() 58 | 59 | # REMIND 由于是使用的全局变量,只能完成一个单例的情况,只能完成小明这一个人,无法同时完成小明和小王,如果需要使用小王,必须复制粘贴扣字。 60 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/无效废物装逼滑稽类 伪oop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | """ 4 | 面向过程写得无效废物类,没有封装,大量rentun,这样写的类没有任何作用。 5 | 和person_f文件的写法是一样的。下面这3个类分别为实例方法类 类方法类 静态方法类,但写成了需要反复重复传参和大量return,这3个全部都是废物滑稽类,不是真oop 6 | 7 | 写这个是为了说明 8 | 1、证明我没说过写类就比写函数流弊了,关键要看是不是真oop。 9 | 2、写代码时候不能有写类就是高大上的想法,关键看写的类是不是真opp。 10 | """ 11 | 12 | 13 | class Person: 14 | def show_weight(self, name, weight): 15 | print(f'{name} 的体重是: {weight} 千克') 16 | 17 | def show_height(self, xingmin, height): # xingmin: xingmin也是代表姓名,故意弄成形参不是name,面向过程就会有这种搞不清楚一个参数在不同函数中是不是同一个意义的大缺点。 18 | print(f'{xingmin} 的身高是: {height} 厘米') 19 | 20 | def grow_weight(self, name, weight, growing_weight): 21 | weight += growing_weight 22 | print(f'{name} 的体重增加 {growing_weight} 千克') 23 | return weight # 必须把结果return到外部保存 24 | 25 | def grow_height(self, xinmin, height, growing_height): 26 | print(f'{xinmin} 的身高增加 {growing_height} 厘米') 27 | height += growing_height 28 | return height # 必须把结果return到外部保存 29 | 30 | def pee(self, name, weight): 31 | """ 32 | 因为小便会导致体重发生变化,必须把原来的体重传进来。 33 | """ 34 | print(f'{name} 蹲着小便') 35 | return self._reduce_weight_because_of_pee(weight) # 必须把结果return到外部保存 36 | 37 | def _reduce_weight_because_of_pee(self, tizhong): 38 | tizhong = tizhong - 0.1 # 体重的别名tizhong和weight是一样的,面向过程不使用实例属性,在很多函数中大量重复传参,形参就会可能不一致,导致看不懂代码。 39 | return tizhong 40 | 41 | 42 | class Person2: 43 | @classmethod 44 | def show_weight(cls, name, weight): 45 | print(f'{name} 的体重是: {weight} 千克') 46 | 47 | @classmethod 48 | def show_height(cls, xingmin, height): # xingmin: xingmin也是代表姓名,故意弄成形参不是name,面向过程就会有这种搞不清楚一个参数在不同函数中是不是同一个意义的大缺点。 49 | print(f'{xingmin} 的身高是: {height} 厘米') 50 | 51 | @classmethod 52 | def grow_weight(cls, name, weight, growing_weight): 53 | weight += growing_weight 54 | print(f'{name} 的体重增加 {growing_weight} 千克') 55 | return weight # 必须把结果return到外部保存 56 | 57 | @classmethod 58 | def grow_height(cls, xinmin, height, growing_height): 59 | print(f'{xinmin} 的身高增加 {growing_height} 厘米') 60 | height += growing_height 61 | return height # 必须把结果return到外部保存 62 | 63 | @classmethod 64 | def pee(cls, name, weight): 65 | """ 66 | 因为小便会导致体重发生变化,必须把原来的体重传进来。 67 | """ 68 | print(f'{name} 蹲着小便') 69 | return cls._reduce_weight_because_of_pee(weight) # 必须把结果return到外部保存 70 | 71 | @classmethod 72 | def _reduce_weight_because_of_pee(cls, tizhong): 73 | tizhong = tizhong - 0.1 # 体重的别名tizhong和weight是一样的,面向过程不使用实例属性,在很多函数中大量重复传参,形参就会可能不一致,导致看不懂代码。 74 | return tizhong 75 | 76 | 77 | class Person3: 78 | @staticmethod 79 | def show_weight(name, weight): 80 | print(f'{name} 的体重是: {weight} 千克') 81 | 82 | @staticmethod 83 | def show_height(xingmin, height): # xingmin: xingmin也是代表姓名,故意弄成形参不是name,面向过程就会有这种搞不清楚一个参数在不同函数中是不是同一个意义的大缺点。 84 | print(f'{xingmin} 的身高是: {height} 厘米') 85 | 86 | @staticmethod 87 | def grow_weight(name, weight, growing_weight): 88 | weight += growing_weight 89 | print(f'{name} 的体重增加 {growing_weight} 千克') 90 | return weight # 必须把结果return到外部保存 91 | 92 | @staticmethod 93 | def grow_height(xinmin, height, growing_height): 94 | print(f'{xinmin} 的身高增加 {growing_height} 厘米') 95 | height += growing_height 96 | return height # 必须把结果return到外部保存 97 | 98 | @staticmethod 99 | def pee(name, weight): 100 | """ 101 | 因为小便会导致体重发生变化,必须把原来的体重传进来。 102 | """ 103 | print(f'{name} 蹲着小便') 104 | return Person3._reduce_weight_because_of_pee(weight) # 必须把结果return到外部保存 105 | 106 | @staticmethod 107 | def _reduce_weight_because_of_pee(tizhong): 108 | tizhong = tizhong - 0.1 # 体重的别名tizhong和weight是一样的,面向过程不使用实例属性,在很多函数中大量重复传参,形参就会可能不一致,导致看不懂代码。 109 | return tizhong 110 | 111 | 112 | if __name__ == '__main__': 113 | """ 114 | person1是无效废物类,因为他改成 person2 person3,下面的调用还能正常运行,所以是个废物类。 115 | """ 116 | xiaohong = Person3() 117 | xiaohong_name = '小红' 118 | xiaohong_height = 110 119 | xiaohong_weight = 25 120 | xiaohong.show_height(xiaohong_name, xiaohong_height) 121 | xiaohong.show_weight(xiaohong_name, xiaohong_weight) 122 | xiaohong_height = xiaohong.grow_height(xiaohong_name, xiaohong_height, 5) 123 | xiaohong_weight = xiaohong.grow_weight(xiaohong_name, xiaohong_weight, 1) # 体重发生了变化,必须把结果return出来保存 124 | xiaohong.show_height(xiaohong_name, xiaohong_height) 125 | xiaohong.show_weight(xiaohong_name, xiaohong_weight) # 展示体重,需要把变化后的体重传进来 126 | xiaohong_weight = xiaohong.pee(xiaohong_name, xiaohong_weight) # 连上个厕所都需要把体重参数传进来,然后还要保存新结果,这样大量传参简直不要太low 127 | xiaohong.show_weight(xiaohong_name, xiaohong_weight) 128 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/真正正宗oop person_oop.py: -------------------------------------------------------------------------------- 1 | # coding=utf8 2 | """ 3 | 1、实现人的功能。oop写法,使用类/对象 加 方法。 4 | 2、重要是要理解面向对象里面的封装,封装是最重要的特点,写出没有体现出封装特点的类就是无效废物类就是面向过程的类。 5 | 3、这种写法清晰明了,类/对象 加 方法 的写法 几乎和模块文件加全局变量加函数的写法思路是一模一样的,只是把命名空间下沉了一级,就能毫无曲折的实现了多实例的要求。继不继承都是次要的,最最最重要是看封装,不要把注意力重点放在这里面的继承了,只是顺便演示一个继承是如何减少代码实现不同点而已。继承不是面向对象必须的,封装才是。 6 | 4、使用oop时候,在实现时候更为简单,几乎行云流水,不需要考虑频繁的考虑传参和return 7 | 出状态(变量)给外部。在调用时候也更具备一致性,在调用处只需要一个实例化出一个对象,类的外部只需要一个变量代表此实例即可。否则使用面向过程还需要模拟多实例,在调用处最少需要保存 名字 体重 身高三个变量,并且每次调用函数都需要传入和保存return出来的值,这样调用起来反复传参和return修改外部变量,调用起来复杂和看起来乱七八糟的调用形式不具备一致性。 8 | """ 9 | 10 | """ 11 | 得出转化公式的规律: 12 | 一、针对全局变量加函数无限复制粘贴扣字的写法, 13 | 1) 模块级降级为类名 14 | 2) 全局变量改成实例属性 15 | 3) 函数改为方法 16 | 17 | 二、针对频繁return和大量重复传参的写法 18 | 1、转化公式1 19 | 0)在脑袋中重构,把写法形式改成全局变量加函数的写法,此时不用担心全局变量是唯一的,不用大量重复传参和return,所有需要传参和return的都想象成使用全局变量和操作全局变量。 20 | 1) 模块级降级为类名 21 | 2) 全局变量改成实例属性 22 | 3) 函数改为方法 23 | 后面三个步骤是一样的。全局变量变为实例属性后,每次实例化后每个对象的实例属性都是互不干扰的。每个对象可以看作为一个模块级写法的 模块文件的无限深拷贝。 24 | 25 | 2、转化公式2 26 | 1) 新增一个类 27 | 2)把重复传参和return的形参,全都去掉,改成实例属性 28 | 3) 函数改为方法。 29 | 30 | 对任何面向过程写法,使用转化公式,一定就可以修改成oop,然来的代码思维不需要做任何改变,只要按这个公式就可以改造。(前提是满足需要使用oop的两个条件,才需要改造) 31 | 对新写的代码,也可以按然来的想法写,说的是在脑袋里面那么写(不然真那么写,再修改,浪费花时间),然后按照此公式转化后写在ide里面。 32 | """ 33 | 34 | """ 35 | 最重要是理解: 命名空间 全局变量 实例属性 多实例需求 函数和方法 的关系,搞清楚了,写oop十分之简单,不会造成极端面向过程的曲折写法。 36 | 在word文档中会写更详细的解释。 37 | """ 38 | 39 | """ 40 | 常见问题解答 41 | 1、是不是所有代码都要用oop? 42 | 答:不是,按照上面的方式判断用那种方式好,目的是要简单代码少就可以,便于维护扩展就好。 43 | 2、函数和类上面区别? 44 | 没有区别,就像问人和走路有什么区别,猪和吃饭有什么区别,问得牛头不对马嘴的伪问题,函数和方法才可以比较。类(对象)和模块才有可比性,必须要搞清楚原因,不然脑袋中有这个比较的想法那就不可能写得了oop。 45 | 面向过程是 文件模块名.eat(狗的名字,shit) 46 | oop是 狗.eat(shit) 47 | """ 48 | 49 | 50 | """ 51 | 编程范式 52 | 1.1 方式一,平铺指令。 从上往下叠加指令,适合简单的独立脚本。不需要和没机会被别的模块导入。 53 | 1.2 方式二,面向过程函数式编程。适合实现独立的转化功能,基本原理是要实现转化 y = f(x),适合函数无依赖状态(不需要在多个函数中频繁的传入和return相同意义的参数)。 54 | 1.3 方式三,oop编程.适合多个函数间需要使用同一个变量,并且需要多实例(如果使在使用面向过程时候需要使用函数频繁的return各种状态/变量由类外使用多个参数来保存这些值和传入这些值,那就是也判断为需要多实例),必须同时满足这两个条件,才使用oop好,否则不需要oop。(但单例模式为了控制灵活的初始化传参,一般也用类的方式) 55 | 1.4 网上说的简单用面向过程,复杂的用面向对象,这简直是错误的废话。简单和复杂界定不了,即使是一个简单的查询价格,经过大量平台的重写对比,oop都能比面向过程减少70%行以上的代码,所以用网上这句话来判断用什么方式来写代码是错误的。只要严格使用上面描述的判断方式,就能很容易知道在什么场景什么时候使用哪种方式好了,不需要oop嗨写成类就是没理解好oop能更好地解决什么。 56 | 1.5 要多使用oop,但不要写成纯静态或者半静态的无效废物类。 面向过程一定可以搞定一切,但是实现麻烦、调用更麻烦,那就不适合面向过程了。比如猴子补丁可以搞定继承,闭包可以搞定封装,但是没什么好处,实现麻烦。先要转oop,只有放弃极端面向过程,不对面向过程过分的偏执和喜欢,才能开始学习更多设计模式。 57 | """ 58 | 59 | 60 | class Person(object): 61 | 62 | def __init__(self, name, height, weight): 63 | self.name = name 64 | self.height = height 65 | self.weight = weight 66 | 67 | def show_weight(self): 68 | print(f'{self.name} 的体重是: {self.weight} 千克') 69 | 70 | def show_height(self): 71 | print(f'{self.name} 的身高是: {self.height} 厘米') 72 | 73 | def grow_weight(self, growing_weight): # 增加的体重是可变的外界传来的,需要作为方法的参数传进来,不可避免。 74 | print(f'{self.name} 的体重增加 {growing_weight} 千克') 75 | self.weight += growing_weight 76 | 77 | def grow_height(self, growing_height): 78 | print(f'{self.name} 的身高增加 {growing_height} 厘米') 79 | self.height += growing_height 80 | 81 | def pee(self): 82 | """ 83 | 举个影响属性的例子,上卫生间小便,会导致体重减少。 84 | :return: 85 | """ 86 | self._reduce_weight_because_of_pee() 87 | 88 | def _reduce_weight_because_of_pee(self): 89 | self.weight = self.weight - 0.1 90 | 91 | 92 | class Boy(Person): 93 | # REMIND 需要实现不同之处 94 | def pee(self): 95 | print(f'{self.name} 站着小便') 96 | super(Boy, self).pee() 97 | 98 | 99 | class Girl(Person): 100 | # REMIND 需要实现不同之处 101 | def pee(self): 102 | print(f'{self.name} 蹲着小便') 103 | super(Girl, self).pee() 104 | 105 | 106 | if __name__ == "__main__": 107 | # 在调用处只需要实例化一个对象,不需要学极端面向过程编程,保存很多个变量。 108 | xiaomin = Boy('小明', 120, 30) 109 | xiaowang = Boy('小王', 130, 32) 110 | xiaomin.grow_height(5) 111 | xiaomin.grow_weight(1) 112 | xiaomin.show_height() 113 | xiaomin.show_weight() 114 | xiaomin.pee() 115 | xiaomin.show_weight() 116 | # REMIND 这里实例化两个男孩,原因是模拟需要多实例,模拟每个对象的属性是互不干扰的,小明增长体重不会影响到小王的体重。 117 | # REMIND 如果是使用全局变量 + 函数 或者类名加静态属性(类属性),则会发生互相干扰,正因为这样行不通再加上不使用oop面向对象,就会造成需要写成在一组函数中频繁return和传参,实现上复杂曲折,也不好读懂。这种写法现象在可预订平台的酒店价格解析里面体现得十分之严重,由于多个函数中频繁传参和return,有的参数命名是同一个东西,但是在各个函数中形参的名字取得又不一样,不仅在维护代码时候难以搞懂,在实现的时候也是麻烦曲折很多。 118 | print('— ' * 30) 119 | xiaowang.show_height() 120 | xiaowang.show_weight() 121 | xiaowang.grow_height(6) 122 | xiaowang.grow_weight(2) 123 | xiaowang.show_height() 124 | xiaowang.show_weight() 125 | xiaowang.pee() 126 | xiaowang.show_weight() 127 | 128 | print('* ' * 30) 129 | xiaohong = Boy('小红', 110, 25) 130 | xiaohong.grow_height(3) 131 | xiaohong.grow_weight(0.5) 132 | xiaohong.show_height() 133 | xiaohong.show_weight() 134 | xiaohong.pee() 135 | xiaohong.show_weight() 136 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/频繁return和重复传参和复制全流程的写法/person_f.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | 4 | """ 5 | 演示使用极端面向过程来写,由于没有封装,没有成员变量和全局变量,所以造成需要在多个函数中频繁重复传参和return 6 | 无论是实现还是调用都更为复杂。 7 | """ 8 | 9 | 10 | def show_weight(name, weight): 11 | print(f'{name} 的体重是: {weight} 千克') 12 | 13 | 14 | def show_height(xingmin, height): # xingmin: xingmin也是代表姓名,故意弄成形参不是name,面向过程就会有这种搞不清楚一个参数在不同函数中是不是同一个意义的大缺点。 15 | print(f'{xingmin} 的身高是: {height} 厘米') 16 | 17 | 18 | def grow_weight(name, weight, growing_weight): 19 | weight += growing_weight 20 | print(f'{name} 的体重增加 {growing_weight} 千克') 21 | return weight # 必须把结果return到外部保存 22 | 23 | 24 | def grow_height(xinmin, height, growing_height): 25 | print(f'{xinmin} 的身高增加 {growing_height} 厘米') 26 | height += growing_height 27 | return height # 必须把结果return到外部保存 28 | 29 | 30 | 31 | def pee(name, weight): 32 | """ 33 | 因为小便会导致体重发生变化,必须把原来的体重传进来。 34 | """ 35 | print(f'{name} 站着小便') 36 | return _reduce_weight_because_of_pee(weight) # 必须把结果return到外部保存 37 | 38 | 39 | def _reduce_weight_because_of_pee(tizhong): 40 | tizhong = tizhong - 0.1 # 体重的别名tizhong和weight是一样的,面向过程不使用实例属性,在很多函数中大量重复传参,形参就会可能不一致,导致看不懂代码。 41 | return tizhong 42 | 43 | 44 | if __name__ == '__main__': 45 | # 极端面向过程不仅需要导致频繁传参和return,还要在调用时候设置很多个外部变量来保存状态,如果属性/状态越多,需要外部保存的变量就越多。 46 | xiaomin_name = '小明' 47 | xioamin_height = 120 48 | xiaomin_weight = 30 49 | show_height(xiaomin_name, xioamin_height) 50 | show_weight(xiaomin_name, xiaomin_weight) 51 | xioamin_height = grow_height(xiaomin_name, xioamin_height, 5) 52 | xiaomin_weight = grow_weight(xiaomin_name, xiaomin_weight, 1) # 体重发生了变化,必须把结果return出来保存 53 | show_height(xiaomin_name, xioamin_height) 54 | show_weight(xiaomin_name, xiaomin_weight) # 展示体重,需要把变化后的体重传进来 55 | xiaomin_weight = pee(xiaomin_name, xiaomin_weight) # 连上个厕所都需要把体重参数传进来,这样大量传参简直不要太low 56 | show_weight(xiaomin_name, xiaomin_weight) 57 | 58 | print('& ' * 100) 59 | 60 | # 频繁重复传参和retutn主要是为了多实例,不然全局变量加函数就搞定了。演示面向过程实现多实例的需求,由于各种变量都是在调用地方传入和保存,所以这种面向过程的地方可以实现多实例,为了实现多实例导致重复传参和频繁return,导致代码无论在本身实现写法还是调用上,都极为复杂。例如演示这种方式实现多实例小王 61 | 62 | # xiaowang_name = '小王' 63 | # xioamin_height = 130 64 | # xiaowang_weight = 35 65 | # show_height(xiaowang_name, xioamin_height) 66 | # show_weight(xiaowang_name, xiaowang_weight) 67 | # xioamin_height = grow_height(xiaowang_name, xioamin_height, 6) 68 | # xiaowang_weight = grow_weight(xiaowang_name, xiaowang_weight, 2) 69 | # show_height(xiaowang_name, xioamin_height) 70 | # show_weight(xiaowang_name, xiaowang_weight) 71 | # xiaowang_weight = pee(xiaowang_name, xiaowang_weight) 72 | # show_weight(xiaowang_name, xiaowang_weight) 73 | 74 | # 这样的写法,如果要实现小红,那就十分麻烦了,因为小红的pee函数肯定不能这么运行。如果直接在此处修改函数,那代码就不能兼容小明 小王。导致又要全盘复制文件,庵后扣字,来重写pee函数。 75 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/4种编程方式,描述一个人吃喝拉撒,(分男生 女生)/频繁return和重复传参和复制全流程的写法/person_f_girl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | 4 | """ 5 | 复制粘贴扣字实现不同之处 6 | 演示使用极端面向过程来写,由于没有封装,没有成员变量和全局变量,所以造成需要频繁传参和return.对于实现不同点全盘容易陷入复制文件全流程。 7 | 女得有的函数不同。 8 | 9 | """ 10 | 11 | 12 | def show_weight(name, weight): 13 | print(f'{name} 的体重是: {weight} 千克') 14 | 15 | 16 | def show_height(xingmin, height): # xingmin: xingmin也是代表姓名,故意弄成形参不是name,面向过程就会有这种搞不清楚一个参数在不同函数中是不是同一个意义的大缺点。 17 | print(f'{xingmin} 的身高是: {height} 厘米') 18 | 19 | 20 | def grow_weight(name, weight, growing_weight): 21 | weight += growing_weight 22 | print(f'{name} 的体重增加 {growing_weight} 千克') 23 | return weight # 必须把结果return到外部保存 24 | 25 | 26 | def grow_height(xinmin, height, growing_height): 27 | print(f'{xinmin} 的身高增加 {growing_height} 厘米') 28 | height += growing_height 29 | return height # 必须把结果return到外部保存 30 | 31 | 32 | def pee(name, weight): 33 | """ 34 | 因为小便会导致体重发生变化,必须把原来的体重传进来。 35 | """ 36 | print(f'{name} 蹲着小便') 37 | return _reduce_weight_because_of_pee(weight) # 必须把结果return到外部保存 38 | 39 | 40 | def _reduce_weight_because_of_pee(tizhong): 41 | tizhong = tizhong - 0.001 # 体重的别名tizhong和weight是一样的,面向过程不使用实例属性,在很多函数中大量重复传参,形参就会可能不一致,导致看不懂代码。 42 | return tizhong 43 | 44 | 45 | if __name__ == '__main__': 46 | xiaohong_name = '小红' 47 | xiaohong_height = 110 48 | xiaohong_weight = 25 49 | show_height(xiaohong_name, xiaohong_height) 50 | show_weight(xiaohong_name, xiaohong_weight) 51 | xiaohong_height = grow_height(xiaohong_name, xiaohong_height, 5) 52 | xiaohong_weight = grow_weight(xiaohong_name, xiaohong_weight, 1) # 体重发生了变化,必须把结果return出来保存 53 | show_height(xiaohong_name, xiaohong_height) 54 | show_weight(xiaohong_name, xiaohong_weight) # 展示体重,需要把变化后的体重传进来 55 | xiaohong_weight = pee(xiaohong_name, xiaohong_weight) # 连上个厕所都需要把体重参数传进来,然后还要保存新结果,这样大量传参简直不要太low 56 | show_weight(xiaohong_name, xiaohong_weight) 57 | 58 | 59 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/README.md: -------------------------------------------------------------------------------- 1 | ## 列举出4种编程方式,并介绍万能的oop 四步转化公式。 2 | ``` 3 | 包含4种编程写法 4 | 1)模块级 全局变量 + 函数 5 | 2)在一组函数中无限频繁传参 和return 6 | 3)无效装逼废物滑稽面向过程类伪oop 7 | 4) 真oop 8 | 使用4种编程方式来实现男生、女生吃喝拉撒长身体,并说明缺点。 9 | 10 | 总结oop 四步转化公式。oop转化公式能够做到万能通用,适用一切任意编程场景,能够大幅度减少编程难度和减少代码行数。 11 | 只要按照这个公式写代码,就不需要死记硬背套设计模式。 12 | 此种编程方式,可以使任意极端纯面向过程代码的文件和项目减少50%行至95%行,写法更直观,并达到流程级可复用。 13 | ``` 14 | 15 | ## oop四步走转化公式 16 | ``` 17 | 一、针对已有全局变量加函数无限复制粘贴扣字的写法, 18 | 0) 如果没有写成极致的全局变量加函数写法,就需要在脑袋里多多的抽取全局变量,全局变量多多益善(要大大减少反复return/传参)。 19 | 1) 模块级降级为类名 20 | 2) 全局变量改成实例属性 21 | 3) 函数改为方法 22 | 23 | 24 | 二、针对已有在一组函数中频繁return和大量重复传参的写法 25 | 1、转化公式1 (先在脑袋中把代码形式转化为全局变量加函数的写法,在使用公式一) 26 | 0)在脑袋中重构,把写法形式改成全局变量加函数的写法,此时不用担心全局变量是唯一的, 27 | 不用大量重复传参和return,所有需要传参和return的都想象成使用全局变量和操作全局变量。 28 | 1) 模块级降级为类名 29 | 2) 全局变量改成实例属性 30 | 3) 函数改为方法 31 | 后面三个步骤是一样的。全局变量变为实例属性后,每次实例化后每个对象的实例属性都是互不干扰的。 32 | 每个对象可以看作为一个模块级写法的 模块文件的无限深拷贝。 33 | 34 | 2、转化公式2 (直接转化,比较熟悉了之后) 35 | 1) 新增一个类 36 | 2)把重复传参和return的形参,全都去掉,抽取为改成实例属性 37 | 3) 函数改为方法。 38 | 39 | 三、新写代码: 40 | 可以使用老的编程方式在脑袋里写,然后使用oop转化公式。 41 | 也可以跳过老的编程方式,直接oop,最重要是实例属性多多益善(但不能没事乱把局部变量含义的东西也加self), 42 | 理解oop的多例。 43 | 44 | 四、 45 | 对任何面向过程写法,使用转化公式,一定就可以修改成oop,然来的代码思维不需要做任何改变, 46 | 只要按这个公式就可以改造。(前提是满足需要使用oop的两个条件,才需要改造) 47 | 48 | 转化oop要有灵魂的第0步,如果跳过此步骤就会想当然以为转化oop就是函数外面加个class关键字外壳,觉得转化没用。 49 | 那是因为你不按照我说的步骤走,造成写成了无效废物装逼滑稽面向过程类,当然会觉得不需要转化。oop要的是内涵不要搞形式外壳。 50 | 51 | 52 | ``` 53 | 54 | 55 | ## oop转化公式的重要本质需要理解的方面 56 | ``` 57 | 最重要是理解: 命名空间 全局变量 实例属性 多实例需求 函数和方法 的关系,搞清楚了, 58 | 写oop十分之简单,不会造成极端面向过程的曲折写法。 59 | 在word文档中会写更详细的解释。 60 | ``` 61 | 62 | ## 常见问题解答 63 | ``` 64 | 1、是不是所有代码都要用oop? 65 | 答:不是,按照上面的方式判断用那种方式好,再用oop转化公式,使用真正得oo编程。 66 | 如果只是对现有代码直接在函数上套个类名,函数改成方法,99%的情况下弄出来的只会是废物滑稽类,伪oop, 67 | 不能有写类就高大上能吓唬人装逼了这种想法。 68 | 69 | 2、函数和类上面区别? 70 | 没有区别,就像问人和走路有什么区别,猪和吃饭有什么区别,问得牛头不对马嘴的伪问题,函数和方法才可以比较。 71 | 类(对象)和模块才有可比性,必须要搞清楚原因,不然脑袋中有这个比较的想法那就不可能写得了oop。 72 | 73 | 面向过程是 文件模块名.eat(狗的名字,shit) 74 | oop是 狗.eat(shit) 75 | ``` 76 | 77 | ## python编程范式 78 | ``` 79 | 1.1 方式一,平铺指令。 从上往下叠加指令,适合简单的独立脚本。不需要和没机会被别的模块导入。 80 | 1.2 方式二,面向过程函数式编程。适合实现独立的转化功能,基本原理是要实现转化 y = f(x), 81 | 适合函数无依赖状态(不需要在多个函数中频繁的传入和return相同意义的参数)。 82 | 1.3 方式三,oop编程.适合多个函数间需要使用同一个变量,并且需要多实例(如果使在使用面向过程时候需要使用函数频繁的return各 83 | 种状态/变量由类外使用多个参数来保存这些值和传入这些值,那就是也判断为需要多实例),必须同时满足这两个条件,才使用oop好, 84 | 否则不需要oop。(但单例模式为了控制灵活的初始化传参,一般也用类的方式) 85 | 1.4 网上说的简单用面向过程,复杂的用面向对象,这简直是错误的废话。简单和复杂界定不了,即使是一个简单的查询价格,经过大量 86 | 平台的重写对比,oop都能比面向过程减少70%行以上的代码,所以用网上这句话来判断用什么方式来写代码是错误的。只要严格使用上面 87 | 描述的判断方式,就能很容易知道在什么场景什么时候使用哪种方式好了,不需要oop嗨写成类就是没理解好oop能更好地解决什么。 88 | 1.5 要多使用oop,但不要写成纯静态或者半静态的无效废物类。 面向过程一定可以搞定一切,但是实现麻烦、调用更麻烦,那就不适合 89 | 面向过程了。比如猴子补丁可以搞定继承,闭包可以搞定封装,但是没什么好处,实现麻烦。先要转oop,只有放弃极端面向过程,不对面 90 | 向过程过分的偏执和喜欢,才能开始学习更多设计模式。 91 | ``` 92 | -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/python编程范式培训及万能oop转化公式.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ydf0509/python36patterns/762b13aea5e5f52d670b3dc7564765db159a68a1/忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/python编程范式培训及万能oop转化公式.docx -------------------------------------------------------------------------------- /忘掉设计模式,使用最强最稳定最有套路的万能编程思维-python oop四步转化公式。/没有发明革命性的新功能的类.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | """ 4 | 封装的工具类没有发明非常实用的好功能,全都是自己取个新的方法名字调用官方的方法,没什么卵用,还要新记忆各种新的名字。还不如直接调用官方的算了。 5 | 比如套上享元模式 单例模式或者缓存模式来控制创建,或者在官方基础上增加个自动聚合数据库命令的,这样才可以,这样才有作用。 6 | """ 7 | from redis import Redis 8 | class CustomRedis(): 9 | def __init__(self,host,port): 10 | self._r = Redis(host,port) 11 | 12 | def my_exists(self,name): 13 | return self._r.exists(name) 14 | 15 | def my_hget(self,name,key): 16 | return self._r.hget(name,key) 17 | 18 | def my_sadd(self,name,value): 19 | return self._r.sadd(name,value) 20 | 21 | def my_lpush(self,name,value): 22 | return self._r.sadd(name,value) 23 | 24 | # 。。。。。。。。。。。。。。。。。。 -------------------------------------------------------------------------------- /结构型模式-mvc模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 21:23 4 | """ 5 | MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。 6 | 7 | Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。 8 | View(视图) - 视图代表模型包含的数据的可视化。 9 | Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。 10 | """ 11 | from monkey_print2 import print 12 | 13 | quotes = ('A man is not complete until he is married. Then he is finished.', 14 | 'As I said before, I never repeat myself.', 15 | 'Behind a successful man is an exhausted woman.', 16 | 'Black holes really suck...', 'Facts are stubborn things.') 17 | 18 | 19 | class QuoteModel: 20 | 21 | def get_quote(self, n): 22 | try: 23 | value = quotes[n] 24 | except IndexError as err: 25 | value = 'Not found!' 26 | return value 27 | 28 | 29 | class QuoteTerminalView: 30 | 31 | def show(self, quote): 32 | print('And the quote is: "{}"'.format(quote)) 33 | 34 | def error(self, msg): 35 | print('Error: {}'.format(msg)) 36 | 37 | def select_quote(self): 38 | return input('Which quote number would you like to see?') 39 | 40 | 41 | class QuoteTerminalController: 42 | 43 | def __init__(self): 44 | self.model = QuoteModel() 45 | self.view = QuoteTerminalView() 46 | 47 | def run(self): 48 | valid_input = False 49 | while not valid_input: 50 | n = self.view.select_quote() 51 | try: 52 | n = int(n) 53 | except ValueError as err: 54 | self.view.error("Incorrect index '{}'".format(n)) 55 | else: 56 | valid_input = True 57 | quote = self.model.get_quote(n) 58 | self.view.show(quote) 59 | 60 | 61 | def main(): 62 | controller = QuoteTerminalController() 63 | while True: 64 | controller.run() 65 | 66 | if __name__ == '__main__': 67 | main() -------------------------------------------------------------------------------- /结构型模式-代理模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 22:40 4 | 5 | """ 6 | 在某些应用中,我们想要在访问某个对象之前执行一个或多个重要的操作,例如,访问敏感 7 | 信息——在允许用户访问敏感信息之前,我们希望确保用户具备足够的权限。操作系统中也存在 8 | 类似的情况,用户必须具有管理员权限才能在系统中安装新程序。 9 | 上面提到的重要操作不一定与安全问题相关。延迟初始化 10 | 是另一个案例:我们想要把一个计算成本较高的对象的创建过程延迟到用户首次真正使用它时 11 | 才进行。 12 | 13 | 14 | 15 | 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 16 | 17 | 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 18 | """ 19 | 20 | 21 | class SensitiveInfo: 22 | 23 | def __init__(self): 24 | self.users = ['nick', 'tom', 'ben', 'mike'] 25 | 26 | def read(self): 27 | print('There are {} users: {}'.format(len(self.users), ' '.join(self.users))) 28 | 29 | def add(self, user): 30 | self.users.append(user) 31 | print('Added user {}'.format(user)) 32 | 33 | 34 | class Info: 35 | 36 | '''SensitiveInfo的保护代理''' 37 | 38 | def __init__(self): 39 | self.protected = SensitiveInfo() 40 | self.secret = '0xdeadbeef' 41 | 42 | def read(self): 43 | self.protected.read() 44 | 45 | def add(self, user): 46 | sec = input('what is the secret? ') 47 | self.protected.add(user) if sec == self.secret else print("That's wrong!") 48 | 49 | 50 | def main(): 51 | info = Info() 52 | while True: 53 | print('1. read list |==| 2. add user |==| 3. quit') 54 | key = input('choose option: ') 55 | if key == '1': 56 | info.read() 57 | elif key == '2': 58 | name = input('choose username: ') 59 | info.add(name) 60 | elif key == '3': 61 | exit() 62 | else: 63 | print('unknown option: {}'.format(key)) 64 | 65 | if __name__ == '__main__': 66 | main() -------------------------------------------------------------------------------- /结构型模式-外观模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 19:00 4 | """ 5 | 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。 6 | 7 | 这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。 8 | """ 9 | 10 | from monkey_print2 import print 11 | 12 | class A: 13 | def run(self): 14 | print('A run') 15 | 16 | def jump(self): 17 | print('A jump') 18 | 19 | 20 | class B: 21 | def run(self): 22 | print('B run') 23 | 24 | def jump(self): 25 | print('B jump') 26 | 27 | 28 | class C: 29 | def run(self): 30 | print('C run') 31 | 32 | def jump(self): 33 | print('C jump') 34 | 35 | 36 | class Facade: 37 | def __init__(self): 38 | self.a = A() 39 | self.b = B() 40 | self.c = C() 41 | 42 | def run(self): 43 | for item in ('a', 'b', 'c'): 44 | getattr(self, item).run() 45 | 46 | def jump(self): 47 | for item in ('a', 'b', 'c'): 48 | getattr(self, item).jump() 49 | 50 | 51 | if __name__ == '__main__': 52 | facade = Facade() 53 | facade.run() 54 | facade.jump() 55 | -------------------------------------------------------------------------------- /结构型模式-桥接模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 17:39 4 | """ 5 | 桥接,是像一座桥连接两岸,而Python程序设计中的桥接指的是抽象部分和实体部分的连接,简单来说是类和类实例化过称中的连接。 6 | 7 | 桥接模式通过在类和类实例化中间作用,使其抽象和实现可以独立变化而不互相干扰,这就是桥接模式最大的作用。 8 | 9 | 核心的思想是通过封装,将一个抽象类的相关参数和方法分别作为桥接类的属性,这样在实例化桥接类后通过修改桥接类的属性,便可以实现抽象和实现之间的独立变化。 10 | 11 | """ 12 | from monkey_print2 import print 13 | 14 | 15 | class A: 16 | def run(self, name): 17 | print("my name is :{}".format(name)) 18 | 19 | 20 | class B: 21 | def run(self, name): 22 | print("我的名字是:{}".format(name)) 23 | 24 | 25 | class Bridge: 26 | def __init__(self, ager, classname): 27 | self.ager = ager 28 | self.classname = classname 29 | 30 | def bridge_run(self): 31 | self.classname.run(self.ager) 32 | 33 | 34 | if __name__ == '__main__': 35 | test = Bridge('李华', A()) 36 | test.bridge_run() 37 | test.ager = 'Tome' 38 | test.bridge_run() 39 | test.classname = B() 40 | test.bridge_run() 41 | test.ager = '李华' 42 | test.bridge_run() 43 | """ 44 | "D:/coding2/python36patterns/结构型模式-桥接模式.py:7" 17:56:50 my name is :李华 45 | "D:/coding2/python36patterns/结构型模式-桥接模式.py:7" 17:56:50 my name is :Tome 46 | "D:/coding2/python36patterns/结构型模式-桥接模式.py:12" 17:56:50 我的名字是:Tome 47 | "D:/coding2/python36patterns/结构型模式-桥接模式.py:12" 17:56:50 我的名字是:李华 48 | 49 | """ 50 | -------------------------------------------------------------------------------- /结构型模式-组合模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 17:39 4 | """ 5 | 组合模式 6 | 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。 7 | 8 | 这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。 9 | 10 | 我们通过下面的实例来演示组合模式的用法。实例演示了一个组织中员工的层次结构。 11 | 12 | 介绍 13 | 意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 14 | 15 | 主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。 16 | 17 | 何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。 18 | 19 | 如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。 20 | 21 | 关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。 22 | 23 | 应用实例: 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。 24 | 25 | 优点: 1、高层模块调用简单。 2、节点自由增加。 26 | 27 | 缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。 28 | 29 | 使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。 30 | 31 | 注意事项:定义时为具体类。 32 | 33 | """ 34 | from monkey_print2 import print 35 | 36 | 37 | # Component:公司抽象类 38 | class Company: 39 | name = '' 40 | 41 | def __init__(self, name): 42 | self.name = name 43 | 44 | def add(self, company): 45 | pass 46 | 47 | def remove(self, company): 48 | pass 49 | 50 | def display(self, depth): 51 | pass 52 | 53 | def line_of_duty(self): # 履行职责 54 | pass 55 | 56 | # Composite:公司类 57 | 58 | 59 | class ConcreteCompany(Company): 60 | childrenCompany = None 61 | 62 | def __init__(self, name): 63 | Company.__init__(self, name) 64 | self.childrenCompany = [] 65 | 66 | def add(self, company): 67 | self.childrenCompany.append(company) 68 | 69 | def remove(self, company): 70 | self.childrenCompany.remove(company) 71 | 72 | def display(self, depth): 73 | print('-' * depth + self.name) 74 | 75 | for component in self.childrenCompany: 76 | component.display(depth + 2) 77 | 78 | def line_of_duty(self): # 履行职责 79 | for component in self.childrenCompany: 80 | component.line_of_duty() 81 | 82 | 83 | # Leaf:具体职能部门 84 | class HRDepartment(Company): 85 | def __init__(self, name): 86 | Company.__init__(self, name) 87 | 88 | def display(self, depth): 89 | print('-' * depth + self.name) 90 | 91 | def line_of_duty(self): # 履行职责 92 | print('%s\t员工招聘培训管理' % self.name) 93 | 94 | 95 | # Leaf:具体职能部门 96 | class FinanceDepartment(Company): 97 | def __init__(self, name): 98 | Company.__init__(self, name) 99 | 100 | def display(self, depth): 101 | print('-' * depth + self.name) 102 | 103 | def line_of_duty(self): # 履行职责 104 | print('%s\t公司财务收支管理' % self.name) 105 | 106 | 107 | 108 | 109 | if __name__ == '__main__': 110 | root = ConcreteCompany('北京总公司') 111 | root.add(HRDepartment('总公司人力资源部')) 112 | root.add(FinanceDepartment('总公司财务部')) 113 | 114 | comp = ConcreteCompany('华东分公司') 115 | comp.add(HRDepartment('华东分公司人力资源部')) 116 | comp.add(FinanceDepartment('华东分公司财务部')) 117 | root.add(comp) 118 | 119 | comp1 = ConcreteCompany('南京办事处') 120 | comp1.add(HRDepartment('南京办事处人力资源部')) 121 | comp1.add(FinanceDepartment('南京办事处财务部')) 122 | comp.add(comp1) 123 | 124 | comp2 = ConcreteCompany('杭州办事处') 125 | comp2.add(HRDepartment('杭州办事处人力资源部')) 126 | comp2.add(FinanceDepartment('杭州办事处财务部')) 127 | comp.add(comp2) 128 | 129 | print('-------公司结构图-------') 130 | root.display(1) 131 | 132 | print('\n-------职责-------') 133 | root.line_of_duty() 134 | 135 | 136 | -------------------------------------------------------------------------------- /结构型模式-装饰者模式-函数装饰器.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 18:32 4 | """ 5 | 函数装饰器 6 | """ 7 | import time 8 | from functools import wraps 9 | from monkey_print2 import print 10 | 11 | 12 | def timethis(func): 13 | ''' 14 | Decorator that reports the execution time. 15 | ''' 16 | 17 | @wraps(func) 18 | def wrapper(*args, **kwargs): 19 | start = time.time() 20 | result = func(*args, **kwargs) 21 | end = time.time() 22 | print(func.__name__, end - start) 23 | return result 24 | 25 | return wrapper 26 | 27 | 28 | @timethis 29 | def fun(): 30 | time.sleep(3) 31 | return 1 32 | 33 | 34 | if __name__ == '__main__': 35 | fun() 36 | -------------------------------------------------------------------------------- /结构型模式-装饰者模式-类装饰器.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 18:32 4 | """ 5 | 类装饰器 6 | """ 7 | import types 8 | from functools import wraps 9 | from monkey_print2 import print 10 | 11 | 12 | 13 | class Profiled: 14 | def __init__(self, func): 15 | wraps(func)(self) 16 | self.ncalls = 0 17 | 18 | def __call__(self, *args, **kwargs): 19 | self.ncalls += 1 20 | return self.__wrapped__(*args, **kwargs) 21 | 22 | def __get__(self, instance, cls): 23 | if instance is None: 24 | return self 25 | else: 26 | return types.MethodType(self, instance) 27 | 28 | @Profiled 29 | def add(x, y): 30 | return x + y 31 | 32 | class Spam: 33 | @Profiled 34 | def bar(self, x): 35 | print(self, x) 36 | 37 | if __name__ == '__main__': 38 | add(2, 3) 39 | add(4, 5) 40 | print(add.ncalls) 41 | 42 | s = Spam() 43 | s.bar(1) 44 | s.bar(2) 45 | s.bar(3) 46 | print(Spam.bar.ncalls) 47 | 48 | -------------------------------------------------------------------------------- /结构型模式-装饰者模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 18:24 4 | 5 | """ 6 | 无论何时我们想对一个对象添加额外的功能,都有下面这些不同的可选方法。 7 |  如果合理,可以直接将功能添加到对象所属的类(例如,添加一个新的方法) 8 |  使用组合 9 |  使用继承 10 | 与继承相比,通常应该优先选择组合,因为继承使得代码更难复用,继承关系是静态的,并且应用于整个类以及这个类的所有实例(请参考[GOF95,第31页]和网页[t.cn/RqrC8Yo])。 11 | 设计模式为我们提供第四种可选方法,以支持动态地(运行时)扩展一个对象的功能,这种方法就是修饰器。修饰器(Decorator)模式能够以透明的方式(不会影响其他对象)动态地将功能添加到一个对象中(请参考[GOF95,第196页])。 12 | 在许多编程语言中,使用子类化(继承)来实现修饰器模式(请参考[GOF95,第198页])。 13 | 在Python中,我们可以(并且应该)使用内置的修饰器特性。一个Python修饰器就是对Python语法的一个特定改变,用于扩展一个类、方法或函数的行为,而无需使用继承。从实现的角度来说, 14 | Python修饰器是一个可调用对象(函数、方法、类),接受一个函数对象fin作为输入,并返回另一个函数对象 。这意味着可以将任何具有这些属性的可调用对象当作一个修饰器。在第1章和第2章中已经看到如何使用内置的property修饰器让一个方法表现为一个变量。在5.4节,我们将学习如何实现及使用我们自己的修饰器。 15 | 修饰器模式和Python修饰器之间并不是一对一的等价关系。Python修饰器能做的实际上比修饰器模式多得多,其中之一就是实现修饰器模式 16 | """ 17 | from monkey_print2 import print 18 | 19 | class Foo: 20 | def f1(self): 21 | print("original f1") 22 | 23 | def f2(self): 24 | print("original f2") 25 | 26 | 27 | class Foo_decorator: 28 | def __init__(self, decoratee): 29 | self._decoratee = decoratee 30 | 31 | def f1(self): 32 | print("before run f1") 33 | self._decoratee.f1() 34 | print("after run f1") 35 | 36 | def __getattr__(self, name): 37 | return getattr(self._decoratee, name) 38 | 39 | if __name__ == '__main__': 40 | u = Foo() 41 | v = Foo_decorator(u) 42 | v.f1() 43 | v.f2() -------------------------------------------------------------------------------- /结构型模式-适配器模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 17:39 4 | """ 5 | 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 6 | 7 | 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。 8 | 9 | """ 10 | from monkey_print2 import print 11 | 12 | 13 | class Dog: 14 | def __init__(self, name): 15 | self.name = name 16 | 17 | def wangwang(self): 18 | print('my name is' + self.name + '。。。汪汪汪。。。') 19 | 20 | def dog_run(self): 21 | print(f'{self.name} is running') 22 | 23 | 24 | class Cat: 25 | def __init__(self, name): 26 | self.name = name 27 | 28 | def miaomiao(self): 29 | print('my name is' + self.name + '。。。喵喵喵。。。') 30 | 31 | def cat_run(self): 32 | print(f'{self.name} is running') 33 | 34 | 35 | class Sheep: 36 | def __init__(self, name): 37 | self.name = name 38 | 39 | def miemie(self): 40 | print('my name is' + self.name + '。。。咩咩。。。') 41 | 42 | def sheet_run(self): 43 | print(f'{self.name} is running') 44 | 45 | 46 | class Adapter: 47 | def __init__(self, adapted_methods): 48 | 49 | self.__dict__.update(adapted_methods) 50 | 51 | def speak(self): 52 | pass 53 | 54 | def run(self): 55 | pass 56 | 57 | 58 | def main(): 59 | animals = [] 60 | dog = Dog('旺财') 61 | cat = Cat('大脸猫') 62 | sheep = Sheep('喜洋洋') 63 | animals.append(Adapter({'speak': dog.wangwang, 'run': dog.dog_run})) 64 | animals.append(Adapter({'speak': cat.miaomiao, 'run': cat.cat_run})) 65 | animals.append(Adapter({'speak': sheep.miemie, 'run': sheep.sheet_run})) 66 | 67 | for a in animals: 68 | a.speak() 69 | a.run() 70 | 71 | 72 | if __name__ == "__main__": 73 | main() 74 | -------------------------------------------------------------------------------- /行为型模式-中介者模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 11:13 4 | 5 | """ 6 | 中介者模式(Mediator Pattern):用一个对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使耦合松散,而且可以独立地改变它们之间的交互. 7 | """ 8 | from monkey_print2 import print 9 | class ChatRoom: 10 | @staticmethod 11 | def show_message(user,msg): 12 | print(f'{user.name} 说: {msg}') 13 | 14 | class User: 15 | def __init__(self,name): 16 | self.name = name 17 | 18 | def send_msg(self,msg): 19 | ChatRoom.show_message(self,msg) 20 | 21 | if __name__ == '__main__': 22 | user1 = User('小明') 23 | user2 = User('小红') 24 | user1.send_msg('早上好') 25 | user2.send_msg('晚上好') 26 | 27 | """ 28 | "D:/coding2/python36patterns/行为型模式-中介者模式.py:12" 11:21:04 小明 说: 早上好 29 | "D:/coding2/python36patterns/行为型模式-中介者模式.py:12" 11:21:04 小红 说: 晚上好 30 | """ -------------------------------------------------------------------------------- /行为型模式-发布订阅模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 10:39 4 | 5 | """重要程度 ☆☆☆ 6 | 订阅-发布模式和观察者模式概念相似,但在订阅-发布模式中,订阅者和发布者之间多了一层中间件:一个被抽象出来的信息调度中心。 7 | 8 | 但其实没有必要太深究 2 者区别,因为《Head First 设计模式》这本经典书都写了:发布+订阅=观察者模式。其核心思想是状态改变和发布通知。在此基础上,根据语言特性,进行实现即可。 9 | 10 | 对比观察者模式看。23种经典设计模式里面不包括发布订阅设计模式,区别很小。 11 | """ 12 | 13 | class Event: 14 | def __init__(self): 15 | self.client_list = {} 16 | 17 | def listen(self, key, fn): 18 | if key not in self.client_list: 19 | self.client_list[key] = [] 20 | self.client_list[key].append(fn) 21 | 22 | def trigger(self, *args, **kwargs): 23 | fns = self.client_list[args[0]] 24 | 25 | length = len(fns) 26 | if not fns or length == 0: 27 | return False 28 | 29 | for fn in fns: 30 | fn(*args[1:], **kwargs) 31 | 32 | return False 33 | 34 | def remove(self, key, fn): 35 | if key not in self.client_list or not fn: 36 | return False 37 | 38 | fns = self.client_list[key] 39 | length = len(fns) 40 | 41 | for _fn in fns: 42 | if _fn == fn: 43 | fns.remove(_fn) 44 | 45 | return True 46 | 47 | 48 | # 借助继承为对象安装 发布-订阅 功能 49 | class SalesOffice(Event): 50 | def __init__(self): 51 | super().__init__() 52 | 53 | 54 | # 根据自己需求定义一个函数:供事件处理完后调用 55 | def handle_event(event_name): 56 | def _handle_event(*args, **kwargs): 57 | print("Price is", *args, "at", event_name) 58 | 59 | return _handle_event 60 | 61 | 62 | if __name__ == "__main__": 63 | # 创建2个回调函数 64 | fn1 = handle_event("event01") 65 | fn2 = handle_event("event02") 66 | 67 | sales_office = SalesOffice() 68 | 69 | # 订阅event01 和 event02 这2个事件,并且绑定相关的 完成后的函数 70 | sales_office.listen("event01", fn1) 71 | sales_office.listen("event02", fn2) 72 | 73 | # 当两个事件完成时候,触发前几行绑定的相关函数 74 | sales_office.trigger("event01", 1000) 75 | sales_office.trigger("event02", 2000) 76 | 77 | sales_office.remove("event01", fn1) 78 | 79 | # 打印:False 80 | print(sales_office.trigger("event01", 1000)) 81 | -------------------------------------------------------------------------------- /行为型模式-命令模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 9:29 4 | """ 5 | 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。 6 | 7 | 8 | 现在多数应用都有撤销操作。虽然难以想象,但在很多年里,任何软件中确实都不存在撤销 9 | 操作。撤销操作是在1974年引入的(请参考网页[t.cn/Rqr3N22]),但Fortran和Lisp分别早在1957 10 | 年和1958年就已创建了撤销操作(请参考网页[t.cn/Rqr3067]),这两门语言仍在被人广泛使用。 11 | 在那些年里,我真心不想使用应用软件。犯了一个错误,用户也没什么便捷方式能修正它。 12 | """ 13 | import os 14 | 15 | verbose = True 16 | 17 | 18 | class RenameFile: 19 | 20 | def __init__(self, path_src, path_dest): 21 | self.src, self.dest = path_src, path_dest 22 | 23 | def execute(self): 24 | if verbose: 25 | print("[renaming '{}' to '{}']".format(self.src, self.dest)) 26 | os.rename(self.src, self.dest) 27 | 28 | def undo(self): 29 | if verbose: 30 | print("[renaming '{}' back to '{}']".format(self.dest, self.src)) 31 | os.rename(self.dest, self.src) 32 | 33 | 34 | class CreateFile: 35 | 36 | def __init__(self, path, txt='hello world\n'): 37 | self.path, self.txt = path, txt 38 | 39 | def execute(self): 40 | if verbose: 41 | print("[creating file '{}']".format(self.path)) 42 | with open(self.path, mode='w', encoding='utf-8') as out_file: 43 | out_file.write(self.txt) 44 | 45 | def undo(self): 46 | delete_file(self.path) 47 | 48 | 49 | class ReadFile: 50 | 51 | def __init__(self, path): 52 | self.path = path 53 | 54 | def execute(self): 55 | if verbose: 56 | print("[reading file '{}']".format(self.path)) 57 | with open(self.path, mode='r', encoding='utf-8') as in_file: 58 | print(in_file.read(), end='') 59 | 60 | 61 | def delete_file(path): 62 | if verbose: 63 | print("deleting file '{}'".format(path)) 64 | os.remove(path) 65 | 66 | 67 | def main(): 68 | orig_name, new_name = 'file1', 'file2' 69 | 70 | commands = [] 71 | for cmd in CreateFile(orig_name), ReadFile(orig_name), RenameFile(orig_name, new_name): 72 | commands.append(cmd) 73 | 74 | [c.execute() for c in commands] 75 | 76 | answer = input('reverse the executed commands? [y/n] ') 77 | 78 | if answer not in 'yY': 79 | print("the result is {}".format(new_name)) 80 | exit() 81 | 82 | for c in reversed(commands): 83 | try: 84 | c.undo() 85 | except AttributeError as e: 86 | pass 87 | 88 | 89 | if __name__ == '__main__': 90 | main() 91 | -------------------------------------------------------------------------------- /行为型模式-备忘录模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 11:22 4 | 5 | """ 6 | 备忘录模式 7 | 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 8 | 9 | 介绍 10 | 意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 11 | 12 | 主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。 13 | 14 | 何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。 15 | 16 | 如何解决:通过一个备忘录类专门存储对象状态。 17 | 18 | 关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。 19 | 20 | 应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理。 21 | 22 | 优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。 23 | 24 | 缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。 25 | 26 | 使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。 27 | 28 | 注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。 29 | """ 30 | from monkey_print2 import print 31 | #!/usr/bin/env python 32 | # -*- coding:utf-8 -*- 33 | 34 | __author__ = 'Andy' 35 | """ 36 | 大话设计模式 37 | 设计模式——备忘录模式 38 | 备忘录模式(Memento Pattern):不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样已经后就可将该对象恢复到原先保存的状态 39 | """ 40 | from monkey_print2 import print 41 | # 发起人类 42 | class Originator(object): 43 | 44 | def __init__(self, state): 45 | self.state = state 46 | 47 | def create_memento(self): 48 | return Memento(self.state) 49 | 50 | def set_memento(self, memento): 51 | self.state = memento.state 52 | 53 | def show(self): 54 | print("当前状态 ", self.state) 55 | 56 | # 备忘录类 57 | class Memento(object): 58 | 59 | def __init__(self, state): 60 | self.state = state 61 | 62 | # 管理者类 63 | class Caretaker(object): 64 | 65 | def __init__(self,memento): 66 | self.memento = memento 67 | 68 | 69 | 70 | if __name__ == "__main__": 71 | # 初始状态 72 | originator = Originator(state='On') 73 | originator.show() 74 | # 备忘录 75 | caretaker = Caretaker(originator.create_memento()) 76 | # 修改状态 77 | originator.state = 'Off' 78 | originator.show() 79 | # 复原状态 80 | originator.set_memento(caretaker.memento) 81 | originator.show() -------------------------------------------------------------------------------- /行为型模式-延迟计算缓存模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/8 0008 17:12 4 | """ 5 | 23种设计模式中没有提到这一种。 6 | 用于执行代价比较大的场景。可以选择永久缓存或者缓存一段时间。 7 | 8 | """ 9 | import sys 10 | import time 11 | from functools import wraps 12 | 13 | from monkey_print2 import print 14 | 15 | class cached_class_property(object): 16 | """类属性缓存装饰器,把方法的结果缓存为类属性""" 17 | 18 | def __init__(self, func): 19 | self.func = func 20 | 21 | def __get__(self, obj, cls): 22 | if obj is None: 23 | return self 24 | value = self.func(obj) 25 | setattr(cls, self.func.__name__, value) 26 | return value 27 | 28 | 29 | # noinspection PyPep8Naming 30 | class cached_instance_property(object): 31 | """实例属性缓存装饰器,把方法的结果缓存为实例属性""" 32 | 33 | def __init__(self, func): 34 | self.func = func 35 | 36 | def __get__(self, obj, cls): 37 | print(obj, cls) 38 | if obj is None: 39 | return self 40 | value = obj.__dict__[self.func.__name__] = self.func(obj) 41 | return value 42 | 43 | 44 | 45 | class FunctionResultCacher: 46 | """ 47 | 使函数的结果缓存指定的时间。例如在5分钟内已经查询了深圳的天气,再次调用查天气的函数直接返回之前查询的天气。 48 | 49 | """ 50 | func_result_dict = {} 51 | """ 52 | { 53 | (f1,(1,2,3,4)):(10,1532066199.739), 54 | (f2,(5,6,7,8)):(26,1532066211.645), 55 | } 56 | """ 57 | 58 | @classmethod 59 | def cached_function_result_for_a_time(cls, cache_time: float): 60 | """ 61 | 函数的结果缓存一段时间装饰器,不要装饰在返回结果是超大字符串或者其他占用大内存的数据结构上的函数上面。 62 | :param cache_time :缓存的时间 63 | :type cache_time : float 64 | """ 65 | 66 | def _cached_function_result_for_a_time(fun): 67 | 68 | @wraps(fun) 69 | def __cached_function_result_for_a_time(*args, **kwargs): 70 | # print(cls.func_result_dict) 71 | # if len(cls.func_result_dict) > 1024: 72 | if sys.getsizeof(cls.func_result_dict) > 100 * 1000 * 1000: 73 | cls.func_result_dict.clear() 74 | 75 | key = cls._make_arguments_to_key(args, kwargs) 76 | if (fun, key) in cls.func_result_dict and time.time() - cls.func_result_dict[(fun, key)][1] < cache_time: 77 | return cls.func_result_dict[(fun, key)][0] 78 | else: 79 | print('函数 [{}] 此次不能使用缓存'.format(fun.__name__)) 80 | result = fun(*args, **kwargs) 81 | cls.func_result_dict[(fun, key)] = (result, time.time()) 82 | return result 83 | 84 | return __cached_function_result_for_a_time 85 | 86 | return _cached_function_result_for_a_time 87 | 88 | @staticmethod 89 | def _make_arguments_to_key(args, kwds): 90 | key = args 91 | if kwds: 92 | sorted_items = sorted(kwds.items()) 93 | for item in sorted_items: 94 | key += item 95 | return key # 元祖可以相加。 -------------------------------------------------------------------------------- /行为型模式-模板模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 10:28 4 | """重要程度 ☆☆☆☆☆ 5 | 非常简单自然的一种设计模式,说白了就是继承。策略模式说罢了就是组合。 6 | 配合oop 4步转化公式,所向披靡,适用范围广。 7 | 8 | 1.场景: 9 | 10 |                         1.1 当多个算法或类实现类似或相同逻辑的时候。 11 | 12 |                         1.2 在子类中实现算法有助于减少重复代码的时候。 13 | 14 |                         1.3 可以让子类利用覆盖实现行为来定义多个算法的时候。 15 | 16 | 2.目的: 17 | 18 |                         2.1 使用基本操作定义算法的框架。 19 | 20 |                         2.2 重新定义子类的某些操作,而无需修改算法的结构。 21 | 22 |                         2.3 实现代码重用并避免重复工作 23 | 24 |                         2.4 利用通用接口或实现 25 | 26 | """ 27 | 28 | from abc import ABCMeta, abstractmethod 29 | from monkey_print2 import print 30 | 31 | 32 | # 抽象方法 AbstractClass 33 | class AbstractClass(metaclass=ABCMeta): 34 | def __init__(self): 35 | pass 36 | 37 | @abstractmethod 38 | def operation1(self): 39 | pass 40 | 41 | @abstractmethod 42 | def operation2(self): 43 | pass 44 | 45 | def operation3(self): 46 | print('操作3') 47 | 48 | # 模板方法 tmplate_method() 49 | def template_method(self): 50 | print("Defining the Algorithm.Operation1 follows Operation2") 51 | self.operation2() 52 | self.operation1() 53 | self.operation3() 54 | 55 | 56 | # 具体类 ConcreteClass 57 | class ConcreteClass(AbstractClass): 58 | def operation1(self): 59 | print("My Concrete Operation1") 60 | 61 | def operation2(self): 62 | print("Operation 2 remains same") 63 | 64 | 65 | if __name__ == '__main__': 66 | concreate = ConcreteClass() 67 | concreate.template_method() 68 | 69 | """ 70 | "D:/coding2/python36patterns/行为型模式-模板模式.py:47" 10:35:10 Defining the Algorithm.Operation1 follows Operation2 71 | "D:/coding2/python36patterns/行为型模式-模板模式.py:59" 10:35:10 Operation 2 remains same 72 | "D:/coding2/python36patterns/行为型模式-模板模式.py:56" 10:35:10 My Concrete Operation1 73 | "D:/coding2/python36patterns/行为型模式-模板模式.py:43" 10:35:10 操作3 74 | 75 | """ 76 | -------------------------------------------------------------------------------- /行为型模式-状态模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 10:08 4 | """ 5 | 在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。 6 | 7 | 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。 8 | """ 9 | 10 | 11 | class State(object): 12 | """Base state. This is to share functionality""" 13 | 14 | def scan(self): 15 | """Scan the dial to the next station""" 16 | self.pos += 1 17 | if self.pos == len(self.stations): 18 | self.pos = 0 19 | print("Scanning... Station is", self.stations[self.pos], self.name) 20 | 21 | 22 | class AmState(State): 23 | def __init__(self, radio): 24 | self.radio = radio 25 | self.stations = ["1250", "1380", "1510"] 26 | self.pos = 0 27 | self.name = "AM" 28 | 29 | def toggle_amfm(self): 30 | print("Switching to FM") 31 | self.radio.state = self.radio.fmstate 32 | 33 | 34 | class FmState(State): 35 | def __init__(self, radio): 36 | self.radio = radio 37 | self.stations = ["81.3", "89.1", "103.9"] 38 | self.pos = 0 39 | self.name = "FM" 40 | 41 | def toggle_amfm(self): 42 | print("Switching to AM") 43 | self.radio.state = self.radio.amstate 44 | 45 | 46 | class Radio(object): 47 | """A radio. It has a scan button, and an AM/FM toggle switch.""" 48 | 49 | def __init__(self): 50 | """We have an AM state and an FM state""" 51 | self.amstate = AmState(self) 52 | self.fmstate = FmState(self) 53 | self.state = self.amstate 54 | 55 | def toggle_amfm(self): 56 | self.state.toggle_amfm() 57 | 58 | def scan(self): 59 | self.state.scan() 60 | 61 | 62 | # Test our radio out 63 | if __name__ == '__main__': 64 | radio = Radio() 65 | actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2 66 | actions = actions * 2 67 | 68 | for action in actions: 69 | action() 70 | 71 | """ 72 | Scanning... Station is 1380 AM 73 | Scanning... Station is 1510 AM 74 | Switching to FM 75 | Scanning... Station is 89.1 FM 76 | Scanning... Station is 103.9 FM 77 | Scanning... Station is 81.3 FM 78 | Scanning... Station is 89.1 FM 79 | Switching to AM 80 | Scanning... Station is 1250 AM 81 | Scanning... Station is 1380 AM 82 | """ -------------------------------------------------------------------------------- /行为型模式-登记模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 13:53 4 | """ 5 | 登记模式。用这个可以兼顾单例模式的创建和管理。 6 | """ 7 | class RegistryHolder(type): 8 | 9 | REGISTRY = {} 10 | 11 | def __new__(cls, name, bases, attrs): 12 | new_cls = type.__new__(cls, name, bases, attrs) 13 | """ 14 | Here the name of the class is used as key but it could be any class 15 | parameter. 16 | """ 17 | cls.REGISTRY[new_cls.__name__] = new_cls 18 | return new_cls 19 | 20 | @classmethod 21 | def get_registry(cls): 22 | return dict(cls.REGISTRY) 23 | 24 | 25 | class BaseRegisteredClass(metaclass=RegistryHolder): 26 | """ 27 | Any class that will inherits from BaseRegisteredClass will be included 28 | inside the dict RegistryHolder.REGISTRY, the key being the name of the 29 | class and the associated value, the class itself. 30 | """ 31 | 32 | 33 | if __name__ == "__main__": 34 | print(sorted(RegistryHolder.REGISTRY)) 35 | 36 | class ClassRegistree(BaseRegisteredClass): 37 | def __init__(self, *args, **kwargs): 38 | pass 39 | 40 | 41 | print(sorted(RegistryHolder.REGISTRY)) -------------------------------------------------------------------------------- /行为型模式-策略模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 10:12 4 | """重要程度 ☆☆☆☆ 5 | 6 | 策略模式(Strategy pattern)鼓励使用多种算法来解决一个问题,其杀手级特性是能够在运 7 | 行时透明地切换算法(客户端代码对变化无感知)。因此,如果你有两种算法,并且知道其中一 8 | 种对少量输入效果更好,另一种对大量输入效果更好,则可以使用策略模式在运行时基于输入数 9 | 据决定使用哪种算法 10 | 11 | 以下演示策略类。在python中函数也是一等公民。简单情况下的策略模式将函数本身作为另一个函数/方法的入参即可。 12 | 13 | """ 14 | 15 | from monkey_print2 import print 16 | 17 | # 策略模式 18 | class Strategy(): 19 | def process(self): 20 | pass 21 | 22 | 23 | class FaultStrategy(Strategy): 24 | def process(self): 25 | print("fault") 26 | 27 | 28 | class NormalStrategy(Strategy): 29 | def process(self): 30 | print("normal") 31 | 32 | 33 | class Park(): 34 | def __init__(self, strategy): 35 | self.__strategy = strategy 36 | 37 | def geoProcess(self): 38 | self.__strategy.process() 39 | 40 | 41 | if __name__ == '__main__': 42 | p = Park(NormalStrategy()) 43 | p.geoProcess() 44 | p = Park(FaultStrategy()) 45 | p.geoProcess() 46 | 47 | """ 48 | "D:/coding2/python36patterns/行为型模式-策略模式.py:30" 10:23:25 normal 49 | "D:/coding2/python36patterns/行为型模式-策略模式.py:25" 10:23:25 fault 50 | """ -------------------------------------------------------------------------------- /行为型模式-观察者模式-重新实现日志系统.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 9:39 4 | 5 | """重要程度 ☆☆☆ 6 | 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。 7 | 有时,我们希望在一个对象的状态改变时更新另外一组对象。 8 | 9 | 说这么多抽象的概念,说点具体的就是以日志为例,有人把print当做日志用,里面的区别相当大,日志不仅可以有streamhandler和filehandler,还有mailhandler httphandler 10 | 等几亿种自定义handler。logger debug时候自动触发各种handler的emit方法。 11 | 用不会日志不理解日志,对logger addHandler各种handler懵逼不知道在干嘛,最主要是不懂观察者模式造成的。 12 | 希望举得例子是接近真实有意义,下面简单使用观察者模式重新模拟实现一个伪日志包。 13 | """ 14 | import abc 15 | from monkey_print2 import print 16 | 17 | 18 | class AbstractHandler(metaclass=abc.ABCMeta): 19 | @abc.abstractmethod 20 | def emit(self, record): 21 | pass 22 | 23 | 24 | class Logger: 25 | def __init__(self, logger_name): 26 | self.name = logger_name 27 | self.handlers = [] 28 | 29 | def add_handler(self, handler): 30 | self.handlers.append(handler) 31 | 32 | def log(self, record: str): 33 | for hr in self.handlers: 34 | hr.emit(f'{self.name} -- {record}') 35 | 36 | 37 | class StreamHandler(AbstractHandler): 38 | def emit(self, record): 39 | print(f' {record} 控制台打印') 40 | 41 | 42 | class FileHandler(AbstractHandler): 43 | def emit(self, record): 44 | print(f' {record} 文件写入') # 只是为了演示写入文件,用print模拟的伪实现。希望要搞清楚这里的目的不是print。 45 | 46 | 47 | class MailHandler(AbstractHandler): 48 | def emit(self, record): 49 | print(f' {record} 发邮件给某人') # 只是为了演示发邮件,用print模拟的伪实现。希望要搞清楚这里的目的不是print。 50 | 51 | 52 | class DingdingHandler(AbstractHandler): 53 | def emit(self, record): 54 | print(f' {record} 钉钉机器人将这句话发给群里') # 只是为了演示发钉钉机器人消息,用print模拟的伪实现。希望要搞清楚这里的目的不是print。 55 | 56 | 57 | if __name__ == '__main__': 58 | logger1 = Logger('a') 59 | logger1.add_handler(StreamHandler()) 60 | logger1.add_handler(FileHandler()) 61 | logger1.add_handler(MailHandler()) 62 | logger1.log('啦啦啦啦啦啦') 63 | 64 | logger2 = Logger('b') 65 | logger2.add_handler(StreamHandler()) 66 | logger2.add_handler(DingdingHandler()) 67 | logger2.log('哈哈哈哈哈哈') 68 | 69 | """ 70 | 可以看到日志非常灵活,可以按需选择几个handler,例如日志a会 控制台打印版 写入文件 发邮件,日志b控制台打印 发钉钉消息。 71 | "D:/coding2/python36patterns/行为型模式-观察者模式.py:36" 10:00:19 a -- 啦啦啦啦啦啦 控制台打印 72 | "D:/coding2/python36patterns/行为型模式-观察者模式.py:40" 10:00:19 a -- 啦啦啦啦啦啦 文件写入 73 | "D:/coding2/python36patterns/行为型模式-观察者模式.py:45" 10:00:19 a -- 啦啦啦啦啦啦 发邮件给某人 74 | "D:/coding2/python36patterns/行为型模式-观察者模式.py:36" 10:00:19 b -- 哈哈哈哈哈哈 控制台打印 75 | "D:/coding2/python36patterns/行为型模式-观察者模式.py:49" 10:00:19 b -- 哈哈哈哈哈哈 钉钉机器人将这句话发给群里 76 | """ 77 | -------------------------------------------------------------------------------- /行为型模式-解释器模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 9:35 4 | """ 5 | 重要程度 ☆☆ 6 | 7 | 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。 8 | 9 | 对每个应用来说,至少有以下两种不同的用户分类。 10 |  基本用户:这类用户只希望能够凭直觉使用应用。他们不喜欢花太多时间配置或学习应 11 | 用的内部。对他们来说,基本的用法就足够了。 12 |  高级用户:这些用户,实际上通常是少数,不介意花费额外的时间学习如何使用应用的 13 | 高级特性。如果知道学会之后能得到以下好处,他们甚至会去学习一种配置(或脚本) 14 | 语言。 15 |  能够更好地控制一个应用 16 |  以更好的方式表达想法 17 |  提高生产力 18 | 解释器(Interpreter)模式仅能引起应用的高级用户的兴趣。这是因为解释器模式背后的主 19 | 要思想是让非初级用户和领域专家使用一门简单的语言来表达想法。然而,什么是一种简单的语 20 | 言?对于我们的需求来说,一种简单的语言就是没编程语言那么复杂的语言 21 | """ 22 | from monkey_print2 import print 23 | 24 | 25 | class PlayContext(): 26 | play_text = None 27 | 28 | 29 | class Expression(): 30 | def interpret(self, context): 31 | if len(context.play_text) == 0: 32 | return 33 | else: 34 | play_segs = context.play_text.split(" ") 35 | for play_seg in play_segs: 36 | pos = 0 37 | for ele in play_seg: 38 | if ele.isalpha(): 39 | pos += 1 40 | continue 41 | break 42 | play_chord = play_seg[0:pos] 43 | play_value = play_seg[pos:] 44 | self.execute(play_chord, play_value) 45 | 46 | def execute(self, play_key, play_value): 47 | pass 48 | 49 | 50 | class NormGuitar(Expression): 51 | def execute(self, key, value): 52 | print("Normal Guitar Playing--Chord:%s Play Tune:%s" % (key, value)) 53 | 54 | 55 | if __name__ == "__main__": 56 | context = PlayContext() 57 | context.play_text = "C53231323 Em43231323 F43231323 G63231323" 58 | guitar = NormGuitar() 59 | guitar.interpret(context) 60 | """ 61 | "D:/coding2/python36patterns/行为型模式-解释器模式.py:29" 09:37:28 Normal Guitar Playing--Chord:C Play Tune:53231323 62 | "D:/coding2/python36patterns/行为型模式-解释器模式.py:29" 09:37:28 Normal Guitar Playing--Chord:Em Play Tune:43231323 63 | "D:/coding2/python36patterns/行为型模式-解释器模式.py:29" 09:37:28 Normal Guitar Playing--Chord:F Play Tune:43231323 64 | "D:/coding2/python36patterns/行为型模式-解释器模式.py:29" 09:37:28 Normal Guitar Playing--Chord:G Play Tune:63231323 65 | """ 66 | -------------------------------------------------------------------------------- /行为型模式-访问者模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 11:31 4 | 5 | """ 6 | 1。为什么要使用设计模式? 7 | 从理论上来说,设计模式是对程序问题比较好的解决方案。无数的程序员都曾经遇到过这些问题,并且他们使用这些解决方案去处理这些问题。所以当你遇到同样的问题,为什么要去想着创建一个解决方案而不是用现成的并且被证明是有效的呢? 8 | 9 | 10 | 11 | 2。访问者模式解决了什么问题? 12 | 1)对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 13 | 2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。 14 | 15 | 16 | 17 | 3。访问者模式使用场景 18 | 1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作 19 | 20 | 2)需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。 21 | 22 | 比如:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。 23 | 24 | 25 | 26 | 数据类只提供一个数据处理的接口,而数据类的处理方法我们叫它访问者 27 | 28 | 下面例子可以实现:安排不同年份的财务报表给不同的角色分析,这就是访问者模式的魅力;访问者模式的核心是在保持原有数据结构的基础上,实现多种数据的处理方法,该方法的角色就是访问者。 29 | 30 | 31 | 32 | 4。访问者模式优点 33 | 1)使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。 34 | 35 | 2)添加新的操作或者说访问者会非常容易。 36 | 37 | 3)将对各个元素的一组操作集中在一个访问者类当中。 38 | 39 | 4)使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。 40 | 41 | 5)可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。 42 | 6)如果操作的逻辑改变,我们只需要改变访问者的实现就够了,而不用去修改其他所有的商品类。 43 | 44 | 7)添加新类别的商品到系统变得容易。只需要改变一下访问者接口以及其实现。已经存在的商品类别不会被干扰影响。 45 | 46 | 47 | 48 | 5。访问者模式缺点 49 | 1)增加新的元素会非常困难。 50 | 51 | 2)实现起来比较复杂,会增加系统的复杂性。 52 | 53 | 3)破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。 54 | 55 | 4)visit()方法的返回值的类型在设计系统式就需要明确。不然,就需要修改访问者的接口以及所有接口实现。另外如果访问者接口的实现太多,系统的扩展性就会下降。 56 | 57 | """ 58 | 59 | 60 | class Finance: 61 | def __init__(self): 62 | self.salesvolume = None # 销售额 63 | self.cost = None # 成本 64 | self.history_salesvolume = None # 历史销售额 65 | self.history_cost = None # 历史成本 66 | 67 | def set_salesvolume(self, value): 68 | self.salesvolume = value 69 | 70 | def set_cost(self, value): 71 | self.cost = value 72 | 73 | def set_history_salesvolume(self, value): 74 | self.history_salesvolume = value 75 | 76 | def set_history_cost(self, value): 77 | self.history_cost = value 78 | 79 | def accept(self, visitor): 80 | pass 81 | 82 | 83 | # 2018年的财务情况 84 | class Finance_year(Finance): 85 | def __init__(self, year): 86 | Finance.__init__(self) 87 | self.analyst = [] 88 | self.year = year 89 | 90 | def add_analyst(self, worker): # 有哪些分析师来分析数据 91 | self.analyst.append(worker) 92 | 93 | def accept(self): # 分析师列表里面的人去分析数据 94 | for v in self.analyst: 95 | v.visit(self) 96 | 97 | 98 | # 会计 99 | class Accounting: 100 | def __init__(self): 101 | self.id = '会计' 102 | self.Duty = '计算报表' 103 | 104 | def visit(self, year_data): 105 | print('我现在分析的是{}年的数据'.format(year_data.year)) 106 | print('我的身份是:{},职责:'.format(self.id, self.Duty)) 107 | print('本年度纯利润:{}'.format(year_data.salesvolume - year_data.cost)) 108 | print('---------------------------------------') 109 | 110 | 111 | # 财务总监 112 | class Audit: 113 | def __init__(self): 114 | self.id = '财务总监' 115 | self.Duty = '分析业绩' 116 | 117 | def visit(self, year_data): # 要把具体哪一年的数据传给分析师,让分析师去分析 118 | print('我现在分析的是{}年的数据'.format(year_data.year)) 119 | print('我的身份是:{},职责:'.format(self.id, self.Duty)) 120 | if year_data.salesvolume - year_data.cost > year_data.history_salesvolume - year_data.history_cost: 121 | msg = '较同期上涨' 122 | else: 123 | msg = '较同期下跌' 124 | print('本年度公司业绩:{}'.format(msg)) 125 | print('---------------------------------') 126 | 127 | 128 | # 战略顾问 129 | class Advisor: 130 | def __init__(self): 131 | self.id = '战略顾问' 132 | self.Duty = '制定明年策略' 133 | 134 | def visit(self, year_data): 135 | print('我现在分析的是{}年的数据'.format(year_data.year)) 136 | print('我的身份是:{},职责:'.format(self.id, self.Duty)) 137 | if year_data.salesvolume > year_data.history_salesvolume: 138 | msg = '行业上涨,扩大规模' 139 | else: 140 | msg = '行业下跌,减少规模' 141 | print('本年度公司业绩:{}'.format(msg)) 142 | print('------------------------------') 143 | 144 | 145 | # 执行分析 146 | class AnalyseData: 147 | def __init__(self): 148 | self.datalist = [] # 需要处理的数据列表, 149 | 150 | def add_data(self, year_data): 151 | self.datalist.append(year_data) 152 | 153 | def remove_data(self, year_data): 154 | self.datalist.remove(year_data) 155 | 156 | def visit(self): 157 | for d in self.datalist: 158 | d.accept() 159 | 160 | 161 | if __name__ == '__main__': 162 | w = AnalyseData() # 计划安排财务,总监,顾问对2018年数据处理 163 | finance_2018 = Finance_year(2018) # 2018年的财务数据 164 | finance_2018.set_salesvolume(200) 165 | finance_2018.set_cost(90) 166 | finance_2018.set_history_salesvolume(190) 167 | finance_2018.set_history_cost(80) 168 | 169 | accounting = Accounting() 170 | audit = Audit() 171 | advisor = Advisor() 172 | 173 | finance_2018.add_analyst(accounting) # 会计参与2018年的数据分析,然后执行了自己的visit方法 174 | finance_2018.add_analyst(audit) 175 | finance_2018.add_analyst(advisor) 176 | 177 | # finance_2018.accept() #也可以直接这样调用 178 | w.add_data(finance_2018) 179 | w.visit() -------------------------------------------------------------------------------- /行为型模式-责任链模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 9:24 4 | 5 | """ 重要程度 ☆☆ 6 | 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。 7 | 8 | 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。 9 | 10 | 11 | """ 12 | class Event: 13 | 14 | def __init__(self, name): 15 | self.name = name 16 | 17 | def __str__(self): 18 | return self.name 19 | 20 | 21 | class Widget: 22 | 23 | def __init__(self, parent=None): 24 | self.parent = parent 25 | 26 | def handle(self, event): 27 | handler = 'handle_{}'.format(event) 28 | if hasattr(self, handler): 29 | method = getattr(self, handler) 30 | method(event) 31 | elif self.parent: 32 | self.parent.handle(event) 33 | elif hasattr(self, 'handle_default'): 34 | self.handle_default(event) 35 | 36 | 37 | class MainWindow(Widget): 38 | 39 | def handle_close(self, event): 40 | print('MainWindow: {}'.format(event)) 41 | 42 | def handle_default(self, event): 43 | print('MainWindow Default: {}'.format(event)) 44 | 45 | 46 | class SendDialog(Widget): 47 | 48 | def handle_paint(self, event): 49 | print('SendDialog: {}'.format(event)) 50 | 51 | 52 | class MsgText(Widget): 53 | 54 | def handle_down(self, event): 55 | print('MsgText: {}'.format(event)) 56 | 57 | 58 | def main(): 59 | mw = MainWindow() 60 | sd = SendDialog(mw) 61 | msg = MsgText(sd) 62 | 63 | for e in ('down', 'paint', 'unhandled', 'close'): 64 | evt = Event(e) 65 | print('\nSending event -{}- to MainWindow'.format(evt)) 66 | mw.handle(evt) 67 | print('Sending event -{}- to SendDialog'.format(evt)) 68 | sd.handle(evt) 69 | print('Sending event -{}- to MsgText'.format(evt)) 70 | msg.handle(evt) 71 | 72 | if __name__ == '__main__': 73 | main() -------------------------------------------------------------------------------- /行为型模式-过滤器模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 14:17 4 | """ 5 | 过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。 6 | """ 7 | from abc import ABCMeta, abstractmethod 8 | from monkey_print2 import print 9 | 10 | 11 | class Person: 12 | def __init__(self, name, sex, marital_status): 13 | self.name = name 14 | self.sex = sex 15 | self.marital_status = marital_status 16 | 17 | def __str__(self): 18 | return f"""Person: [Name: {self.name}, Gender: {self.sex} Marital Status: {self.marital_status}]""" 19 | 20 | 21 | class Criteria(metaclass=ABCMeta): 22 | @abstractmethod 23 | def meet_criteria(self, persons) -> list: 24 | pass 25 | 26 | 27 | class CriteriaMale(Criteria): 28 | def meet_criteria(self, persons) -> list: 29 | return [p for p in persons if p.sex.lower() == 'MALE'.lower()] 30 | 31 | 32 | class CriteriaFemale(Criteria): 33 | def meet_criteria(self, persons) -> list: 34 | return [p for p in persons if p.sex.lower() == 'FEMALE'.lower()] 35 | 36 | 37 | class CriteriaSingle(Criteria): 38 | def meet_criteria(self, persons) -> list: 39 | return [p for p in persons if p.sex.lower() == 'SINGLE'.lower()] 40 | 41 | 42 | class AndCriteria(Criteria): 43 | def __init__(self, criteria: Criteria, criteria_other: Criteria): 44 | self.criteria = criteria 45 | self.criteria_other = criteria_other 46 | 47 | def meet_criteria(self, persons) -> list: 48 | return self.criteria_other.meet_criteria(self.criteria.meet_criteria(persons)) 49 | 50 | 51 | class OrCriteria(Criteria): 52 | def __init__(self, criteria: Criteria, criteria_other: Criteria): 53 | self.criteria = criteria 54 | self.criteria_other = criteria_other 55 | 56 | def meet_criteria(self, persons) -> list: 57 | persons1 = self.criteria.meet_criteria(persons) 58 | persons2 = self.criteria_other.meet_criteria(persons) 59 | return list(set(persons1 + persons2)) 60 | 61 | 62 | if __name__ == '__main__': 63 | def print_person_list(p_list): 64 | for p in p_list: 65 | print(p) 66 | 67 | 68 | person_list = list() 69 | person_list.append(Person("Robert", "Male", "Single")) 70 | person_list.append(Person("John", "Male", "Married")) 71 | person_list.append(Person("Laura", "Female", "Married")) 72 | person_list.append(Person("Diana", "Female", "Single")) 73 | person_list.append(Person("Mike", "Male", "Single")) 74 | person_list.append(Person("Bobby", "Male", "Single")) 75 | 76 | male = CriteriaMale() 77 | female = CriteriaFemale() 78 | single = CriteriaSingle() 79 | singleMale = AndCriteria(single, male) 80 | singleOrFemale = OrCriteria(single, female) 81 | 82 | print("\nMales: ") 83 | print_person_list(male.meet_criteria(person_list)) 84 | 85 | print("\nFemales: ") 86 | print_person_list(female.meet_criteria(person_list)) 87 | 88 | print("\nSingle Males: ") 89 | print_person_list(singleMale.meet_criteria(person_list)) 90 | 91 | print("\nSingle Or Females: ") 92 | print_person_list(singleOrFemale.meet_criteria(person_list)) 93 | -------------------------------------------------------------------------------- /行为型模式-迭代器模式-使用类.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 11:49 4 | 5 | class AccountIterator(): 6 | def __init__(self, accounts): 7 | self.accounts = accounts # 账户集合 8 | self.index = 0 9 | 10 | def __iter__(self): 11 | return self 12 | 13 | def __next__(self): 14 | if self.index >= len(self.accounts): 15 | raise StopIteration("到头了...") 16 | else: 17 | self.index += 1 18 | return self.accounts[self.index - 1] 19 | 20 | 21 | if __name__ == '__main__': 22 | account_iter = AccountIterator(['a','b','c','d']) 23 | print(next(account_iter)) 24 | print('----') 25 | for a in account_iter: 26 | print(a) 27 | -------------------------------------------------------------------------------- /行为型模式-迭代器模式.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 11:39 4 | """ 5 | 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。 6 | 7 | 迭代器模式属于行为型模式。 8 | 9 | 介绍 10 | 意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。 11 | 12 | 主要解决:不同的方式来遍历整个整合对象。 13 | 14 | 何时使用:遍历一个聚合对象。 15 | 16 | 如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。 17 | 18 | 关键代码:定义接口:hasNext, next。 19 | 20 | 应用实例:JAVA 中的 iterator。 21 | 22 | 优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。 23 | 24 | 缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。 25 | 26 | 使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。 27 | 28 | 注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。 29 | 30 | python有更pythonic的方式。 31 | """ 32 | 33 | from abc import ABCMeta, abstractmethod 34 | 35 | class Iterator(metaclass=ABCMeta): 36 | 37 | @abstractmethod 38 | def hasNext(self): 39 | pass 40 | 41 | @abstractmethod 42 | def next(self): 43 | pass 44 | 45 | class ConcreteIterator(Iterator): 46 | 47 | def __init__(self, alist): 48 | self.alist = alist 49 | 50 | def hasNext(self): 51 | '''是否还有下一个元素''' 52 | return self.alist != [] 53 | 54 | def next(self): 55 | '''返回下一个元素''' 56 | return self.alist.pop(0) 57 | 58 | 59 | class Aggregate(object): 60 | 61 | def iterator(self): 62 | pass 63 | 64 | class ConcreteAggregate(Aggregate): 65 | 66 | def __init__(self, alist): 67 | self.alist = alist 68 | 69 | def iterator(self): 70 | '''返回迭代器对象''' 71 | return ConcreteIterator(self.alist) 72 | 73 | 74 | if __name__ == "__main__": 75 | concreteAggregate = ConcreteAggregate([1, 2, 3]) 76 | concreteIterator = concreteAggregate.iterator() 77 | while concreteIterator.hasNext(): 78 | print (concreteIterator.next()) -------------------------------------------------------------------------------- /行为型模式-迭代器模式3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author : ydf 3 | # @Time : 2019/10/9 0009 11:56 4 | 5 | from __future__ import print_function 6 | 7 | 8 | def count_to(count): 9 | """Counts by word numbers, up to a maximum of five""" 10 | numbers = ["one", "two", "three", "four", "five"] 11 | for number in numbers[:count]: 12 | yield number 13 | 14 | 15 | # Test the generator 16 | count_to_two = lambda: count_to(2) 17 | count_to_five = lambda: count_to(5) 18 | 19 | 20 | if __name__ == "__main__": 21 | for number in count_to_two(): 22 | print(number) 23 | 24 | 25 | for number in count_to_five(): 26 | print(number) 27 | --------------------------------------------------------------------------------