设计模式对于程序开发的重要性不言而喻,我也一直尝试深入学习,但由于自身水平的拙劣,每次学完就忘,并没理解透彻。整理这篇文章主要想通过现有资料和网络博文,重新学习一下设计模式。主要参考的文献资料有:
- 掘金作者JavaDoop的博文:设计模式——Java https://juejin.im/post/6844903695667167240
- 菜鸟教程专题:设计模式 https://www.runoob.com/design-pattern/design-pattern-tutorial.html
- GitHub项目:📚 Java 23种设计模式全归纳 https://github.com/youlookwhat/DesignPattern
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式分为三种类型,共23种:
- 创建型模式(5):单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
- 结构型模式(7):适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式(11):模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式、访问者模式。
### 1、创建型模式
***
#### 1.1、单例模式
单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。定义单例模式只需要三步就可以保证对象的唯一性。
- (1) 不允许其他程序用new对象;
- (2) 在该类中创建对象;
- (3) 对外提供一个可以让其他程序获取该对象的方法。
单例模式也分为饿汉式、懒汉式两种,实现方式冬种多样,这里介绍几种常见的实现:
- **简单饿汉式**:
它基于类加载机制避免了多线程的同步问题。不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
```java
package Singleton;
public class SingletonHungry {
//第一步:不允许其他程序用new对象,即通过private修饰无参构造器;
private SingletonHungry() {}
//第二部:在内部创建类的实例;
private static SingletonHungry instance = new SingletonHungry();
//第三步:创建获取该类的实例的方法
public static SingletonHungry getInstance() {
return instance;
}
}
```
- **简单懒汉模式**:
种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程情况下不能正常工作。
```java
package Singleton;
public class SingletonLazy {
//第一步:不允许其他程序用new对象,即通过private修饰无参构造器;
private SingletonLazy() {}
//第二部:在内部创建类的实例;
private static SingletonLazy instance;
//第三步:创建获取该类的实例的方法
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
```
- **线程安全懒汉模式**:
这种方式具备很好的lazy loading,能够在多线程中很好的工作.实现了双检锁/双重校验锁(DCL,即 double-checked locking)。两次检查instance是否非空。
```java
package Singleton;
public class SingletonLazy {
//第一步:不允许其他程序用new对象,即通过private修饰无参构造器;
private SingletonLazy() {}
//第二部:在内部创建类的实例,用volatile关键字修饰,实现线程安全;
private static volatile SingletonLazy instance;
//第三步:创建获取该类的实例的方法
public static SingletonLazy getInstance() {
if (instance == null) {
//加锁
synchronized(SingletonLazy.class) {
//再次判断是否非空,加锁后不判空会出现线程安全问题
if(instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
}
```
- **嵌套类实现**:
这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方:在饿汉式方式是只要Singleton类被装载就会实例化;内部类是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类。优点:避免了线程不安全,延迟加载,效率高。
```java
package Singleton;
public class SingletonIn {
//关闭用new创建对象
private SingletonIn() {};
//定义内部类,创建类的实例
private static class Hodler{
private static SingletonIn instance = new SingletonIn();
}
//定义获取类的实例的方法
public static SingletonIn getInstance() {
return Hodler.instance;
}
}
```
- **枚举实现**:
这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
```java
package Singleton;
public class SingletonEnum {
private SingletonEnum() {}
public static SingletonEnum getInstance() {
return Singleton.INSTANCE.getInstance();
}
/**
* 使用枚举方法实现单例模式
*/
private enum Singleton {
INSTANCE;
private SingletonEnum instance;
/**
* JVM保证这个方法绝对只调用一次
*/
Singleton() {
instance = new SingletonEnum();
}
public SingletonEnum getInstance() {
return instance;
}
}
}
```
#### 1.2、简单工厂模式
通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类或接口。之所以称之为简单工厂模式,是因为只有一个工厂。
```java
package simpleFactory;
/**
* 图形工厂
* @author 面条
*
*/
public class ShapeFactory {
/**
* 根据传入不同的类型创建不同图形类的对象
* @param type
* @return
*/
public Shape drawShape(String type) {
Shape shape = null;
switch (type) {
case "Triangle":
shape = new Triangle();
break;
case "Circle":
shape = new Circle();
break;
case "Polygon":
shape = new Polygon();
break;
}
return shape;
}
}
```
```java
package simpleFactory;
/**
* 不同图形类的公共接口
*
* @author 面条
*
*/
public interface Shape {
public void draw();
}
```
```java
package simpleFactory;
/**
* 三角形
* @author 面条
*
*/
public class Triangle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a triangle!!!!!");
}
}
```
```java
package simpleFactory;
/**
* 圆形
* @author 面条
*
*/
public class Circle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a circle!!!!!");
}
}
```
```java
package simpleFactory;
/**
* 多边形
* @author 面条
*
*/
public class Polygon implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a Polygon!!!!!");
}
}
```
测试一下该画图形的简单工厂:
```java
package simpleFactory;
public class Test {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//画三角形
shapeFactory.drawShape("Triangle").draw();
//画圆
shapeFactory.drawShape("Circle").draw();
//画多边形
shapeFactory.drawShape("Polygon").draw();
}
}
```
结果为:
```
Draw a triangle!!!!!
Draw a circle!!!!!
Draw a Polygon!!!!!
```
#### 1.3、工厂模式
简单工厂模式很简单,定义一个创建对象的接口,但由实现该接口的类来决定要实例化的对象是哪一个。工厂方法模式把类实例化的过程推迟到接口的实现类。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。
举个例子:比如图形中有圆形、三角形、多边形,但是圆形、三角形、多边形分别都有黄色的、红色的绿色的,那么通过不同的工厂方法就可以创建不同颜色的图形的对象。
工厂的创建:
```java
package Factory;
/**
* 图案工厂接口
* @author 面条
*
*/
public interface ShapeFactory {
public Shape drawShape(String shape);
}
/*****************************************************************************
package Factory;
/**
* 红色图案的工厂
* @author 面条
*
*/
public class RedShapeFactory implements ShapeFactory {
@Override
public Shape drawShape(String type) {
// TODO Auto-generated method stub
Shape shape = null;
switch (type) {
case "Triangle":
shape = new RedTriangle();
break;
case "Circle":
shape = new RedCircle();
break;
case "Polygon":
shape = new RedPolygon();
break;
}
return shape;
}
}
/****************************************************************************
package Factory;
/**
* 绿色图案的工厂
* @author 面条
*
*/
public class GreenShapeFactory implements ShapeFactory {
@Override
public Shape drawShape(String type) {
// TODO Auto-generated method stub
Shape shape = null;
switch (type) {
case "Triangle":
shape = new GreenTriangle();
break;
case "Circle":
shape = new GreenCircle();
break;
case "Polygon":
shape = new GreenPolygon();
break;
}
return shape;
}
}
```
图案的接口和类的创建:
```java
package Factory;
/**
* 不同图形类的公共接口
*
* @author 面条
*
*/
public interface Shape {
public void draw();
}
/**************************************************************************
package Factory;
/**
* 红色的圆形
* @author 面条
*
*/
public class RedCircle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a red circle!!!!!");
}
}
/************************************************************************
package Factory;
/**
* 红色的三角形
* @author 面条
*
*/
public class RedTriangle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a red triangle!!!!!");
}
}
/************************************************************************
package Factory;
/**
* 红色的多边形
* @author 面条
*
*/
public class RedPolygon implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a red Polygon!!!!!");
}
}
/************************************************************************
package Factory;
/**
* 绿色的圆形
* @author 面条
*
*/
public class GreenCircle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a green circle!!!!!");
}
}
/************************************************************************
package Factory;
/**
* 绿色的多边形
* @author 面条
*
*/
public class GreenPolygon implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a green Polygon!!!!!");
}
}
/************************************************************************
package Factory;
/**
* 绿色的三角形
* @author 面条
*
*/
public class GreenTriangle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Draw a green triangle!!!!!");
}
}
```
测试一下该工厂模式:
```java
package Factory;
public class Test {
public static void main(String[] args) {
ShapeFactory redShapeFactory = new RedShapeFactory();
//画红色三角形
redShapeFactory.drawShape("Triangle").draw();
//画红色圆
redShapeFactory.drawShape("Circle").draw();
//画红色多边形
redShapeFactory.drawShape("Polygon").draw();
ShapeFactory greenShapeFactory = new GreenShapeFactory();
//画绿色三角形
greenShapeFactory.drawShape("Triangle").draw();
//画绿色圆
greenShapeFactory.drawShape("Circle").draw();
//画绿色多边形
greenShapeFactory.drawShape("Polygon").draw();
}
}
```
结果如下:可以看到对于不同的工厂,输入相同的type,会画出不同的颜色的图案。
```java
Draw a red triangle!!!!!
Draw a red circle!!!!!
Draw a red Polygon!!!!!
Draw a green triangle!!!!!
Draw a green circle!!!!!
Draw a green Polygon!!!!!
```
#### 1.4、抽象工长模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。抽象工厂模式包含如下角色:
- AbstractFactory:抽象工厂
- ConcreteFactory:具体工厂
- AbstractProduct:抽象产品
- Product:具体产品

