设计模式总纲

工厂模式:
怎么写:
    工厂模式有简单工厂模式,工厂模式,抽象工厂模式,看起很复杂,
    但其实很简单,
    本质就是把构造方法放到非构造方法里面,
    class 类名_之_我是简单工厂{
        public 类名_之_我是产品 方法名_之_生产(){
            return new 类名_之_我是产品()
        }
    }
    到时候一个普通的方法就起到了构造方法的作用,我更倾向于称前面那个包装了构造方法的
    非构造方法为生产线方法,
    简单工厂模式就是个包含了至少一个生产线方法的类,
    /*包含多个生产线方法的方法(不是函数的意思)可以是都放到一个函数中然后给
    参数做判断,也可以是到时候把生产线方法作为成员方法*/
    至于工厂模式,它与简单工厂上进行了一定的抽象提取,把流水线方法的返回值从'ne
    w出来的对象'换成了接口(抽象类也可以):
    class 类名_之_我是工厂{
        public 接口名_之_我是产品 方法名_之_生产(){
            return new 接口名_之_我是产品()
        }
    }
    然后该继承的继承,该实现的实现.
    注意这里的工厂只有一个生产线方法,
    而有多个生产线方法的就叫抽象工厂模式了:
    Interface 接口名_之_我是抽象工厂{
        public 接口名_之_我是产品 方法名_之_生产方法1(){
            return new 接口名_之_我是产品()
        }

        public 接口名_之_我是产品 方法名_之_生产方法2(){
            return new 接口名_之_我是产品()
        }

        public 接口名_之_我是产品 方法名_之_生产方法3(){
            return new 接口名_之_我是产品()
        }
    }
    这里的生产线方法就不能放到一个成员方法里面了,道理大家都懂.
什么时候用:
1.要创建对象的时候用
2.要"简化/一般化"构造方法的时候用,生产线方法可以提取固定部分.
3."创建的对象们"隶属于'复杂的产品类,产品族'/*产品类和产品组的概念,可以参考生物的界门纲目科属种,其实你可以让工厂的生产线方法
返回工厂,再返回工厂来模拟更复杂的对象结构*/的时候,(抽象)工厂将产品抽象以便于拓展.
4./*补充3*/其实"具有相似功能的类"可以看作一类/族产品    
  
适配器模式:
什么时候用:
    接口/*两段程序通过函数的调用实现连接,函数就是之间的接口*/不匹配,
    /*如手机的充电口和充电线的头不匹配,电器的额定电压和电源的电压不匹配,笔盖和笔不匹配.*/
    适配器就可以把"其中的一方"包装成'另一方规定的尺寸/形状',
    对于函数(接口)来说就是"函数名称,返回值类型,参数"要互相匹配.
    /*出现了不匹配的接口,应该是你用了非原生态的代码*/
怎么写:
    本质是对一个方法的包装,从而定向改变它的名称,返回值.
    1.这里要被包装的方法所在的类叫适配者,包装适配者的类叫适配器.
    2.首先,适配器要得到"适配者的被包装方法",继承 or 对象包含.
    3.抽象化,如先写个适配器目标接口,要包含的适配者也先写成接口/抽象类,
      这就要看适配者的类结构了.
    4.实例,这里举一个简单了例子:
    class 类名_之_我是适配者:
        def 方法名_之_我的方法名不符合接口(self):
            pass
    
    class 类名_之_我是适配器:
        def 方法名_之_我的方法名符合接口(self):
            类名_之_我是适配者.方法名_之_我的方法名不符合接口()

责任链模式:
什么时候用:
    对一个"数据对象/请求对象"的处理/响应,是'轮流/逐级'的过程.
    /*如斗地主,三个人按顺序出牌;
    再如自来水的过滤生产过程,
    取源水--沉淀--过滤--消毒等等;
    再如学生的升学过程,
    小学--中学--大学;
    再比如业务的逐级审批,
    交易员—复核员—部门经理(—会计部审核)—主管领导—风险控制部—(—贷款审查委员会)—公司领导;*/
