# JAVA-设计模式
2个最牛B的设计模式文章参考
# 1. 六大设计原则
单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】
一个类只负责一项职责。
里氏替换原则【LISKOV SUBSTITUTION PRINCIPLE】
子类可以扩展父类的功能,但不能改变父类原有的功能。尽量别重写!
依赖倒置原则【DEPENDENCE INVERSION PRINCIPLE】:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程。
接口隔离原则【INTERFACE SEGREGATION PRINCIPLE】
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。不要让某个类被迫实现它不需要使用的方法。
迪米特法则【LOW OF DEMETER】
高内聚,松耦合。不要和"陌生人"说话、只与你的直接朋友通信
开闭原则【OPEN CLOSE PRINCIPLE】
对扩展开放,对修改封闭。
组合/聚合复用原则【Composition/Aggregation Reuse Principle(CARP) 】(注意,这条原则有些书不讲)
尽量使用组合和聚合少使用继承的关系来达到复用的原则。
# 2. 23种设计模式(GOF23)
设计模式的本质,就是避免一坨代码的屎山,if else无线扩展的人间地狱。设计模式本身是对设计原则的实践和应用。
设计模式分为三种类型,共23种:
- 创建型模式:单例模式 (opens new window)、抽象工厂模式 (opens new window)、建造者模式 (opens new window)、工厂模式 (opens new window)、原型模式 (opens new window)。
- 结构型模式:适配器模式 (opens new window)、桥接模式 (opens new window)、装饰模式 (opens new window)、组合模式 (opens new window)、外观模式 (opens new window)、享元模式 (opens new window)、代理模式 (opens new window)。
- 行为型模式:模版方法模式 (opens new window)、命令模式 (opens new window)、迭代器模式 (opens new window)、观察者模式 (opens new window)、中介者模式 (opens new window)、备忘录模式 (opens new window)、解释器模式 (opens new window)、状态模式 (opens new window)、策略模式 (opens new window)、责任链模式 (opens new window)、访问者模式 (opens new window)。
# 2.1 创建型模式
# 2.1.1 单例模式
What
只创建一次实例的模式。
Why
为了解决全局使用的类被频繁创建销毁导致影响整体代码性能。
Why not
Q:为啥不用全局静态变量?
A:全局静态变量搞多了太占用资源(内存),而且生命周期很长。
How
- 静态类使用:如,某个类里面放个static Map 作为全局公用的缓存
- 懒汉模式(线程不安全):「懒汉」,用的时候再new 对象。
- 懒汉模式(线程安全):方法加synchronized上锁。缺点是占用资源,一般不用这个破方法。
- 饿汉模式(线程安全):比如打游戏时,我根本用不到某些地图,但也在启动的时候给我全部加载在内存了。
- 双重检验锁(线程安全):改进懒汉线程不安全问题的一种解决办法。对new 对象加synchronized,内外两次判断对象是否为null.
- 静态内部类方法(线程安全):既懒,又不占资源,还线程安全,推荐。
- 枚举:最牛B的解决办法。但是,它不支持继承。
Where
配置类(CacheConfig),线程池、缓存、日志记录器、数据库连接池
# 2.1.2 简单工厂模式(不属于GOF23,仅供学习)
What
简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
Why
在创建一个对象时不向客户暴露内部细节。不要让客户端去处理逻辑,让工厂来帮助解决。
How
Product产品接口(或是抽象类),下面有多个Product实现类(或子类),用一个Factory类来处理,要创建哪个Product实现类(通过传入参数如type等来处理业务逻辑,来区分)
Where
通过User-agent判断调用端类型,搞一个DeviceFactory类来处理。
# 2.1.3 工厂模式
What
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
说人话就是,产品有不同的子产品,工厂也可以有不同的分厂,让他们各司其职。
Why
在简单工厂模式使用时,发现以下几个问题。
工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
为解决上述问题,发明了一种新的设计模式:工厂方法模式。
How
- 步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
- 步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
- 步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
- 步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
- 步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
Where
网页导出数据,支持导出各类格式,文本格式、数据库备份形式、Excel格式、Xml格式等。搞一个工厂模式。
# 2.1.4 抽象工厂模式
What
提供一个接口,用于创建 相关的对象家族 。
这个个人感觉用的不太多,因为它只适应于特大型系统架构设计,一般中小型公司的业务基本用不上。
TODO....
# 2.1.5 建造者模式(生成器模式)(Builder)
What
封装一个对象的构造过程,并允许按步骤构造。
相当于是对工厂生产产品的一种装配,由于这种装配可能随时改变,所以需要抽取出来,实现产品局部与整体的解耦
Why
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,可以考虑使用建造者模式。
How
可以参考OkHttpClient当中的builder,记住,在类中定义内部类Builder然后采用链式操作是比较好的方法。
Where
StringBuilder 、OkHttpClient Builder、GsonBuilder
OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(httpLoggingInterceptor) .connectTimeout(30, TimeUnit.SECONDS) .build();
# 2.1.6 装饰器模式(Decorator)
What
不改变原有类,对类进行扩展,相当于做个套娃,可拥有自己设计的功能。
它也是开闭原则的应用。
Why
一开始想到用继承,但会导致子类过多的问题,又想到AOP切面,但是AOP会带来更复杂的问题。所以....装饰一下就行。
How
创建一个装饰类实现原有类的接口,把接口丢进装饰类的构造方法中,然后在装饰类重写接口方法。
Where
InputStream(字节流、字符流)
# 2.1.6 适配器模式(Adepter)
What
把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类可以在一起工作。如手机适配器(将交流电转化为直流电)。
Why
原有的接口不能直接使用。所以需要一个转换类。
How
将原有类聚合到适配器类,然后创建一个类似转换的方法,以后客户端直接调用转换方法就可以了。
Where
InputStream(字节流、字符流)