23 种经典设计模式共分为 3 种类型,分别是创建型、结构型和行为型。
一、创建型设计模式
创建型设计模式包括:单例模式、工厂模式、建造者模式、原型模式。它主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。
1. 单例模式
单例模式用来创建全局唯一的对象。一个类只允许创建一个对象(或者叫实例),那这个类就是一个单例类,这种设计模式就叫作单例模式。单例有几种经典的实现方式,饿汉式、懒汉式、双重检测、静态内部类、枚举。
也有人把单例当作反模式,有一些替代的方案来实现全局唯一类。比如,通过工厂模式、IOC 容器来保证全局唯一性。
2. 工厂模式
工厂模式包括简单工厂、工厂方法、抽象工厂这 3 种细分模式。其中,简单工厂和工厂方法比较常用,抽象工厂的应用场景比较特殊,很少用到。
工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。实际上,如果创建对象的逻辑并不复杂,直接通过 new 来创建对象就可以了,不需要使用工厂模式。当创建逻辑比较复杂,是一个“大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。
当每个对象的创建逻辑都比较简单的时候,使用简单工厂模式,将多个对象的创建逻辑放到一个工厂类中。当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的工厂类,使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。
一个非常经典的应用场景:依赖注入框架,比如 Spring IOC、Google Guice,用来集中创建、组装、管理对象,跟具体业务代码解耦,让程序员聚焦在业务代码的开发上。DI 框架已经成为了开发的必备框架。
3. 建造者模式
建造者模式用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。
如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,可以通过构造函数配合 set() 方法来解决。但如果存在下面情况中的任意一种,就要考虑使用建造者模式了。
a.若必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就就会出现参数列表很长的问题。
b.若类的属性之间有一定的依赖关系或者约束条件,使用构造函数配合 set() 方法的设计思路,那这些依赖关系或约束条件的校验逻辑就会无处安放。
c.若要创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,要实现这个功能,就不能在类中暴露 set() 方法。构造函数配合 set() 方法来设置属性值的方式就不适用了。
4. 原型模式
如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式,来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型模式。
原型模式有两种实现方法,深拷贝和浅拷贝。浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象……而深拷贝得到的是一份完完全全独立的对象。所以,深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间。
如果要拷贝的对象是不可变对象,浅拷贝共享不可变对象是没问题的,但对于可变对象来说,浅拷贝得到的对象和原始对象会共享部分数据,就有可能出现数据被修改的风险。所以,应该尽量使用深拷贝。
总的来说,创建型模式,可理解为把对象的创建与使用解耦,使代码简洁、易拓展,变复杂为简单。
网友评论