# 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种:

# 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. 步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
  2. 步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
  3. 步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
  4. 步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
  5. 步骤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(字节流、字符流)

参考 (opens new window)

# 2.1.6 适配器模式(Adepter)

What

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类可以在一起工作。如手机适配器(将交流电转化为直流电)。

Why

原有的接口不能直接使用。所以需要一个转换类。

How

将原有类聚合到适配器类,然后创建一个类似转换的方法,以后客户端直接调用转换方法就可以了。

Where

InputStream(字节流、字符流)

参考 (opens new window)