怎么写:
    1.没有固定的模式,关键是转发/*就是调用下一级的处理方法*/,
    且除了链式的调用传递,还可以加点分叉,前后连接,双向连接,很像神经细胞.
    2.这里写一个简单的典型案例:
    class 类名_之_处理者的父类:
        def 方法名_之_设置下一级(self,handler):
            self.下一级=handler
        def 方法名_之_处理请求(self):
            pass
    
    class 类名_之_具体的处理者:
        def 方法名_之_处理请求(self,变量名_之_请求):
            if 能处理:
                #具体的处理过程
            else:
                下一级.方法名_之_处理请求(变量名_之_请求)

观察者模式:
什么时候用:
    一个对象变,多个对象跟着变.
    /*如红绿灯,公众号,长官对士兵下达命令*/
怎么写:
    1.称"引起其他类变化的类"为主题类,赶着主题类变化的类叫观察者类.
    2.在主题类中有个"观察者类的列表",当主题变化是,就遍历观察者并调用其变化方法.
    /*本质还是间接函数调用*/
    3.例子:
    class 类名_之_主题类:
        self.变量名_之_观察者列表=[]
        def 方法名_之_通知观察者(self):
            for 变量名_观察者 in self.变量名_之_观察者列表:
                变量名_观察者.方法名_之_跟着变化()
        def 方法名_之_添加观察者(self):
            pass#增删改查等
        
    class 类名_之_观察者:
        def 方法名_之_跟着变化(self):
            pass
    4.拓展:把观察者改个名字,叫监听者;主题类也更名为事件源.
    调用事件源的"方法名_之_通知观察者"时,给该函数传递一个参数叫event,
    同理,该函数内部也把event传给"方法名_之_跟着变化()",
    "方法名_之_跟着变化()"根据传进来的event进行判断并响应,
    这就是GUI中广泛用到的事件监听机制了,再把event放到一个安全队列中,加上多线程,就成了事件队列了,
    这个在游戏开发中也很常用.

策略模式:
什么时候用:
    完成某个"功能/目的"有多种'策略/算法'可供选择.
    /*
    排序可以选择冒泡排序,选择排序,插入排序等*/
怎么写:
    1.其实就是对'策略/算法'的抽象编程.
    2.实例:
    class 类名_之_用策略的人{
        public void 方法名_之_使用策略(接口名_之_抽象策略 变量名_之_具体策略){
            变量名_之_具体策略.方法名_之_具体策略实现()
        }
    }

    Interface 接口名_之_抽象策略{
        void 方法名_之_具体策略实现(){}
    }
    将来实现"接口名_之_抽象策略"之后往'方法名_之_使用策略'一传就好了.

状态模式:
什么时候用:
    类有不同的状态(某些属性),且不同状态下,表现(各函数的效果)不同.
    /*人有高兴和伤心两种状态,不同状态下的行为(走路,说话,吃饭)不同,
    如果你在每个行为函数中"先判断状态再实现",if语句很多,而且新增状态(如生气,平静)很麻烦*/
怎么用:
    1.先分离,从人分离出了高兴人,伤心人,生气人等等;
    再抽象,把"高兴人,伤心人,生气人"归纳/抽象/概括为情绪人.
    2.将判断的过程提前,先决定要使用的情绪人是哪个,然后直接调用情绪人的
    吃饭/说话/走路方法.
    3.经过这些改变,原来的人其实只负责用哪个情绪人,具体的行为实现就交给具体的情绪人.
    4.这样下来,if语句减少,增加状态也更容易了.
    5.例子:
    class 类名_之_人:
        def 方法名_之_用哪个情绪人(self,变量名_之_心情字符串):
            if 变量名_之_心情字符串=="高兴":
                self.要用的情绪人=类名_之_高兴人()
            if 变量名_之_心情字符串=="伤心":
                self.要用的情绪人=类名_之_伤心人()
        def 方法名_之_说话(self):
            self.要用的情绪人.方法名_之_说话()
        def 方法名_之_吃饭(self):
            self.要用的情绪人.方法名_之_吃饭()
        def 方法名_之_走路(self):
            self.要用的情绪人.方法名_之_走路()

    class 类名_之_抽象情绪人:
        def 方法名_之_说话(self):
            pass
        def 方法名_之_吃饭(self):
            pass
        def 方法名_之_走路(self):
            pass

