1、创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
2、 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
3、行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
2.2.1.1 概念
2.2.1.2 两种单例模式的实现
/**
* 饿汉单例模式
*
* @author wangjie
* @version V1.0
* @date 2019/12/14
*/
public class SingletonDemo {
private static volatile SingletonDemo instance = new SingletonDemo();
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 构造方法");
}
/**
* @return
*/
public static SingletonDemo getInstance(){
return instance;
}
}
/**
* 懒汉单例模式
*
* @author wangjie
* @version V1.0
* @date 2019/12/14
*/
public class SingletonDemo {
private static volatile SingletonDemo instance=null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 构造方法");
}
/**
* 双重检测机制
* @return
*/
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
2.2.1.3 单例模式分析
单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含的角色只有一个,就是单例类——Singleton。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
在单例模式的实现过程中,需要注意如下三点:
1、单例类的构造函数为私有;
2、提供一个自身的静态私有成员变量;
3、提供一个公有的静态工厂方法。
2.2.1.4 单例模式适用环境
2.2.2.1 概念
2.2.2.2 分类
2.2.2.3 实现
2.2.2.3.1 简单工厂实现
1、需要创建的对象较少。
2、客户端不关心对象的创建过程。
/**
* 绘图工具
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public interface Shape {
/**
* 绘图
*/
void draw();
}
/**
* 画圆
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class Circle implements Shape {
public Circle() {
log.info("【Circle init】");
}
/**
* 圆
*/
@Override
public void draw() {
log.info("【Draw Circle】");
}
}
/**
* 正方形
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class Square implements Shape {
public Square() {
log.info("【Square init】");
}
@Override
public void draw() {
log.info("【Draw Square】");
}
}
/**
* 绘画简单工厂
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public class ShapeFactory {
/**
* 获取形状
* @param shapeType
* @return
*/
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
public static void main(String[] args) {
// 获取 Circle 的对象,并调用它的 draw 方法
Shape circle = ShapeFactory.getShape("CIRCLE");
circle.draw();
// 获取 Square 的对象,并调用它的 draw 方法
Shape square = ShapeFactory.getShape("SQUARE");
square.draw();
}
}
1、简单工厂模式可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
2、简单工厂模式包含三个角色:工厂角色负责实现创建所有实例的内部逻辑;抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口;具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
3、简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
4、简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。
5、简单工厂模式适用情况包括:工厂类负责创建的对象比较少;客户端只知道传入工厂类的参数,对于如何创建对象不关心。
2.2.2.3.2 工厂方法实现
工厂方法模式应该是在工厂模式家族中是用的最多模式,一般项目中存在最多的就是这个模式。
工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂 。
适用环境
1、一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
2、一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中
/**
* 获取形状的接口
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public interface Factory {
Shape getShape();
}
/**
* 画圆的工厂
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public class CircleFactory implements Factory {
@Override
public Shape getShape() {
return new Circle();
}
}
/**
* 画正方形的工厂
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public class SquareFactory implements Factory{
@Override
public Shape getShape() {
return new Square();
}
}
/**
* 工厂方法模型测试
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public class Test {
public static void main(String[] args) {
Factory circlefactory = new CircleFactory();
Shape circle = circlefactory.getShape();
circle.draw();
}
}
1 、工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
2、工厂方法模式包含四个角色:抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,即产品对象的共同父类或接口;具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间往往一一对应;抽象工厂中声明了工厂方法,用于返回一个产品,它是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口;具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。
3 、工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
4、工厂方法模式的主要优点是增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;其缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。
5、工厂方法模式适用情况包括:一个类不知道它所需要的对象的类;一个类通过其子类来指定创建哪个对象;将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。
2.2.2.3.3 抽象工厂实现
1、一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
2、系统中有多于一个的产品族,而每次只使用其中某一产品族。
3、属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
4、系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
/**
* 裤子
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public interface Pants {
/**
* 今天搭配那条裤子
*/
void Wear();
}
/**
* 今天穿什么鞋子
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public interface Shoes {
/**
* 今天搭配那双鞋子
*/
void Wear();
}
/**
* 耐克的裤子
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class NikePants implements Pants {
@Override
public void Wear() {
log.info("今天穿耐克的裤子");
}
}
/**
* 耐克的鞋子
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class NikeShoes implements Shoes {
@Override
public void Wear() {
log.info("今天穿耐克的鞋子");
}
}
/**
* 今天穿阿迪的裤子
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class AdidasPants implements Pants {
@Override
public void Wear() {
log.info("今天穿阿迪的裤子");
}
}
/**
* 今天穿阿迪的鞋子
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class AdidasShoes implements Shoes {
@Override
public void Wear() {
log.info("今天穿阿迪的鞋子");
}
}
/**
* 穿搭工厂
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public interface AbstractWearFactory {
/**
* 穿什么裤子
* @return
*/
Pants wearPants();
/**
* 穿什么鞋
* @return
*/
Shoes wearShoes();
}
/**
* 周一穿阿迪的裤子搭耐克的鞋
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public class MondayWearFactory implements AbstractWearFactory{
@Override
public Pants wearPants() {
return new AdidasPants();
}
@Override
public Shoes wearShoes() {
return new NikeShoes();
}
}
/**
* 周二穿耐克的裤子搭阿迪的鞋
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
public class TuesdayWearFactory implements AbstractWearFactory{
@Override
public Pants wearPants() {
return new NikePants();
}
@Override
public Shoes wearShoes() {
return new AdidasShoes();
}
}
/**
* 抽象工厂测试
*
* @author wangjie
* @version V1.0
* @date 2020/4/3
*/
@Slf4j
public class Test {
public static void main(String[] args) {
AbstractWearFactory factory = new MondayWearFactory();
Pants pants = factory.wearPants();
pants.Wear();
Shoes shoes = factory.wearShoes();
shoes.Wear();
}
}
1、抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
2、抽象工厂模式包含四个角色:抽象工厂用于声明生成抽象产品的方法;具体工厂实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中;抽象产品为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法;具体产品定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。
3、抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。
4、抽象工厂模式的主要优点是隔离了具体类的生成,使得客户并不需要知道什么被创建,而且每次可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便;主要缺点在于增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
5、抽象工厂模式适用情况包括:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节;系统中有多于一个的产品族,而每次只使用其中某一产品族;属于同一个产品族的产品将在一起使用;系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
2.2.3.1 概念
建造者模式(Builder Pattern) 又名生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
抽象工厂模式不关心构建过程,只关心什么产品由什么工厂生产,而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
例如汽车,它包括车轮、方向盘、发送机等各种部件,对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节
适用环境:
1、需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
2、需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
3、对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。
4、隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
/**
* 汽车
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public class Car {
/**
* 轮胎
*/
private String tyre;
/**
* 方向盘
*/
private String steeringWheel;
//get,set省略
}
/**
* 汽车创建抽象类
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public abstract class CarBuilder {
protected Car car = new Car();
public abstract void buildTyre();
public abstract void buildSteeringWheel();
public Car getCar(){
return car;
}
}
/**
* 宝马建造者
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
@Slf4j
public class BMWCarBuilder extends CarBuilder{
@Override
public void buildTyre() {
car.setTyre("宝马轮胎");
}
@Override
public void buildSteeringWheel() {
car.setSteeringWheel("宝马方向盘");
}
}
/**
* 奔驰建造者
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
@Slf4j
public class MercedesCarBuilder extends CarBuilder{
@Override
public void buildTyre() {
car.setTyre("奔驰轮胎");
}
@Override
public void buildSteeringWheel() {
car.setSteeringWheel("奔驰方向盘");
}
}
/**
* 指挥者
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public class Director {
private CarBuilder builder;
public Director(CarBuilder builder){
this.builder = builder;
}
public Car construct(){
builder.buildTyre();
builder.buildSteeringWheel();
return builder.getCar();
}
}
/**
* 建造者模式测试
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
@Slf4j
public class Test {
public static void main(String[] args) {
BMWCarBuilder bmwCarBuilder = new BMWCarBuilder();
Director director = new Director(bmwCarBuilder);
Car car= director.construct();
log.info("车:{}", JSON.toJSONString(car));
}
}
2.2.3.2 建造者模式总结:
1、建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2、建造者模式包含如下四个角色:抽象建造者为创建一个产品对象的各个部件指定抽象接口;具体建造者实现了抽象建造者接口,实现各个部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象;产品角色是被构建的复杂对象,包含多个组成部件;指挥者负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造
3、在建造者模式的结构中引入了一个指挥者类,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。
4、建造者模式的主要优点在于客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象,每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,符合“开闭原则”,还可以更加精细地控制产品的创建过程;其主要缺点在于由于建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,因此其使用范围受到一定的限制,如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
5、建造者模式适用情况包括:需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性;需要生成的产品对象的属性相互依赖,需要指定其生成顺序;对象的创建过程独立于创建该对象的类;隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同类型的产品。
2.2.4.1 概念
1、创建新对象成本较大(例如初始化时间长,占用CPU多或占太多网络资源),新对象可以通过复制已有对象来获得,如果相似对象,则可以对其成员变量稍作修改。
2、系统要保存对象的状态,而对象的状态很小。
3、需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的组合状态,通过复制原型对象得到新实例可以比使用构造函数创建一个新实例更加方便。
2.2.4.2 分析
2.2.4.3 原型模式代码示例:
/**
* 形状原型
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
/**
* 圆形
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public class Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public void draw() {
System.out.println("draw Circle");
}
}
/**
* 长方形
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("draw Rectangle");
}
}
/**
* 原型库
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
/**
* 原型库
*/
public ShapeCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Rectangle rectangle = new Rectangle();
rectangle.setId("2");
shapeMap.put(rectangle.getId(),rectangle);
}
public Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
}
/**
* 原型模式测试
*
* @author wangjie
* @version V1.0
* @date 2020/4/4
*/
public class Test {
public static void main(String[] args) {
ShapeCache shapeCache = new ShapeCache();
Shape clonedShape = (Shape) shapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) shapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
}
}