概念理解起来可能有点绕,那就直接说例子。
假设你是一个geek,你想组装一台电脑,电脑是由主板、显示器等构成的,所以你得自己从市面上购买这些零件然后组装。你可以选择的电脑配件品牌有很多,例如Lenovo、Dell等,这些品牌都是在自家的生产工厂中生产配件,然后买给你。这里我们可以将主板、显示器视为抽象产品,将Lenovo 显示器、Dell 显示器、Lenovo 主板、Dell 主板视为具体产品,将我们选购的过程理解为获取特定电脑配件生产工厂的工厂(抽象工厂),然后将Lenovo、Dell视为电脑配件生产工厂(具体工厂),这些生产工厂又具有自己生产主板、显示器的方法。
创建抽象工厂和具体工厂:
```java
package abstractFactory;
/**
* 抽象工厂
*
* @author 面条
*
*/
public abstract class ComputerFactory {
public abstract MainBoard createMainBoard();
public abstract Monitor createMonitor();
}
/***************************************************************************
package abstractFactory;
/**
* Dell工厂
* @author 面条
*
*/
public class DellComputerFactory extends ComputerFactory {
@Override
public MainBoard createMainBoard() {
// TODO Auto-generated method stub
System.out.println("开始生产Dell主板~~~~");
DellMainBoard dellMainBoard = new DellMainBoard();
System.out.println("Dell主板生产成功~~~~");
return dellMainBoard;
}
@Override
public Monitor createMonitor() {
// TODO Auto-generated method stub
System.out.println("开始生产Dell显示器~~~~");
DellMonitor dellMonitor = new DellMonitor();
System.out.println("Dell显示器生产成功~~~~");
return dellMonitor;
}
}
/***************************************************************************
package abstractFactory;
/**
* 联想工厂
* @author 面条
*
*/
public class LenovoComputerFactory extends ComputerFactory {
@Override
public MainBoard createMainBoard() {
// TODO Auto-generated method stub
System.out.println("开始生产Lenovo主板~~~~");
LenovoMainBoard lenovoMainBoard = new LenovoMainBoard();
System.out.println("Lenovo主板生产成功~~~~");
return lenovoMainBoard;
}
@Override
public Monitor createMonitor() {
// TODO Auto-generated method stub
System.out.println("开始生产Lenovo显示器~~~~");
LenovoMonitor lenovoMonitor = new LenovoMonitor();
System.out.println("Lenovo显示器生产成功~~~~");
return lenovoMonitor;
}
}
```
创建抽象产品和具体产品:
```java
package abstractFactory;
/**
* 主板抽象产品
* @author 面条
*
*/
public abstract class MainBoard {
}
/***************************************************************************
package abstractFactory;
/**
* 显示器抽象产品
* @author 面条
*
*/
public abstract class Monitor {
}
/***************************************************************************
package abstractFactory;
/**
* Dell主板
* @author 面条
*
*/
public class DellMainBoard extends MainBoard {
}
/***************************************************************************
package abstractFactory;
/**
* Dell显示器
* @author 面条
*
*/
public class DellMonitor extends Monitor {
}
/***************************************************************************
package abstractFactory;
/**
* 联想主板
* @author 面条
*
*/
public class LenovoMainBoard extends MainBoard {
}
/***************************************************************************
package abstractFactory;
/**
* 联想显示器
* @author 面条
*
*/
public class LenovoMonitor extends Monitor {
}
```
测试该抽象工厂模式:
```java
package abstractFactory;
public class Test {
public static void main(String[] args) {
//获取Dell工厂,并生产Dell主板
ComputerFactory dellComputerFactory = new DellComputerFactory();
MainBoard mainBoard = dellComputerFactory.createMainBoard();
//获取Lenovo工厂,并生产Lenovo显示器
ComputerFactory lenovoComputerFactory = new LenovoComputerFactory();
Monitor monitor = lenovoComputerFactory.createMonitor();
//组装电脑
Computer.assembleComputer(mainBoard, monitor);
}
}
```
```
开始生产Dell主板~~~~
Dell主板生产成功~~~~
开始生产Lenovo显示器~~~~
Lenovo显示器生产成功~~~~
开始组装电脑~~~~~~
组装完成!!!!!
```
**抽象工厂模式的优点:**
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
-
**抽象工厂模式的缺点:**
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
#### 1.5、建造者模式
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。经常碰见的 XxxBuilder 的类,通常都是建造者模式的产物。建造者模式其实有很多的变种,但是对于客户端来说,通常使用都是一个模式的:
```
Xxxx xxxx = new XxxxBuilder().a().b().c().builder();
```
比如下面这个例子:
```java
package builderPattern;
public class User {
// 下面是“一堆”的属性
private String name;
private String password;
private String nickName;
private int age;
// 构造方法私有化,不然客户端就会直接调用构造方法了
private User(String name, String password, String nickName, int age) {
this.name = name;
this.password = password;
this.nickName = nickName;
this.age = age;
}
// 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯,
// 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好
public static UserBuilder builder() {
return new UserBuilder();
}
public static class UserBuilder {
// 下面是和 User 一模一样的一堆属性
private String name;
private String password;
private String nickName;
private int age;
private UserBuilder() {
}
// 链式调用设置各个属性值,返回 this,即 UserBuilder
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder password(String password) {
this.password = password;
return this;
}
public UserBuilder nickName(String nickName) {
this.nickName = nickName;
return this;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
// build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。
// 当然,可以在 “复制” 之前做点检验
public User build() {
if (name == null || password == null) {
throw new RuntimeException("用户名和密码必填");
}
if (age <= 0 || age >= 150) {
throw new RuntimeException("年龄不合法");
}
// 还可以做赋予”默认值“的功能
if (nickName == null) {
nickName = name;
}
return new User(name, password, nickName, age);
}
}
}
```
测试这个建造者模式:
```java
package builderPattern;
public class Test {
public static void main(String[] args) {
User d = User.builder()
.name("foo")
.password("pass12345")
.age(25)
.build();
}
}
```
**优点**: 1、建造者独立,易扩展。 2、便于控制细节风险。
**缺点**: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
**使用场景**: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
**注意事项**:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

Java实现设计模式