模板方法模式:
什么时候用:
    "实现一个目的/功能的函数"由许多步骤组成,
    但是"其中几个步骤的具体实现"没有确定/进行拓展/要多样化.
    /*比如装修,先铺地,再搞墙,再放家具,这个装修函数的模板已经确定了,
    但铺地的时候是瓷砖还是木地板,放家具的时候放什么家具*/
怎么写:
    1.写一个类,把父方法的模板写好/*也就是父方法中子方法的位置,顺序确定好*/,
      然后把用到的子方法写成空方法.将来继承这个类就好
    2.可以把子方法也写成模板方法,继续套娃.
    3.例子:
    class 类名_之_模板方法所在类:
        def 方法名_之_步骤1(self):
            pass
        def 方法名_之_步骤2(self):
            pass
        def 方法名_之_步骤3(self):
            pass
        def 方法名_之_模板方法(self):
            方法名_之_步骤1()
            方法名_之_步骤2()
            方法名_之_步骤3()

外观模式:
什么时候用:
    客户端(一般main方法)要和"多个代码片段的不同接口"交互,过于复杂,
    可以使用外观对象,让它和"多个代码片段的不同接口"交互,
    而客户端只与外观对象交互.
    /*好比交作业,
    没有使用外观模式,就是你给各个课代表分别交作业.
    使用了外观模式,就相当于有个组长,你直接把作业交给他,他再给各个课代表交作业.
    再好比家里电器的开关:
    没有使用外观模式,就是你自己挨个关开关;
    使用了外观模式,相当于你有了个总开关,关闭总开关所有的开关就都关了.
    可以理解为秘书模式或者跑腿模式*/
    它是为了隐藏复杂的系统,简化操作,而且有单向性,只有客户端通过外观对象
    调用子系统,子系统用不着外观对象.
怎么写:
    定义外观对象类,统一子系统和客户端的接口就完事.


中介模式:
什么时候用:
    需要对象交互且"对象数量多,交互关系复杂"/"不同类别对象交互困难"/待续,
    中介对象就可以沟通对象,交互的任务就全部交给它了.
    /*这个例子很多,微信朋友圈,聊天群,各种中介公司,邮局*/
    还可以理解为仲裁,裁判,公证处.
怎么写:
    包装一下别的对象的方法,加点判断,数据处理就行.


