我们传统的23种设置模式如下
- 创建型模式:用于创建对象
- 工厂方法(Factory Method) 模式
- 抽象工厂(Abstract Factory) 模式
- 原型(Protptype) 模式
- 单例(Singleton) 模式
- 构建器模式
- 结构型模式:建立更大的结构
- 适配器(Adapter)模式
- 桥接(Bridge)模式
- 组合(Composite)模式
- 装饰(Decorator)模式
- 外观(Facade)模式
- 享元(Flyweight)模式
- 代理(Proxy)模式
- 行为型模式:交互及职责分配
- 责任链(Chain of Responsibility)模式
- 命令(Command)模式
- 解释器(Interpreter)模式
- 迭代器(Iterator)模式
- 中介者(Mediator)模式
- 备忘录(Memento)模式
- 观察者(Observer)模式
- 状态(State)模式
- 策略(Strategy)模式
- 模板方法(Temmlate)模式
- 访问者(Visotor)模式
本篇文章主要说明创建型模式,它的类图,设计思想以及Java代码的实现。
1、工厂方法模式
简要说明
定义一个创建对象的接口,但是子类决定需要实例化哪一个类。工厂方法使得子类实例化的过程推迟。
速记关键字
动态生产对象
通俗的讲,就是我们创建一个工厂类,我们要创建什么对象,传一个参数给工厂,工厂就负责创建一个对象返回给我。
类图如下
Java代码实现
/**
* 产品接口
*/
public interface IProduct {
/**
* 生产产品的方法
*/
void product();
}
public class Product1 implements IProduct{
@Override
public void product() {
System.out.println("生产产品1:手机");
}
}
public class Product2 implements IProduct{
@Override
public void product() {
System.out.println("生产产品2:电脑");
}
}
public class Product3 implements IProduct{
@Override
public void product() {
System.out.println("生产产品3:平板");
}
}
/**
* 工厂方法类
*/
public class Creator {
/**
* 根据产品类型生产产品 该类一般是静态类
* @param productType 产品类型
* @return 产品
*/
public static IProduct factory(Integer productType){
if (productType == 1){
return new Product1();
}
if (productType == 2){
return new Product2();
}
if (productType == 3){
return new Product3();
}
throw new RuntimeException("请传入正确的产品类型");
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 创建产品1
IProduct product1 = Creator.factory(1);
// 创建产品2
IProduct product2 = Creator.factory(2);
// 创建产品3
IProduct product3 = Creator.factory(3);
// 打印一下,看一下是不是对应的产品
product1.product();
product2.product();
product3.product();
}
}
注意:这里的工厂方法,也就是Creator的factory方法,一般是静态的,因为它为了方便不用再去实例化工厂了,因为所有的事情都交给这一个工厂做了
输出结果
2、抽象工厂模式
由上面的工厂方法知道,我们的工厂方法讲所有的事情都交给了工厂。但是我们如果要针对手机我们要生产一系列的产品,比如华为手机,苹果手机。此时工厂方法就不能够满足了。我们需要一个华为手机工厂,苹果手机工厂。这里抽象工厂模式应运而生。它就是为了生成这样的系列产品的工厂模式。
简要说明
提供一个接口,可以创建一系列相关或者相互依赖的对象,而无需指定它们的具体的类。
速记关键字
生产成系列对象
类图如下
Java代码实现
/**
* 抽象产品A, 手机工厂,用于生产手机
*/
public interface AbstractProductA {
void product();
}
public class ConcreteProductA1 implements AbstractProductA {
@Override
public void product() {
System.out.println("生产产品A:华为手机");
}
}
public class ConcreteProductA2 implements AbstractProductA {
@Override
public void product() {
System.out.println("生产产品A:苹果手机");
}
}
/**
* 抽象产品B, 笔记本电脑工厂,用于生产笔记本
*/
public interface AbstractProductB {
void product();
}
public class ConcreteProductB1 implements AbstractProductB {
@Override
public void product() {
System.out.println("生产产品B:华为笔记本");
}
}
public class ConcreteProductB2 implements AbstractProductB {
@Override
public void product() {
System.out.println("生产产品B:苹果笔记本");
}
}
/**
* 抽象工厂 可以是接口 也可以是抽象类 总的定义需要生产哪些系列产品
*/
public interface AbstractFactory {
// 创建产品A(返回的对象是抽象产品A)
AbstractProductA createProductA(Integer type);
// 创建产品B(返回的对象是抽象产品B)
AbstractProductB createProductB(Integer type);
}
/**
* 具体工厂A,实现了抽象工厂,但是只实现抽象产品A的具体对象创建
*/
public class ConcreteFactoryA implements AbstractFactory{
@Override
public AbstractProductA createProductA(Integer type) {
if (type == 1){
return new ConcreteProductA1();
}
if (type == 2){
return new ConcreteProductA2();
}
throw new RuntimeException("请传入正确的产品A类型");
}
@Override
public AbstractProductB createProductB(Integer type) {
return null;
}
}
/**
* 具体工厂B,实现了抽象工厂,但是只实现抽象产品B的具体对象创建
*/
public class ConcreteFactoryB implements AbstractFactory{
@Override
public AbstractProductA createProductA(Integer type) {
return null;
}
@Override
public AbstractProductB createProductB(Integer type) {
if (type == 1){
return new ConcreteProductB1();
}
if (type == 2){
return new ConcreteProductB2();
}
throw new RuntimeException("请传入正确的产品B类型");
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 先实例化某个具体的工厂
AbstractFactory factoryA = new ConcreteFactoryA();
AbstractFactory factoryB = new ConcreteFactoryB();
// 比如我要通过两个工厂分别生产一个华为手机和一个华为笔记本
AbstractProductA productA = factoryA.createProductA(1);
AbstractProductB productB = factoryB.createProductB(1);
// 输出
productA.product();
productB.product();
}
}
运行结果
由类图和代码可以清晰的看出来,我们的抽象工厂定义了要创建哪些系列的产品,这些产品交给哪些具体的工厂去实现。而每个具体的工厂都会根据产品的类型参数,去创建不同的产品。这样我们新增一个具体的产品非常的容易,我们增加一个具体的工厂也改动不大,这就是设计模式的魅力所在。
3、原型模式
简要说明
用原型实例指定创建对象的类型,并且通过拷贝这个原型来创建新的对象
速记关键字
克隆对象
类图如下
浅克隆与深克隆
浅克隆表示只克隆对象的直接属性,如果对象里面包含另一个对象,不会连这个对象下面的属性也克隆,而是设置的这个对象的引用。 深克隆表示除了克隆对象的直接属性,如果对象里面包含另一个对象,也会连这个对象下面的属性也克隆,而不是设置的这个对象的引用。
Java代码实现
@Data
public class User implements Cloneable{
private String name;
private Integer age;
// 地址对象,浅克隆之后的这个对象不会克隆,使用的是原来的引用,深克隆则会克隆
private Address address;
// 浅克隆,实现Cloneable接口即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 深克隆,在实现浅克隆的基础上,将Address手动克隆一遍,Address本身也需要实现Cloneable接口
protected Object deepClone() throws CloneNotSupportedException {
User clone = (User)super.clone();
// 把里里面是对象的属性,执行一遍克隆即可
Address address = (Address)clone.getAddress().clone();
clone.setAddress(address);
return clone;
}
}
@Data
public class Address implements Cloneable{
private String province;
private String city;
private String area;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* 客户端实现
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建对象
Address address = new Address();
address.setProvince("重庆");
address.setProvince("重庆市");
address.setCity("渝中区");
User user = new User();
user.setName("IT动力");
user.setAge(18);
user.setAddress(address);
// 对象浅克隆, 浅克隆之后的属性里面的对象属性是不变的,因为存储的是引用
User clone = (User)user.clone();
System.out.println("浅克隆不会克隆address属性,结果应该为true: " + (clone.getAddress() == user.getAddress()));
// 对象深克隆
User clone2 = (User)user.deepClone();
System.out.println("深克隆会克隆address属性, 结果应该为false: " + (clone2.getAddress() == user.getAddress()));
}
}
输出结果
从上面看到,深克隆的实现我们手动去做了克隆Address对象的操作,但是在实际代码中,我们显然不能够去这样做。而实现的方式就是每个对象都实现序列化接口。深克隆时会序列化成流,然后再从流里读取出来进行反序列化。这样就能实现深克隆。
JSON序列化代码实现
@Data
public class Address implements Serializable {
private String province;
private String city;
private String area;
}
@Data
public class User implements Serializable {
private String name;
private Integer age;
private Address address;
protected Object deepClone() throws CloneNotSupportedException {
// 这里直接引入fastjson做测试
return JSON.parseObject(JSON.toJSONString(this), User.class);
}
}
/**
* 客户端实现
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建对象
com.dp.prototype.Address address = new Address();
address.setProvince("重庆");
address.setProvince("重庆市");
address.setCity("渝中区");
User user = new com.dp.prototype.User();
user.setName("IT动力");
user.setAge(18);
user.setAddress(address);
// 对象深克隆
User clone2 = (User) user.deepClone();
System.out.println("深克隆会克隆address属性, 结果应该为false: " + (clone2.getAddress() == user.getAddress()));
}
}
输出结果
4、单例模式
简要说明
保证一个类只有一个实例,并提供一个访问它的全局访问点
速记关键字
单实例
类图如下
单例模式,无外乎就是如何创建一个单实例,并且提供一个唯一访问入口。但是单例如何实例化又分为了饿汉式单例模式和懒汉式单例模式。
饿汉式单例模式
饿汉式单例模式,就是我们创建私有的一个静态变量,直接对进行实例化,并且提供一个私有的构造函数,防止对象被新建。最后提供一个公开的获取实例的方法,直接返回声明的对象。
它是从出生就做好了实例化,后面也不需要再去实例化了,所以它是天生线程安全的。
懒汉式单例模式
饿汉式是一开始就把对象创建好了,懒汉式一开始比较懒,它不创建,在方法被调用时才会创建,所以叫懒汉式。但是这样创建在多线程可能会有问题,因此一般需要加锁。
加锁得方式有整个方法加锁,还有一种叫做双重检测锁得单例模式,能够应对高并发。
Java代码实现
/**
* 饿汉式单例模式
*/
public class HungrySingleton {
// 私有得静态得实例变量,饿汉式直接初始化对象
private static final HungrySingleton instants = new HungrySingleton();
// 私有构造函数,防止其他类通过反射实例化该类
private HungrySingleton(){
System.out.println("我是饿汉式单例的私有构造方法,我被调用了");
}
// 静态的public方法,供外界网文的唯一入口
public static HungrySingleton getInstance(){
// 直接返回上面对应的对象
return instants;
}
}
/**
* 懒汉式单例模式
*/
public class LazySingleton {
// 私有得静态得实例变量,饿汉式直接初始化对象
private static LazySingleton instants;
// 私有构造函数,防止其他类通过反射实例化该类
private LazySingleton(){
System.out.println("我是懒汉式单例的私有构造方法,我被调用了");
}
// 静态的public方法,供外界网文的唯一入口, 注意懒汉式在这里需要加锁,不然会存在线程安全问题
public static synchronized LazySingleton getInstance(){
// 为空 则创建并且赋值给全局变量
if (instants == null){
instants = new LazySingleton();
}
// 返回上面对应的对象
return instants;
}
}
/**
* 懒汉式单例模式2 双重检测锁(推荐)
*/
public class LazySingleton2 {
// 私有得静态得实例变量,饿汉式直接初始化对象 注意,这里需要将全局变量加上volatile关键字,从而禁止重排序
private static volatile LazySingleton2 instants;
// 私有构造函数,防止其他类通过反射实例化该类
private LazySingleton2(){
System.out.println("我是懒汉式单例2的私有构造方法,我被调用了");
}
// 静态的public方法,供外界网文的唯一入口
public static LazySingleton2 getInstance(){
// 第一个if判断是否为空,不为空直接返回,避免synchronized同步代码块的执行,多线程场景下频繁加锁会影响性能
if(instants == null){
// 为空的情况才加锁
synchronized (LazySingleton2.class){
// 第二个if判断是否为空,当a线程优先获得锁,执行到此处,b线程没竞争到锁会被阻塞在外面,a线程判断实例是否为空,为空则new实例,
// a线程释放锁之后,b线程拿到锁进来后判断instance是否为null,此时不为null,则释放锁往下
if(instants == null){
instants = new LazySingleton2();
}
}
}
return instants;
}
}
输出结果
单例模式的代码实现方式有这么多种,那么工作中会使用哪种呢。如果需要考虑并发问题,就需要使用双重检测锁,这样性能会高一些。
为什么不使用饿汉式呢,因为它一开始就把对象初始化了,也就是内存从一开始就占用了。如果存在胆量的单例,但是却没有使用,那就是对内存的一种极大的浪费。而懒汉式则只有在真正需要时才会去创建对象。
5、构造器模式
简要说明
讲一个复杂类的表示与其构造相分离,使得形同的构建过程能够得出不同的表示。
速记关键字
复杂对现象构造
类图如下
java代码实现
/**
* 房子类 包含了房子的长宽高
*/
@Data
public class Room {
private String length;
private String width;
private String height;
@Override
public String toString() {
return "Room{length='" + length + ", width='" + width + ", height='" + height + '}';
}
}
/**
* 构建器接口
*/
public interface Builder {
// 构建房子的长度
void buildLength();
// 构建房子的宽度
void buildWidth();
// 构建房子的高度
void buildHeight();
// 构建房子
Room buildRoom();
}
/*
* 大房子的具体构建者
*/
public class LargeRoomBuilder implements Builder {
private final Room room = new Room();
@Override
public void buildLength() {
room.setLength("100米");
}
@Override
public void buildWidth() {
room.setWidth("120米");
}
@Override
public void buildHeight() {
room.setHeight("5米");
}
@Override
public Room buildRoom() {
return room;
}
}
/*
* 小房子的具体构建者
*/
public class SmallRoomBuilder implements Builder {
private final Room room = new Room();
@Override
public void buildLength() {
room.setLength("30米");
}
@Override
public void buildWidth() {
room.setWidth("40米");
}
@Override
public void buildHeight() {
room.setHeight("3米");
}
@Override
public Room buildRoom() {
return room;
}
}
/**
* 指挥者,引入指挥者的目的:指定构造流程、方式
*/
public class Director {
// 引入构建器
private final Builder builder;
// 构造注入
public Director(Builder builder) {
this.builder = builder;
}
// 构建房间方法
public Room buildRoom() {
// 构建房间,分别构建长宽高,然后就能能够构建一个房间
builder.buildLength();
builder.buildWidth();
builder.buildHeight();
return builder.buildRoom();
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 指定构造者,交由指挥者构建大房子
Room largeRoom = new Director(new LargeRoomBuilder()).buildRoom();
System.out.println(largeRoom);
// 指定构造者,交由指挥者构建小房子
Room smallRoom = new Director(new SmallRoomBuilder()).buildRoom();
System.out.println(smallRoom);
}
}
输出打印
6、小结
这里主要说明了设计模式中的创建型模式的概念,类图,以及代码的实现。而结构型模式和行为型模式,将会在后面去说明,因为创建型模式相对比较简单,也容易理解,所以放在最前面。后买你虽然会难一些,但是我们要知难而上。学无止境,加油!