美文网首页
常见的设计模式

常见的设计模式

作者: 清风欢明月渡 | 来源:发表于2020-06-24 18:51 被阅读0次

    创建型模式

    单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式。
    

    结构型模式

    适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
    

    行为模式

    模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责模式,访问者模式
    

    单例模式

    核心作用
    - 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
    常见的应用场景
    - windows的任务管理器就是很典型电视单例模式
    - windows的回收站也是典型的单例应用,在整个系统运行过程之后,回收站一直维护着仅有的一个实例。
    - 项目中,读取配置文件的类,一般也只有一个对象,没有必要每次使用配置文件数据,每次new一个对象读取。
    - 网站的计数器,一般也是采用单例模式实现,否则难以同步。
    - 应用程序的日志应用,一般都采用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
    - 数据库连接池的设计一般也采用单例模式,因为数据库连接是一种数据库资源。
    - 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
    - Application也是一个单例的典型应用(Servlet编程中会涉及到)
    - 在Spring中每个Bean默认就是一个单例的,这样做的优点是Spring容器可以管理。
    - 在servlet编程之后,每个Servlet也是单例。
    - 在Spring MVC框架中,控制器对象也是单例。
    

    单例模式的优点

    - 由于单例模式只能生成一个实例,减少看系统性能开销,当一个对象的产生需要比较多的资源的时候,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
    - 单例模式可以再系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
    

    常见的五种单例模式实现方式

    — 主要
        饿汉式(线程安全,调用效率高,但是,不能延时加载。)
        懒汉式(线程安全,调用效率不高,但是,可以延时加载。)
    — 其他
        双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)
        静态内部类式(线程安全,调用效率高,但是可以延迟加载。)
        枚举单例(线程安全,调用效率高,不能延时加载。可以防止反射和反序列化漏洞)
    

    单例带来的问题

    - 反射可以破解上面几种(不包括枚举)实现方式!(可以在构造方法中手动抛出异常控制)
    - 反序列化可以破解上面几种(不包括枚举)实现方式!
        可以通过定义readResolve()防止获得不同对象。
    

    如何选择

    - 单例对象占用资源少,不需要延迟加载
        枚举式 优于 饿汉式
    - 单例对象 占用 资源大,需要延迟加载:
        静态内部式 优于 懒汉式
    

    工厂模式

    实现了创建者和调用这的分离
    核心本质
        - 实例化对象,用工厂方法代替new操作
        - 将选择实现类,创建对象统一管理和控制。从而将调用这跟我们的实现类解耦。
    - 简单工厂模式(静态工厂模式)
        工厂类一般使用静态方法,通过接收的参数不同来返回不同的实例对象。
        对于增加新产品无能为力,不修改代码的话,无法扩展
    - 工厂方法模式
        用来生产同一等级结构中的固定产品。(支持增加任意产品)
    - 抽象工厂模式
        用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
    

    面向对象设计的基本原则:

    - OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭;
    - DIP(依赖倒转原则,Dependence Inversion Principle): 要针对接口编程,不要针对实现编程
    - LOD(迪米特法则,Law of Demeter):只与你直接的朋友通信,而避免和陌生人通信。
    

    建造者模式

    分离出了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。
    

    建造者模式的本质

    - 分理处对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
    - 由于实现了构建和装配的解耦,不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
    

    原型模式

    - 通过new产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式。
    - 就是Java的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点。
    - 优势有:效率高(直接克隆,避免了重新执行构造过程步揍)。
    - 克隆类似于new,但不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,在修改克隆对象的值
    

    原型模式实现

    - Cloneable接口和clone
    - Prototype模式中实现起来最困难的地方就是内存复制操作,所辛在Java中提供了clone()方法替我们做了绝大部分事情
    

    结构型模式

    - 核心作用:从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
    - 分类
        适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
    

    适配器模式

    什么事适配器模式?
    - 将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
    模式中的角色
    - 目标接口(Target):客户所期待的接口。目标可以是具体的或者抽象的类,也可以是接口。
    - 需要适配的类(Adaptee):需要适配的类或适配者类。
    - 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
    工作中的场景
    - 经常用来做旧系统改造和升级
    - 如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍。
    例如
    - java.io.InputStreamReader(InputStream)
    - java.io.OutputStreamReader(OutputStream)
    

    代理模式(Proxy pattern)

    - 核心作用
        .通过代理,控制对象的访问
         可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即:AOP的微观实现!)
    - AOP(Aspect Oriented Programming面向切面编程)的核心实现机制
    

    核心角色

    抽象角色
        - 定义代理角色和真实角色的公共对外方法
    真实角色
        - 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
        - 关注真正的业务逻辑
    代理角色
        - 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
        - 将统一的流程控制放到代理角色中处理!
    

    应用场景

    - 安全代理:屏蔽对真实角色的直接访问
    - 远程代理:通过代理类处理远程方法调用(RMI)
    - 延迟加载:先加载轻量级的代理对象,真正需要在加载真实对象。
    例如:
        比如你需要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
    

    分类

    - 静态代理(静态定义代理类)
    - 动态代理(动态生成代理类)
        JDK自带的动态代理
        javaassist字节码操作库实现
        CGLIB
        ASM(底层使用指定,维护性较差)
    

    动态代理相比静态代理的优点

    - 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样我们可以更加灵活和统一的处理众多的方法
    

    JDK自带的动态代理

    - java.lang.reflect.Proxy
        作用:动态生成代理类和对象
    - java.lang.reflect.InvocationHandler(处理器接口)
        可以通过invoke方法实现对真实角色的代理访问
        每次通过Proxy生成代理类对象时都要指定对应的处理器对象
    

    桥接模式

    - 处理多继承结构,处理多维度变化场景,将各个维度设计成独立的继承结构,使各个维度都可以独立的扩展在抽象层简历关联。
    

    桥接模式的总结

    - 桥接模式可以取代多层继承的方案。多层继承违背了单一职责原则,复用性较差,类的个数也非 常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护成本。
    - 桥接模式极大的提高了系统可扩展性,在两个变化维度之后任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
    

    组合模式(composite)

    使用组合模式的场景:
        - 把部分和整体的关系用树形结构来表示,从而是客户端可以单一统一的方式处理部分对象和整体对象。
    组合模式核心:
        - 抽象构建(Component)角色:定义了叶子和容器构建的共同点
        - 叶子(Leaf)构建角色:无子节点
        - 容器(Composite)构建角色:有容器特征,可以包含子节点
    

    装饰模式(decorator)

    - 动态的为一个对象增加新的功能。
    - 装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
    

    实现细节:

    - Component抽象构建角色:
        真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
    - ConcreteComponent具体构建角色(真实对象):
        IO流中的FileInputStream,FileOutputStream
    - Decorator装饰角色:
        持有一个抽象构建的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能。
    - ConcreteDecorator具体装饰角色:
        负责给构建对象增加新的责任。
    

    装饰模式的总结

    - 装饰模式(Decorator)也叫包装模式(Wrapper)
    - 装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类。
    优点:
        - 扩展对象功能,比继承灵活,不会导致类个数急剧增加
        - 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
        - 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。
    缺点:
        - 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
        - 装饰模式易于出错,调式排查比较麻烦。
    

    装饰模式和桥接模式的区别

    两个模式都是为了解决过多子类对象问题。但他们不一样。桥接模式是对象自身有机制沿着多个维度变化,是既有部分不稳定,装饰模式是为了增加新的功能。
    

    外观模式

    迪米特法则(最少知识原则):
        一个软件实体应当尽可能少的与其他实体发生相互作用
    为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
    

    享元模式(FlyWeight)

    场景:
        - 内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或者相似的对象,我们可以通过享元模式,节省内存。
    核心
        - 享元模式以共享的方式高效地支持大量细粒度对象的重用。
        - 享元对象能做到共享的关键是区分了内部状态和外部状态。
            内部状态:可以共享,不会随环境变化而改变
            外部状态:不可以共享,会随环境变化而改变
    

    享元模式实现:

    - FlyweightFactory享元工厂类
        创建并管理享元对象,享元池一般设计成键值对
    - FlyWeight抽象享元类
        通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
    - ConcreteFlyWeight具体享元类
        为内部状态提供成员变量进行存储
    - UNsharedConcreteFlyWeight非共享享元类
        不能被共享的子类可以设计为非共享享元类
    

    享元模式总结、

    优点:
        - 极大减少内存中对象的数量
        - 相同或相似对象内存之后只存一份,极大的节约资源,提高系统性能
        - 外部状态相对独立,不影响内部状态
    缺点:
        - 模式较复杂,使程序逻辑复杂化
        - 为了节省内存共享了内部状态,分离出外部状态,而读取出外部状态使运行时间变长。用时间换取空间。
    

    责任链模式

    将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。
    场景:
    - 打牌时,轮流出牌
    - 接力赛跑等
    

    迭代器模式

    - 提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
    - 聚合对象:存储数据
    - 迭代器:遍历数据
    

    中介者模式

    - 如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其复杂,这些对象称为"同事对象"。
    - 我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星形结构。
    

    命令模式

    -将一个请求封装为一个对象,从而是我们可以用不同的请求对客户进行参数话;对请求排队或者记录请求日志,以及支持可撤销的操作。也称为:动作Action模式、事务transaction模式
    

    观察者模式

    - 观察这模式主要用于一对多的通知。当一个对象(目标对象Subject或者Objservable)的状态变化时,他需要及时告知一系列对象(观察者对象,Observer),令他们做出响应。
    - 通知观察者的方式:
        推
            - 每次都会把通知以广播方式发送给所有观察这,所有观察者只能被动接受。
        拉
            - 观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。

    相关文章

      网友评论

          本文标题:常见的设计模式

          本文链接:https://www.haomeiwen.com/subject/zwhdfktx.html