访问者模式:
    1.和许多设计模式相同,它也是通过"特定的对象和方法的调用关系",最终调用了
    某个方法,称该方法为核心方法.
    在工厂模式中,核心方法是构造方法,
    在观察者模式中,核心方法是观察者的"跟着变化"函数,
    在策略模式中,核心方法是具体的策略方法,
    在责任链模式中,核心方法是指向下一级和处理方法.
    2.在访问者模式中,核心方法是一个"[对象内部数据]的分析展示方法",
    且该核心方法以"要分析的对象"为参数,该方法不是"要分析的对象"的成员方法.
    要写成这样(python):
    **方法名_之_核心分析方法(变量名_之_要被分析的对象).
    3.实现核心方法很简单,建个数据分析类,里面定义好"方法名_之_核心分析方法",
    将来一调用就好(python):
    **变量名_之_数据分析实现类的对象.方法名_之_核心分析方法(变量名_之_要被分析的对象)
    4.但访问模式复杂在它在这个基础上又包装了一层,结果如下(python):
    **
    class 类名_之_要被分析的类:
        def 方法名_之_accept(self,变量名_之_数据分析实现类的对象):
            变量名_之_数据分析实现类的对象.方法名_之_核心分析方法(self)
    实际中这么用:
    变量名_之_要被分析的对象.方法名_之_accept(变量名_之_数据分析实现类的对象)
    5.
    起初,我感觉这种设计模式作茧自缚,
    联系前面所学,在"被分析的对象"的类相同时,就算数据分析业务花样多,变化多,也不用访问者模式,
    状态模式就足够了.
    但如果"被分析的对象"的类别不同,而且数据分析业花样多,变化多,那怎么办?
    /*比如某公司要进行业务考核,
    有两种员工:
    技术人员(基本数据为代码质量,工作时间),管理人员(基本数据为项目收益,工作时间),
    有三个考核人员:
    张三(主要看工作时间),李四(主要看代码质量或者项目收益),王五(各项基本数据都要看).
    两个个考核指标,努力程度.*/
    这种情况下,访问者模式的优越性就上来了,不会弱于状态模式.
    6.该模式的一般代码:
    **
    (Java,函数声明时"参数列表的位置"就只写类型,不写形参的名称了)
    Interface 接口名_之_数据分析接口{
    void 方法名_之_留给类A的数据分析方法(类名_之_要被分析的类A){}
    void 方法名_之_留给类B的数据分析方法(类名_之_要被分析的类B){}
    }

    class 类名_之_要被分析的类A{
    public void 方法名_之_accept(接口名_之_数据分析接口){}
        接口名_之_数据分析接口.方法名_之_留给类A的数据分析方法(this)
    }
    class 类名_之_要被分析的类B{
    public void 方法名_之_accept(接口名_之_数据分析接口){}
        接口名_之_数据分析接口.方法名_之_留给类B的数据分析方法(this)
    }
    7.将来,一旦数据分析方式改变,就实现一下"接口名_之_数据分析接口"就好,
    不必纠结于类型判断,状态转换,花样变化,还是比较省心的.
    8.回顾前面,该模式把简单的逻辑包装了一层,
    本质是调换了函数调用方:
        *.未包装之前,由于遍历(遍历的对象是类型不一致的被分析对象们)的原因,
        数据分析实现类只能一个方法,
        那么多样化就只能先判断再实现了.
        *.包装之后,就是被分析对象调用"自己的数据分析方法",
        就不用判断了,
        到这里只能算适配器模式(自带接口);
        然后你把"单个被分析类"的数据分析方法抽象出来,这就是策略模式;
        最后把不同类型的"抽象数据分析方法"给放到一个抽象接口/类中,
        才能优雅简洁地按一种方式分析不同类型数据,这才是访问者模式.
    9.最后总结,访问者模式乍看起来佶屈聱牙,狗尾续貂,莫名其妙,
      但遇到"多种方式分析不同类型对象"时却简快明了,势如破竹,
      显然,这种情况下不同类型的数据是聚合再一个可迭代对象中进行批量处理的.
      你可以将这个模式理解为:
      1.被分析对象提供了一个分析自己的入口,让访问者进来看情况调用访问者自己的分析方法,
      2.也可以理解为集成策略模式,超级策略模式,统一策略模式

解释器模式:
    设计模式中最难懂的就是它和上文的访问者模式了.
    1.可以使用已知和未知的观点看这个模式,程序面对的参数是未知的,
    但它可以根据写好的规则去把未知的转化成已知的,可处理的.
    2.注意两个观念,终止符就是说这个因素是已知的,可处理的,
    非终止符,就是说只要存在非终止符就要继续解释,直到只剩下终止符.
    /*比如加减运算,+,-就是非终止符,参与运算的a,b等由于初始值给定,算是终止符.*/
    3.a+b-c,逻辑如下:
    先从左到右看一遍,发现非终止符+,调用转化函数,把+,以及它左右两边的a,b都传进去,
    返回一个a+b的值,设为d(方便叙述).
    原式变为d-c,
    先从左到右看一遍,发现非终止符-,调用转化函数,把-,以及它左右两边的d,c都传进去,
    返回一个d+c的值,设为e.
    原始变为e,
    看一遍,没有非终止符,解释完毕,结果为e的值.
    4.实际中,非终止符会很多,还有优先级的问题,还得要一个环境变量储存中间值(好比
    化学反应的中间产物)

