
Java基础-OOP&设计模式
1. OOP七大原则
1.单一职责
每个类只保证其自身的功能,只负责单一的职责,不负责其他或者共有的职责。
例:订单类只做订单增删改查,不要同时承担订单日志、支付计算,日志抽独立日志类。
2.接口隔离
接口只定义必要的方法,而不应该把多个不相干的方法糅合到一个接口里。
例如一个接口定义方法123,A实现类只使用1,B实现类只使用2,C实现类只使用2和3,导致各个实现类需要实现接口里对于自身而言不使用的方法,造成冗余,也降低了可读性。
3.依赖倒置
高层模块不应该依赖底层模块,比如电脑的主板不会去依赖显卡,内存,而是通过自己定义的扩展槽来实现,通过声明缓冲层的方式,让高层去依赖缓冲层,而不是直接依赖底层模块,
细节依赖抽象,而抽象不依赖细节。
4.里式替换
子类可以重写父类扩展方法,但不能破坏父类原有行为
继承能够带来很多便利,但是也一定程度上造成代码破坏性,比如B类继承A类,此时A类的子类可以重写父类的方法,这样如果A类修改,其下所有子类都会造成修改,可能导致功能不可用,
子类重写不能破坏父类原有行为;若需要大幅修改原有逻辑,优先使用组合 / 聚合替代继承。
5.开闭原则
对扩展开放,对修改关闭。
一个类可以扩展,但是不能去修改它原有的东西。
6.迪米特法则(最小朋友原则)
一个对象应该与其他对象保持最小的了解。
只与直接的朋友通信,A类想调用B类的方法,只管调用,对于A类来说,只依赖直接朋友。
7.合成复用
尽量使用合成/聚合/组合的方式,而不是使用继承、
因为java是单继承,使用了继承会对类造成局限性。所以组合大于继承。
2.设计模式
-
创建型
-
工厂模式
-
抽象工厂模式
-
单例模式
-
原型模式
-
建造者模式
-
-
结构型
-
适配器模式
-
装饰者模式
-
代理模式
-
外观模式
-
桥接模式
-
组合模式
-
享元模式
-
-
行为型
-
策略模式
-
模板方法模式
-
观察者模式
-
迭代器模式
-
责任链模式
-
命令模式
-
备忘录模式
-
状态模式
-
访问者模式
-
中介者模式
-
解释器模式
-
创建型
工厂方法模式*
概述:隐藏类的实现,将对象的创建交给工厂来管理,符合开闭原则。
抽象产品、具体产品、抽象工厂(只定义一个生产方法)、具体工厂(只生产单一产品)
便于进行管理分类,隔离。
例:工厂只造手机,通过具体工厂 造不同型号的手机
只生产单一产品等级。
抽象工厂模式
概述:可以看做是对抽象工厂方法的抽象,工厂方法仅生产同一等级产品;抽象工厂可生产一整套配套产品族。
大概流程:对工厂抽象,定义每条流水线,由抽象工厂的实现来决定具体创建哪些对象。
例:工厂不仅造手机,还造电脑,平板,一整套产品线
生产一整套相关产品族。
单例模式*
概述:保证该类有且只有一个实例。私有化构造方法,对外暴露一个静态的获取实例的方法。通过某些手段保证它多次调用只会返回同一个对象。
-
饿汉模式:启动时创建
-
实例声明为static:静态属性或静态代码块在只会在类加载时执行一次
-
私有化静态属性
-
静态代码块内创建
-
-
使用枚举:本质就是static
-
-
懒汉模式:调用时才加载
-
私有化构造方法
-
提供对外静态方法
-
属性声明为volatiile,多线程开放,内存屏障+禁止指令重排保证线程安全
-
getInstance方法判断只有实例为null时才创建,且用sync关键字上锁
-
在sync中再次判断,双重检查。
-
原型模式
概述:创建重复的对象,克隆对象,避免频繁重复创建,减少资源损耗。
浅拷贝:
- 基础类型存在栈里,天然浅拷贝。
深拷贝:
- 新建引用,拷贝的对象引用指向新的地址,不会干涉原对象。
使用
-
实现Cloneable接口
-
重写clone()方法
-
深拷贝:
-
在拷贝对象的clone()方法中,同时也调用一次自己的引用类型的字段clone()
-
实现序列化接口,利用序列化和反序列化实现深拷贝
-
结构型
适配器模式
不同的系统业务之间做接口兼容,创建一个适配器来兼容两边不同的规范,
对象适配器使用组合, 类适配器使用多重继承。(java没有多继承,所以只有对象适配器,类适配器仅c++等语言存在)
A中方法为exec(),B中方法为run(),想在B中调run()方法,执行A的exec()
需要有一个适配器类实现B的接口且引入A,并定义方法为run(),中调用A的,exec();
例:港版switch的充电插头是三个口的圆口,国内只有2个口或者3个口的斜口,要想用港版switch的插头在国内充电,要么加个转接头,要么直接换个国行的。
装饰者模式
对原本的类进行装饰,在不改变原有类结构的情况下 使用装饰类来方便得做扩展装饰功能
与代理模式有些许类似,和代理模式的区别:
-
代理模式偏向于对原有类的访问控制,顺带增强(托管),装饰者模式偏向于扩展类的能力(增强)。
-
spring aop使用代理模式,FileInputStream就是InputStream的装饰者类。
-
代理模式是行为的扩展,装饰者模式是对对象的扩展。
-
例如想要拧开罐头,代理模式是把拧开罐头这个行为交给别人帮忙实现,
-
而装饰者模式可能就是对这个罐头盖装了个拉环,让它更容易被打开。
具体:声明一个装饰者类,引入被装饰对象,并定义扩展方法。(不破坏原有结构,同时获得了增强能力)
代理模式*
将事情委托给代理者来做,(找中介,托管)
托管类的具体行为,而不是扩展类的能力
-
静态代理:
已知类的类型和具体实现,编写代理类与其共同实现一个接口,并在构造方法引入被代理对象,重新实现它的具体方法,期间的所有事情由代理对象决定。
-
动态代理: 利用反射或cglib在程序运行时动态的生成原对象的代理对象,并做代理。
-
基于jdk【必须实现接口】:
Proxy:代理类,newProxyInstance()方法返回一个Object类型的代理对象,可强制类型转换为指定的代理对象类型, 入参:被代理对象的ClassLoader,被代理对象的接口,invocationHandler对象
InvocationHandler接口,只有一个invoke方法,实现该方法,可配合反射去调用被代理对象的方法,并可在方法调用前后去自定义实现,这个方法返回代理之后的对象。
invoke(Object proxy,Method method,Object[] args) 调用这个方法的代理实例,被代理对象的方法对象,被代理对象的方法入参
原理:
-
通过Proxy的newProxyInstance方法创建了一个代理对象。
-
实现了InvocationHandler并实现了invoke方法,
-
后续调用方法时实际上是invoke返回的对象
-
-
基于cglib【不能是final】:
-
cglib代理也叫做子类代理,他在内存中创建一个子类对象,从而实现对目标对象的扩展
-
cglib是一个高性能的代码生成包,可以在运行期间扩展java类与实现java接口,被广泛用于AOP的框架
-
底层是通过字节码处理框架ASM来转换字节码并生成新的类
-
较于jdk动态代理,无需 被代理对象实现接口。
-
-
外观模式
提供一个包装盒,隐藏类的具体实现细节,让调用者可以方便的调用
具体实现:下单需要校验数据,创建订单,减库存,调用支付,发送消息通知等一系列操作,把这些操作封装到一个订单服务中,调用方只需要调用下单这个方法就行了。
去医院看病可能要挂号,叫号,门诊,取药,如果有一个接待人员先在大厅一步一步指导你怎么做,就不会麻烦了。
桥接模式
抽象和实现解耦,将抽象与实现分离开来,使它们可以独立变化。
适用于类中出现两个独立变化的维度,且两个维度都需要扩展,比如品牌和功率,一个软件支持的文件格式和支持的系统 在软件开发中,有时桥接(Bridge)模式可与设配器模式联合使用。
当桥接(Bridge)模式的实现化角色的接口与现有类的接口不一致时,可以在二者中间定义一个适配器将二者连接起来
优先组合而非继承
类似多维数组,长和列的维度完全不相干。
组合模式
适用于整体和局部差异不大的部分,通俗点就是做树形结构经常用,自己有一个自己的属性,套娃。
透明模式:自己规定了所有孩子的方法,有的孩子用不到的方法也得实现
安全组合模式:只定义基础的方法,剩下的差异化交由孩子自己来实现扩展。
享元模式
系统有大量类似的对象,频繁的创建造成性能损耗
享元模式:通过共享技术有效的支持大量细粒度的对象
内部状态(可共享,不变)外部状态(外部传入,不共享)
例如 String 线程池等 创建对象时将值放入常量池,再次取则从常量池中取,如果值发生改变则创建新对象
对于多线程需要考虑线程安全问题
行为型
策略模式*
定义算法族群,让算法的变化独立于本身。
在执行一道程序时,可能会出现A方案,后续可能会因方案的变化,要切换到B方案去,用什么方案取决于领导
例:同一个项目有多个文件服务实现(本地文件服务,OSS云对象存储)通过系统配置切换,用策略模式实现。
模板模式
定义一个模板接口,把公共的规定好的部分抽象出来,剩下的具体细节交给实现类实现。方便管理子类
子类可以重新定义,不破坏总体的结构。
例:冲咖啡和冲茶的大体步骤相同,具体细节不同,例:手动挡和自动挡汽车在换挡方式不同,其他大致相同,或电车和油车的充电\加油方式不同。
观察者模式*
发布/订阅模式
存在一对多关系,且对多进行管理
使用集合存储全部订阅者,目标状态变更时遍历通知所有订阅者。
当其中一个元素改变时,监听到该元素改变并通知给所有的参与者,
-
一方的状态值发生改变的时候,同时所有的消息订阅者,让他们收到消息后自主更新。(示例:微信公众号,发布者发布文章后,订阅者得到通知)
-
主要结构:抽象发布者,发布者实现,抽象订阅者,订阅者实现
-
抽象发布者和抽象订阅者目的都是为了约束其具体实例
-
发布者保存了订阅者的列表,提供了一个发布方法以通知订阅者,执行所有订阅者的消息通知方法,订阅者只要订阅了发布者,在其发布消息后响应式执行更新方法
迭代器模式
提供一个规范的迭代器,能够遍历数据的每一个元素
集合框架中的Iterator
责任链模式*
多个接受者通过聚合形成一个责任链,一个消息发布后,由责任链中的每个人排队一条一条处理或转发。
servlet,security中的filter,各种filter
多个对象处理同一个请求,多个对象形成一条链
命令模式
把命令抽象化,易于对行为的扩展,如会议的记录,撤销,重做等
备忘录模式
把对象的各个节点状态保存,可以灵活的回退,前进
历史记录,撤销,重做,数据库事务
性能问题,每个节点保存一个对象的副本
状态模式*
根据不同状态执行不同的行为,当状态改变时,行为随之改变。
例如,审批接口,审批状态为通过时,执行通过的回调,(更新数据,执行通过后的业务),状态为退回时,执行退回回调,驳回该数据并返回审批意见。
访问者模式
将数据结构和数据操作分离开,根据访问者的不同执行不同的数据操作,且不改变结构。
扩展性高,适用于数据结构相对稳定的系统
可以灵活的扩展数据操作,例:xml/json解析,封装统一导出方法(同一业务,不同页面需要访问不同导出字段等)等
中介者模式
减少类之间的复杂调度,提供一个中介者来统一对各个类之间的管理
聊天室(用户不互发消息,全部发给聊天室中介转发)、MVC 控制器
解释器模式
对一些固定语法进行解释,容易改变和扩展文法
例如定义abc,输出111,定义000,输出bb这样
类容易膨胀