一、单例模式
一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
单例特点
- 某个类只能有一个实例;(构造器私有)·
- 它必须自行创建这个实例;(自己编写实例化逻辑)
- 它必须自行向整个系统提供这个实例(对外提供实例化方法)
1 饿汉式
1.1 直接赋值
饿汉代表对象随着类的加载而创建
示例
/**
* @Description 单例对象 饿汉
* @Date 2021/7/11 22:21
* @author: A.iguodala
*/
public class HungrySingleton {
/**
* 代表一些属性
*/
private Object data;
/**
* 饿汉式
* 直接构造出该单例对象
*/
private static final HungrySingleton singleton = new HungrySingleton();
/**
* 将构造器设为私有,禁止访问
*/
private HungrySingleton() {
}
/**
* 提供静态方法获取该单例对象
* @return
*/
public static HungrySingleton getSingleton() {
return singleton;
}
}
1.2 枚举
/**
* @Description
* @Date 2021/7/12 14:14
* @author: A.iguodala
*/
public enum EnumSingleton {
/**
* 单例对象
*/
INSATANCE;
}
2 懒汉式
懒汉式指的是当使用该对象的时候再进行创建,避免资源的浪费
但是在多线程环境下要进行加锁以及可见性判断来保证线程安全
2.1 双重检查
示例
/**
* @Description 单例模式,懒汉
* @Date 2021/7/11 22:38
* @author: A.iguodala
*/
public class LazySingleton {
/**
* 代表一些属性
*/
private Object data;
/**
* 懒汉式
* 当使用的时候再构造该对象
* 使用volatile保证内存语义的有序性
*/
private volatile static LazySingleton singleton;
/**
* 将构造器设为私有,禁止访问
*/
private LazySingleton() {
}
/**
* 提供静态方法获取该单例对象
* @return
*/
public static LazySingleton getSingleton() {
// 检查是否为空,不是则返回对象
if (singleton == null) {
// 加锁避免线程安全问题
synchronized (LazySingleton.class) {
// 双重检查,如果不为空,才创建对象
if (singleton == null) {
// 这里解释为什么singleton需要加volatile关键字
// 因为new LazySingleton() 也并不是一个原子操作,
// 要进行分配内存,初始化零值,设置对象头,执行初始化方法,再赋值引用
// 可能会有重排序的产生,相当于在作为初始化操作之前singleton已经不是null了
// 所以采用volatile来保证有序性
singleton = new LazySingleton();
}
}
}
return singleton;
}
}
为什么singleton对象要使用volatile关键字修饰?
因为new LazySingleton() 也并不是一个原子操作,要进行分配内存,初始化零值,设置对象头,执行初始化方法,再赋值引用的操作,可能会有重排序的产生,相当于在初始化singleton操作之前singleton已经不是null了,导致其他线程获取了没有初始化完全的对象。所以采用volatile来保证有序性
2.2 静态内部类
该方法通过静态内部类创建对象,可以实现懒加载以及,通过JVM保证类加载的线程安全来保证单例模式的线程安全
/**
* @Description
* @Date 2021/7/12 14:05
* @author: A.iguodala
*/
public class StaticInnerSingleton {
/**
* 通过该类来构造该单例对象
*/
private static class SingletonHolder {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}
/**
* 将构造器设置为私有
*/
private StaticInnerSingleton() {
}
/**
* 获取单例对象
* @return
*/
public static final StaticInnerSingleton getSingleton() {
return SingletonHolder.INSTANCE;
}
}
二、原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。
原型模式已经深深嵌入Java中,Object的clone方法就是原型模式的表现
例如,数据库查询到的数据构建成对象之后放入缓存中,如果直接取得该对象,并进行了修改,则该缓存中的对象也被修改,形成了脏数据,应该从缓存中取得的是以该缓存对象为原型的克隆对象
示例:
import jdk.jfr.DataAmount;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description 原型模式
* @Date 2021/7/11 23:22
* @author: A.iguodala
*/
public class PrototypeTest {
/**
* 测试
* @param args
*/
public static void main(String[] args) throws Exception {
UserService userService = new UserService();
User user1 = userService.getUser("Aiguodala");
user1.setUsername("nnzi");
System.out.println("User 1 ==>" + user1);
User user2 = userService.getUser("Aiguodala");
System.out.println("User 2 ==>" + user2);
User user3 = userService.getUser("Aiguodala");
System.out.println("User 3 ==>" + user3);
User user4 = userService.getUser("Aiguodala");
System.out.println("User 4 ==>" + user4);
}
}
class UserService {
/**
* 模拟缓存存储查询出的user信息
*/
Map<String, User> cache = new ConcurrentHashMap<>();
/**
* 模拟注入UserMapper
*/
UserMapper userMapper = new UserMapper();
User getUser(String username) throws CloneNotSupportedException {
if (cache.get(username) == null) {
// 如果缓存中没有则去数据库查询
User user = userMapper.getUser(username);
// 将查出的对象克隆加入缓存,这样对其他线程对该对象的修改不会影响到缓存的值
User cloneUser = (User) user.clone();
// 将查询结果放入缓存
cache.put(username, user);
return cloneUser;
}
// 如果缓存中有,则直接克隆返回
User cloneUser = (User) cache.get(username).clone();
return cloneUser;
}
}
/**
* 模拟DAO层与数据库交互
*/
class UserMapper {
/**
* 模拟从数据库查询出User对象
* @param username
* @return
*/
User getUser(String username) {
return new User(username, "");
}
}
/**
* 模拟User实体对象
*/
@Data
@ToString
@AllArgsConstructor
class User implements Cloneable{
String username;
String password;
/**
* 实现克隆方法,通过原型创建一个克隆对象
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return new User(username, password);
}
}
说明修改一个User对其他获取的对象不会造成影响,如果将克隆的方法去掉,直接使用缓存中的User,当某一个User改变用户名,则所有获取到该User的都会改变,可以自己尝试。
三、工厂模式
工厂模式(Factory Pattern)提供了一种创建对象的最佳方式。我们不必关心对象的创建细节,只需要根据不同情况获取不同产品即可
1. 简单工厂
简单工厂在于简单,直接通过传入的参数以及if判断或者swich判断来进行创建
示例:
/**
* @Description
* @Date 2021/7/11 23:46
* @author: A.iguodala
*/
public class SimplyFactory {
public static void main(String[] args) {
Car benz = CarFactory.buildCar("Benz");
benz.run();
Car bwm = CarFactory.buildCar("BWM");
bwm.run();
}
}
/**
* 汽车工厂
*/
class CarFactory {
public static Car buildCar(String want) {
// 通过if判断应该造哪种车
// 这就是简单工厂
if ("BWM".equals(want)) {
return new BWM();
}else if ("Benz".equals(want)) {
return new Benz();
}
return null;
}
}
/**
* 汽车抽象类
*/
abstract class Car {
/**
* 汽车品牌
*/
String brand;
abstract void run();
}
/**
* 汽车子类,宝马
*/
class BWM extends Car{
@Override
void run() {
System.out.println("I am 宝马");
}
}
/**
* 汽车子类,奔驰
*/
class Benz extends Car {
@Override
void run() {
System.out.println("I am 奔驰");
}
}
缺点:
- 如果有一万种车要造,要写一万个if,代码冗余
- 如果新加了一辆车,那么需要修改源码,违反开闭原则,扩展困难
2. 工厂方法
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
例如,一个造车的工厂以及其子类具体到造哪一种车,如果需要哪一种车就到哪个特有的工厂去获取,而不是直接向父类的工厂获取,如果要新添加一种车,则只需要创建车和该车的工厂,不需要改动源码,符合开闭原则
/**
* @Description
* @Date 2021/7/12 14:18
* @author: A.iguodala
*/
public class FactoryMethod {
public static void main(String[] args) {
/**
* 把工厂抽象成父类,每个工厂生产不同的产品
* 如果要添加产品,则只需要写该产品以及方法
*/
CarFactory porscheFactory = new PorscheFactory();
Car porsche = porscheFactory.getCar();
porsche.run();
CarFactory ferrariFactory = new FerrariFactory();
Car ferrari = ferrariFactory.getCar();
ferrari.run();
}
}
/**
* 抽象出的汽车
*/
abstract class Car {
/**
* 汽车品牌
*/
String brand;
/**
* 开车方法
*/
abstract void run();
}
class Porsche extends Car {
@Override
void run() {
System.out.println("保时捷 run");
}
}
class Ferrari extends Car {
@Override
void run() {
System.out.println("法拉利 run");
}
}
/**
* 抽象出的汽车工厂
*/
abstract class CarFactory {
/**
* 汽车工厂有一个造出车的方法
* @return
*/
abstract Car buildCar();
/**
* 通过该方法获取车
* @return
*/
Car getCar () {
Car car = buildCar();
// 给Car做一些操作
return car;
}
}
class PorscheFactory extends CarFactory {
@Override
Car buildCar() {
return new Porsche();
}
}
class FerrariFactory extends CarFactory {
@Override
Car buildCar() {
return new Ferrari();
}
}
缺点:
- 系统复杂,冗长,多了好多个类
- 工厂职责单一,只能建造一种产品,例如,如果要让工厂造洗衣机,就只能改动抽象类工厂的代码,增加一个方法实现
2. 抽象工厂
抽象工厂就是对各个工厂在进行一个抽象,抽象一个总工厂,里面可以造各种产品,解决工厂方法产品单一的缺点,如果想要增加一个产品,则在总工厂中增加一个方法,然后有一个该产品的实现类工厂即可
/**
* @Description
* @Date 2021/7/12 15:06
* @author: A.iguodala
*/
public class AbstractFactory {
public static void main(String[] args) {
GeneralFactory porscheFactory = new PorscheFactory();
Car porsche = porscheFactory.getCar();
porsche.run();
GeneralFactory n95Factory = new N95MaskFactory();
Mask n95 = n95Factory.getMask();
n95.protect();
}
}
/**
* 抽象出的汽车
*/
abstract class Car {
/**
* 汽车品牌
*/
String brand;
/**
* 开车方法
*/
abstract void run();
}
class Porsche extends Car {
@Override
void run() {
System.out.println("保时捷 run");
}
}
class Ferrari extends Car {
@Override
void run() {
System.out.println("法拉利 run");
}
}
/**
* 抽象出的口罩
*/
abstract class Mask {
/**
* 类型
*/
String type;
/**
* 防护方法
*/
abstract void protect();
}
class N95Mask extends Mask {
@Override
void protect() {
System.out.println("N95口罩 防护");
}
}
class SurgicalMask extends Mask {
@Override
void protect() {
System.out.println("外科口罩 防护");
}
}
/**
* 抽象出的总工厂
*/
abstract class GeneralFactory {
/**
* 可以造车
* @return
*/
abstract Car getCar();
/**
* 也可以造口罩
* @return
*/
abstract Mask getMask();
}
/**
* 抽象出的汽车工厂
*/
abstract class CarFactory extends GeneralFactory{
/**
* 汽车工厂有一个造出车的方法
* @return
*/
abstract Car buildCar();
/**
* 车工厂只实现总工厂的造车方法
* @return
*/
@Override
Car getCar () {
Car car = buildCar();
// 给Car做一些操作
return car;
}
@Override
Mask getMask() {
return null;
}
}
class PorscheFactory extends CarFactory {
@Override
Car buildCar() {
return new Porsche();
}
}
class FerrariFactory extends CarFactory {
@Override
Car buildCar() {
return new Ferrari();
}
}
/**
* 抽象出的口罩工厂
*/
abstract class MaskFactory extends GeneralFactory{
/**
* 子类继承造口罩的方法
* @return
*/
abstract Mask buildMask();
@Override
Mask getMask() {
Mask mask = buildMask();
return mask;
}
@Override
Car getCar() {
return null;
}
}
class N95MaskFactory extends MaskFactory {
@Override
Mask buildMask() {
return new N95Mask();
}
}
class SurgicalMaskFactory extends MaskFactory {
@Override
Mask buildMask() {
return new SurgicalMask();
}
}
四、建造者模式
建造者模式封装建造对象的过程,但是并不屏蔽建造的细节
例如,建造一个手机,你不关心具体建造过程是什么样的,但是你关注内存,大小,颜色等等信息,这些信息由你提供
import lombok.Data;
import lombok.ToString;
/**
* @Description
* @Date 2021/7/12 16:14
* @author: A.iguodala
*/
public class Builder {
public static void main(String[] args) {
AbstractPhoneBuilder huaweiBuilder = new HUAWEIBuilder();
huaweiBuilder.diyColor("black");
huaweiBuilder.diyMemory(128);
huaweiBuilder.diySize("4寸");
huaweiBuilder.diyCpuCore(8);
Phone phone1 = huaweiBuilder.build();
// 因为返回值是AbstractPhoneBuilder
// 所以也可以链式调用
Phone phone2 = new HUAWEIBuilder().diyColor("blue")
.diyMemory(256)
.diySize("5寸")
.diyCpuCore(8)
.build();
System.out.println(phone1);
System.out.println(phone2);
}
}
/**
* 建造的手机类
*/
@Data
@ToString
class Phone {
private String color;
private Integer memory;
private String size;
private Integer cpuCore;
}
/**
* 抽象的手机建造者
*/
abstract class AbstractPhoneBuilder {
/**
* 建造的手机
*/
protected Phone phone;
/**
* 可以自定义实现细节,
* @param color
* @return 返回值为AbstractPhoneBuilder则可以链式调用
*/
abstract AbstractPhoneBuilder diyColor(String color);
abstract AbstractPhoneBuilder diyMemory(Integer memory);
abstract AbstractPhoneBuilder diySize(String size);
abstract AbstractPhoneBuilder diyCpuCore(Integer cpuCore);
/**
* 将对象构造出来
* @return
*/
public Phone build() {
return phone;
}
}
/**
* 华为建造者继承手机总建造者,自定义生产什么样的手机
*/
class HUAWEIBuilder extends AbstractPhoneBuilder {
public HUAWEIBuilder() {
phone = new Phone();
}
@Override
AbstractPhoneBuilder diyColor(String color) {
phone.setColor(color);
return this;
}
@Override
AbstractPhoneBuilder diyMemory(Integer memory) {
phone.setMemory(memory);
return this;
}
@Override
AbstractPhoneBuilder diySize(String size) {
phone.setSize(size);
return this;
}
@Override
AbstractPhoneBuilder diyCpuCore(Integer cpuCore) {
phone.setCpuCore(cpuCore);
return this;
}
}
或者也可以改写成内部类的形式
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @Description
* @Date 2021/7/12 16:14
* @author: A.iguodala
*/
public class Builder {
public static void main(String[] args) {
Phone phone = new Phone.Builder()
.color("red")
.memory(512)
.size("3寸")
.cpuCore(8)
.build();
System.out.println(phone);
}
}
/**
* 建造的手机类
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
class Phone {
private String color;
private Integer memory;
private String size;
private Integer cpuCore;
public static class Builder {
private String color;
private Integer memory;
private String size;
private Integer cpuCore;
public Builder color(String color) {
this.color = color;
return this;
}
public Builder memory(Integer memory) {
this.memory = memory;
return this;
}
public Builder size(String size) {
this.size = size;
return this;
}
public Builder cpuCore(Integer cpuCore) {
this.cpuCore = cpuCore;
return this;
}
public Phone build() {
return new Phone(color, memory, size, cpuCore);
}
}
}
总结:
- 创建型的模式主要作用于对象的创建
- 单例模式保证了线程安全以及全局只有一个该对象
- 工厂模式屏蔽了创建对象的过程以及细节,只管从工厂获取对象,而建造者模式则屏蔽了创建的过程,没有屏蔽创建的细节,可以实现自己的定制化
- 原型模式被Java原生支持,利用一个对象克隆出一个原型对象使用
之后我在别的地方看到设计模式的具体应用还会继续补充!