理解设计模式前的前堤:
1、模式的应用目标是把可维护性作为很重要指标的程序,像一次性的Demo程序就不需要多高的可维护性;
2、意识到并认可面向接口编程的好处,不认可请回看1;
3、设计模式的本质是解耦,解耦的根本手段是分层,分的层越多,关系越不直观;
4、不用设计模式完全不影响实现需求,只是写的代码多了,重构多了,写着写着,也就进入了设计模式的“窠臼”。
创建型模式
简单工厂
算不是上设计模式,是一种常见的编码模式。将new一个对象实例的过程用一个静态的简单工厂方法封装起来,这样client端不依赖于具体实现类Impl,换实现类时只需要修改这个静态方法一处即可,实现了初步的解耦。我认为在应用场景上,简单工厂适合应用在条件固定的情况,比如性别,分类最多也就是:男、女、不详,修改的概率基本没有。另外反射的使用,也使得简单工厂的可扩展性更强,例如:Class.forName方法。
工厂方法
简单工厂的缺点是是当需要增加新的实现类Impl时,要修改静态简单工厂方法,即Impl类与简单工厂类的这个方法耦合了。试想一下如果你的简单工厂方法用在简单项目也就算了,如果你写的是一个框架,你让调用方怎么扩展?如果我让不同的Impl类依赖不同的工厂,那调用方要扩展的时候只需要加个Impl类,加个对应的工厂类,由调用方决定调用不同的工厂产生实例,这就是工厂方法。
抽象工厂
工厂方法的一个很明显的缺点是工厂类太多了,如何组织合并这些工厂呢?比如小米生产笔记本电脑+手机,华为也生产笔记本电脑+手机,那按品牌分的话工厂有两个,一个是小米工厂,一个是华为工厂。按产品分的话工厂有两个,一个是手机工厂,一个是电脑工厂。一般认为产品是手机和笔记本、而小米还是华为,只是这个产品的某个属性:品牌。所以抽象工厂应该是小米工厂和华为工厂,这两个工厂分别有两个方法,分别是生产笔记本电脑和生产手机。
单例模式
没什么好说的,写过代码的人都懂。值得一提的是spring IOC的controller、service、dao默认都是单例的。
原型模式
用过深拷贝的都懂。值得一提的是,我个人认为两个不同类型的对象间相同名字的字段copy也算是原型模式的一种应用,比如BeanUtils.copyProperties。应用场景一般是通过从一个原型里copy出一个另一新对象,然后增减相应字段形成目标对象。
建造者模式
用过Jooq、Mybatis generator或者Activiti的应该对链式调用不陌生,其实链式调用用的就是建造者模式,只是Director的职责由Client端担任了。
例如:new BaseResponse.Builder(1).setData(data).setMessage("moext").build()
应用场景也很常见,就是有多个属性,某些属性可选的情况,当构造函数重载很多的时候就可以考虑用建造者模式重写了,那些动不动就4个以上参数的构造函数对调用方来说是极不友好的。
结构型模式
适配器模式
适配器模式也是用得相对较多的一种模式,比如最常用的接口编程中DTO到数据库表实体Entity之间的转换,还各种协议转换的编码等。
享元模式
享元模式也是企业项目中必用的一种模式,各种池,比如线程池,连接池等都是享元模式的应用。
代理模式
JDK自带的代理对接口生成代理类,Cglib的动态代理更是直接用字节码增强的方式来实现,相比之下JDK自带的代理更符合代理模式,一般情况下可以用代理类对被代理类进行增强,外部对被代理类的访问直接通过代理类进行。
装饰模式
装饰者模式和代理模式在结构上相似,但用处不同,装饰者模式通过对原对象进行包装,大多是装饰链的应用,比如一个毛坯房,通过水电装饰、抹灰模式、刷漆模式、软装模式,最终达到精装房的效果,在这个过程中毛坯房对象、硬装房对象、精装房对象都是可以访问的,而代理模式中,一般只可以通过访问代理对象达到访问被代理对象的效果。装饰者模式在BIO中被广泛应用。这个模式在日常编码中的应用需要结合建模,将原始模型通过一步步装饰,达成目标模型,同时在每一步装饰出来的对象都可以访问。当然你爱用面向过程的方式,step by step的加装饰效果也不是说不可以实现的,不用设计模式不影响实现需求的编码。
外观模式
通俗一点的例子就是汽车发动,对司机来说就是一个open方法,而在内部,则有电力系统的open,燃油系统的open、动力系统的open等,由于司机并不关心汽车内部的发动细节,他只关注整个汽车有个统一的open方法。外观模式在GUI编程中直接应用得很多,在合适的建模下,其实日常应用中也可以有意识的运用,运用的关键在于使用者的角度看需不需要关注组件细节。
组合模式
组合模式先要将结构抽象成树,比如文件系统,按分类有:目录,子目录,文件,首先需要抽象出节点的概念,这个节点可以是目录、子目录或文件,然后形成树状结构。常见应用在组织架构、菜单、文件系统等树状结构中来表示部分与整体的层次关系。
桥接模式
就是在抽象部分与具体部分通过“桥”接起来,使两个部分可以独立变化而不互相影响。当你发现在编程中要考虑X*Y笛卡尔积种情况的时候就可以考虑是否用桥接模式,比如咖啡容量有大、中、小杯,口味有:原味、加奶、加糖 三种,如果你用继承来实现的话,有3 * 3=9种实现类。而用桥接模式,可以把容量定为的抽象部分,口味定为实现部分。咖啡类通过引用口味类达到给咖啡添加各类口味的效果。一杯加奶大杯咖啡就是 new LargeCoffee(new Milk()); 可以看到,加口味时,只需要添加一个口味实现,加容量时,需要加一个容量实现类,口味和容量可以独立地变化。那什么情况下选择抽象部分什么情况下选择实现部分呢?一般来说抽象部分是较稳定纬度或者是有限的客观的事物,比如对电商公司来说:new Phone(new HuaWei()),就比new HuaWei(new Phone())更好,因为手机这个客观的事物相对品牌而言会更加稳定。
下篇介绍行为型模式