#下面的就比较简单了,逻辑上简单很多.

合成复用模式/聚合模式:
    "复用"意思是代码的重复使用,
    显然可以用继承实现,也可以间接调用,也就是把别人的对象的方法包装到自己的方法里.
    如果你把别的对象放到自己的对象里,
    成了自己的成员内部类,
    这种复用代码的方式就叫合成复用原则,也叫聚合模式.
    它有很多好处,简化结构,还能避免拓展带来的类爆炸.
    但继承也有自己的好处,继承得到的对象可以靠爹,也就是多态的使用,
    不过在python中多态的优势就下降了.

桥接模式:
    其实是聚合模式对于成员内部类的抽象,原来用的是一个具体类的方法做自己的功能实现,
    现在把该具体类换成结构就完事了,所有实现该接口的具体类都能用了,也就是路走宽了.
    它还解耦了抽象和具体,把"抽象改变"带给实现类的影响减少了,
     
装饰模式:
    把一个真实类包装一下,拓展一下功能
    其实就是合成复用原则的一种,不过以被包装对象为主,
    好比托尼史塔克变成钢铁侠.

建造者模式:
    没什么好说的,如果某对象的构造比较麻烦(成员变量多 or 成员内部类/对象多),
    把这个构造的构造以及设置相关参数的过程统统写成函数,放在一个类中,这个类就叫指挥者.
    同样是批量生产,它就像一个复杂的生产线,简单工厂模式就像拿模具压月饼.

命令模式:
    没啥好说的,就是对象A的某个函数调用对象B的某个函数.
    就是传递的参数包含了较多信息(写到对象里),B的函数根据该参数对象做出反应.
    这个参数对象也叫命令对象.
    就像GUI中的event,有不同的类型,比如鼠标按下事件,键盘按下事件,鼠标松开事件,
    键盘松开事件,每种类型的事件包含了具体的信息:
    如鼠标按下事件有很多属性,按键的位置,按的哪个键,相对上一次该事件的位移.

组合模式:
    1.有"部分--整体",树状结构出现时考虑该模式.
     /*1、算术表达式包括操作数、操作符和另一个操作数,其中,
     另一个操作符也可以是操作数、操作符和另一个操作数。
      2.文件夹和文件的关系
      3.数值和树叶的关系*/
     2.怎么实现:
     先写个节点(Node)接口/抽象类,里面声明好增删改查以及其他操作,
     然后写树叶类和树枝类实现节点,并且重写相应方法就好了

单例模式:
    某个类只需要一个实例对象就好.
    /*Windows 中只能打开一个任务管理器,
    这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,
    或出现各个窗口显示内容的不一致等错误。
    还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、
    显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、
    数据库的连接池、网站的计数器、Web 应用的配置对象、
    应用程序中的对话框、系统中的缓存等常常被设计成单例。*/


里氏替换原则:
    该原则告诉我们什么时候用继承,
    只有当"父亲能干的"儿子全都能干的时候,才能用继承.
    待续

迪米特原则:
    减少对象之间的交互,一个软件实体应当尽可能少的与其他实体发生相互作用,
    从而减少它们之间的羁绊/牵连/因果,
    搞个公共的交互类就完事.
    待续


开闭原则:
    扩展开放,修改关闭.
    意思是功能发生改变的时候,尽量少改已经写好的代码.
    待续

接口隔离原则:
    一个类对另一个类的依赖应该建立在最小的接口上,
    类间的依赖关系应该建立在最小的接口上.
    待续

单一职责原则:
    一个类被改变的原因不能超过一个,也就是说,一个类只有一个职责,
    如果职责过多,代码就会臃肿,可读性更差,也更难以维护.
    其实上单一职责原则和接口隔离原则有一定的关系,接口隔离以后,
    职责就单一了,实现这个接口的类的职责自然也就单一了.
    待续

你可能感兴趣的:(编程,#,设计模式)