啥是设计模式?
设计模式:是软件开发人员在开发过程中遇到一些问题的解决方案,是有很多的开发人员根据自己平时的总结和错误得出来的。
为啥使用设计模式?
因为使用设计模式可以规范我们的代码,帮助我们在平时的开发中少走一些不必要的弯路。既能让别人看的懂,又能提高自身的高复用性和高扩展性,让自己的代码更健壮,也降低了维护成本,帮助我们解决了复杂的业务问题。
设计模式的原则?
1、开闭原则(对扩展开放,对修改关闭):只能在原有基础上扩展,不能修改原有的东西。
2、里氏替换原则(这个是通过抽象(接口或者抽象类)去定义规范,然后具体实现交给具体的实现类或者子类,也就是说用到父类的地方换成子类,程序依然可以运行)
可以说:里氏代换原则是继承复用的一个基础。
3、依赖倒转原则(这个要求我们在程序代码传递参数或者关联关系的时候一定要让抽象(接口或者抽象类)去做,包括变量类型声明,参数类型声明,数据类型转换等等,不要让具体类去做)
为了确保这个原则的应用,一个具体的类只能实现接口或者抽象类里定义的方法,不要自己增加方法,否则在使用抽象指向具体类的时候无法使用子类里独有的方法。
4、接口隔离原则(多个任务写多个接口去实现,一个接口只实现一个任务,总之,干好自己的任务,别人的任务自己别管)。
5、达米特法则,又称最少知道原则(一个类只管好自己的事情,尽量不要和别的类有任何的联系)
6、合成/聚合原则(类与类之间的关系最好是相互关联,尽量少用继承的关系,否则你在后期维护的时候牵一发而动全身)。
设计模式的分类:
![](https://i-blog.csdnimg.cn/blog_migrate/73c0fab1926ff928e3b068f33fe0df23.png)
之间的相互关系:
![](https://i-blog.csdnimg.cn/blog_migrate/d5e8346eb5d6e67b9ff5e03635eb0dd7.png)
1、工厂模式(factory):
工厂模式:隐藏复杂的逻辑关系,强调最终得到的结果。
分为简单工厂模式,工厂方法模式,抽象工厂模式。
简单工厂模式:
客户端:
工厂:
![](https://i-blog.csdnimg.cn/blog_migrate/77983d5968187e2b8aebf2fc40729ad8.png)
工厂方法模式:
客户端:
![](https://i-blog.csdnimg.cn/blog_migrate/0ae83701e1d5751e2d1f0c12bdecab34.png)
工厂:(这里是让具体的工厂的实现)
![](https://i-blog.csdnimg.cn/blog_migrate/77c2f851e3beef32f913d31ca36c3b72.png)
抽象工厂模式:
工厂:
具体实现:
![](https://i-blog.csdnimg.cn/blog_migrate/9c71651dd3cf4e28aa0143b734ace625.png)
客户端:
![](https://i-blog.csdnimg.cn/blog_migrate/8d31a2add9c82f5b5e4f0df698d1fcd7.png)
总结:
工厂模式强调的是结果,对中间的业务逻辑实现隐藏。
特点:结构清晰,维护方便,实现松耦合。
2、单例模式(singleton):
单例,即在程序开始到结束,目标类只允许有一个实例。
分为:饿汉模式和懒汉模式。
饿汉模式:
![](https://i-blog.csdnimg.cn/blog_migrate/4af3e9c7d74d443b490f25a66aab82d7.png)
懒汉模式:
强化版:
![](https://i-blog.csdnimg.cn/blog_migrate/746a5361a26a03df99ae848b50f31663.png)
3、代理模式(proxy):
代理模式:是有一些事情我们需要做但是不想做,我们就可以找代理帮忙。
分为:静态代理,JDK动态代理,CGLIB的动态代理
静态代理:
客户端(被代理对象):
![](https://i-blog.csdnimg.cn/blog_migrate/4ee0d1a1cf239408b2bb3dc7bab34651.png)
代理对象:
![](https://i-blog.csdnimg.cn/blog_migrate/1223e9996c75634653ede5ab17adc2ae.png)
最终实现:
![](https://i-blog.csdnimg.cn/blog_migrate/b5c8d6b2c70b7ac354a6afce2ab36242.png)
JDK动态代理:
被代理对象(必须实现接口,这是JDK动态代理的特点):
![](https://i-blog.csdnimg.cn/blog_migrate/adbebded8e12ebe997deaa71ab725483.png)
代理对象:
![](https://i-blog.csdnimg.cn/blog_migrate/55a795d73c388afcfa98c1466eee2712.png)
最终实现:
![](https://i-blog.csdnimg.cn/blog_migrate/2f806c6ffa7439e8e678800617ff5463.png)
CGLIB动态代理:
被代理类:
![](https://i-blog.csdnimg.cn/blog_migrate/da1e2bef8bfddc0dc45bf4779b88d546.png)
代理类:
![](https://i-blog.csdnimg.cn/blog_migrate/40a36436c4b47395604a45c408f9c967.png)
总结:
代理的原则是实现字节码重组,让代理类和被代理类产生依赖关系实现代理。
区别:
1.jdk必须要实现接口,cglib不需要。
2.jdk不需要任何依赖包,cglib需要cglib自身的类库
3.jdk是对接口生成代理类,不针对类,cglib是真正对类动手,生成其子类。
4、策略模式(strategy):
策略模式强调的是选择,比如付款,我们可以通过支付宝,微信,银联付款等等,目的是一样的,采取什么样的策略取决于我们的选择。
订单:
![](https://i-blog.csdnimg.cn/blog_migrate/5bbf3d31eb961c00ba81178b98e18bd8.png)
支付方式和过程(采用枚举的方式配置好选项):
![](https://i-blog.csdnimg.cn/blog_migrate/82f04d23ed2cdf19ffbfbe1e142d9836.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7142b1227bca62d584cb6b33e6fb1e59.png)
最终实现:
![](https://i-blog.csdnimg.cn/blog_migrate/9ccee66e3aa7a38d695e1415eaf2b105.png)
5、委派模式(delegate):
委派模式不属于23种常规的设计模式之列,但是还是比较常用的,比如在SpringIOC对Bean的装配过程中。
委派模式强调的是上级提出由下级实现 或者说指派 这一过程。
举例:老板安排任务给项目经理,项目经理会根据任务的情况具体分给哪个人去做,他本身是不干活的。
老板:
![](https://i-blog.csdnimg.cn/blog_migrate/99dc68e6a4d932c2eef34e7b05e4cb2f.png)
项目经理:
![](https://i-blog.csdnimg.cn/blog_migrate/fab7fac41d54c05e269b230ed5fde4b9.png)
员工:
![](https://i-blog.csdnimg.cn/blog_migrate/6e5e031cad84db4f2c1834d8857ab146.png)
6、模板模式(template):
模板模式强调的是我们可以根据一套固定的算法流程,通过编写自定义的部分获得我们想要的结果。
举例:一台榨汁机,我们需要什么果汁就把什么水果放进去就可以了。榨汁机榨汁过程你是没办法改变的,所以说榨汁机就算是一个模板。
纯JDBC数据库连接:
![](https://i-blog.csdnimg.cn/blog_migrate/4428f80d7d5ef7773d6e1d8ab268f2b8.png)
查找我们想要的信息并实现映射:
![](https://i-blog.csdnimg.cn/blog_migrate/b985c77c7ce3f959fb933c518bcc59cf.png)
红框内容是我们可以改变的"原料"
最终实现的结果:
![](https://i-blog.csdnimg.cn/blog_migrate/ddfef4835032ee0c238af1d19393728f.png)
7、适配器模式(adapter):
适配器模式是将一个接口转化为客户期望的另一个接口,使那些原本接口不兼容不能在一起工作的类能在一起工作。
分为:对象组合和类继承。
举例:我们的笔记本电脑是三项插头的,但是你面对一个二项插孔的时候,是不是很绝望,我们需要适配器给他转化一下。
对象组合:
![](https://i-blog.csdnimg.cn/blog_migrate/a59cbf97774d56cdd54de82e5477361b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e52bbb56b79552259d32a0f040ec8b54.png)
转化:
![](https://i-blog.csdnimg.cn/blog_migrate/985ca72e6785ff92b4c64edf74d9654d.png)
最终实现:
![](https://i-blog.csdnimg.cn/blog_migrate/8e847fd1504179e2e3884023dfb8f357.png)
类继承:
![](https://i-blog.csdnimg.cn/blog_migrate/82b5e9243b8f24cf5e483aa6a1435668.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8bca1919f0a24df0705dbbed7e5c5033.png)
总结:
对象适配器:是把"被适配者"作为一个对象组合到适配器类中,以修改目标接口包装被适配者。
类适配器:通过多重继承不兼容接口,实现对目标接口的适配,单一的为某个类实现适配。
强调的是我们把不兼容的东西弄成兼容的。
特点:
1、通过适配器,客户端可以调用同一个接口,对客户端来说是透明的。
2、复用了现存的类,解决了现存类和复用类环境要求不一致的问题。
3、将目标类和适配者类解耦,通过一个适配器类重用现有的适配者类,无需修改原来的代码。
8、观察者模式(observer):
N个订阅者订阅了一个发布者,当发布者发布数据时,订阅者会对自己本地的数据做出实时更新。
分为推模型和拉模型。
拉模型:
目标对象:
目标对象的实现:
![](https://i-blog.csdnimg.cn/blog_migrate/a885d8a883ac4fff7b49b211e3e5d4eb.png)
订阅者:
![](https://i-blog.csdnimg.cn/blog_migrate/0ff9d6e48b6108ad938aa794552b541e.png)
客户端:
![](https://i-blog.csdnimg.cn/blog_migrate/80b1fa3b47922e68e52eda361fe2b042.png)
推模型:
目标对象:
![](https://i-blog.csdnimg.cn/blog_migrate/a630d7e88385a02a22a8ac0fcd70f229.png)
订阅者:
![](https://i-blog.csdnimg.cn/blog_migrate/8b109d7b925156d3f469d86361cb59c4.png)
Java自带的观察者模式实现类(拉模式为例):
目标对象:
![](https://i-blog.csdnimg.cn/blog_migrate/4a0cb6397e99932ede1a9b698103930b.png)
订阅者:
![](https://i-blog.csdnimg.cn/blog_migrate/f07c1e357a5ef76821eb8d919adb93d4.png)
客户端:
![](https://i-blog.csdnimg.cn/blog_migrate/0b3255be8f68c47732bb225b3cb6c6af.png)
总结:
目标对象:提供三个方法,增加订阅者,删除订阅者,和提示订阅者。
订阅者:只有一个修改的方法,接收到发布者的提醒并实现数据修改。
拉模型:就是发布者不知道订阅者需要什么东东,索性就把自己这个对象给过去,订阅者从对象里取到想要的东西。
推模型:发布者知道订阅者只想要天气情况,所以把描述天气情况的字符串传过去。
可以看出推模型的局限性比较大,遇到别的情况,需要重写update方法
。
Java中的观察者模式:
发布者要继承Observable类
订阅者要实现Observer接口
发布者修改状态并通知之前一定要加上setChange()
优点:
1、他实现了观察者和目标之间的抽象耦合
2、观察者模式实现了动态联动
3、支持广播通信
缺点:
并不是所有的订阅者都需要修改状态,所以说会引起无谓的操作,影响性能,更或者影响业务数据。
---------------------------------------------------------------------------------------------------------------