设计模式。
文章目录
- 设计模式。
- GoF(Gang of Four) 23。
- 创建型模式。5
- 结构性模式。7
- 行为型模式。11
- OOP 七大原则。
- 单例模式。
- 饿汉式。
- 懒汉式。
- 懒汉式 ~ 改进:双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。
- 反射破坏单例。
- 解决反射。
- 破坏反射 2。
- 解决反射 2。枚举。
- 工厂模式。
- 简单工厂模式。
- 工厂方法模式。
- 小结。
- 抽象工厂模式。
GoF(Gang of Four) 23。
创建型模式。5 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
结构性模式。7 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式。11 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
OOP 七大原则。
- 开-闭原则(Open-Closed Principle, OCP)。
对扩展开放,对修改关闭。
- 里氏替换原则(Liskov Substitution Principle, LSP)。
继承必须确保超类所拥有的性质在子类中仍然成立。
- 依赖倒置原则(Dependence Inversion Principle)。
要面向接口编程,不要面向实现编程。
- 接口隔离原则(Interface Segregation Principle, ISP)。
要为各个类建立他们需要的专用接口。
- 合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)。
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
- 迪米特法则(Law of Demeter, LoD)。
只与你的直接朋友交谈,不要跟“陌生人”说话。
- 单一职责原则(Simple Responsibility Principle, SRP)。
控制类的粒度大小,将对象解耦,提高其内聚性。
单例模式。
饿汉式。
package com.geek.singleton;
/**
* 饿汉式单例模式。
*/
public class Hungry {// 饿汉,类一加载就创建对象。
private final static Hungry HUNGRY = new Hungry();
// 可能浪费空间。
private byte[] data1 = new byte[1024 * 1024];
private byte[] data2 = new byte[1024 * 1024];
private byte[] data3 = new byte[1024 * 1024];
private byte[] data4 = new byte[1024 * 1024];
// 构造器私有。
private Hungry() {}public static Hungry getInstance() {
return HUNGRY;
}
}
懒汉式。
- 单线程情况 ok。
package com.geek.singleton;
/**
* 懒汉式单例模式。
*/
public class Lazy {private static Lazy lazy;
private Lazy() {
System.out.println(Thread.currentThread().getName() + " ok.");
}public static Lazy getInstance() {
if (lazy == null) {
lazy = new Lazy();
}
return lazy;
}// 单线程下单例 ok。// 多线程并发。
public static void main(String[] args) {
for (int i = 0;
i < 10;
i++) {
new Thread(Lazy::getInstance).start();
}
}
// Thread-0 ok.
// Thread-6 ok.
// Thread-3 ok.
// Thread-2 ok.
}
懒汉式 ~ 改进:双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。 加 volatile 是因为 new 对象不是原子性操作。
package com.geek.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 懒汉式单例模式。
*/
public class Lazy2Synchronized {private static volatile Lazy2Synchronized lazy;
private Lazy2Synchronized() {}// 双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。
public static Lazy2Synchronized getInstance() {
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy2Synchronized();
// 不是一个原子性操作。
/**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把引用指向对象。
*
* 期望 123
* 然而 132
*A 线程先分配内存空间,引用指向了对象,
*这时 B 线程进来,会认为这个引用不为 null
*直接 return lazy;
。
*此时 lazy 还没有完成构造,return null。
* (指令重排造成问题。)
*
* 所以要在 lazy 加上 volatile。
*/
}
}
}return lazy;
}// 单线程下单例 ok。// 多线程并发。
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
for (int i = 0;
i < 10;
i++) {
new Thread(Lazy2Synchronized::getInstance).start();
}Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance();
反射破坏单例。
package com.geek.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 懒汉式单例模式。
*/
public class Lazy2Synchronized {private static volatile Lazy2Synchronized lazy;
private Lazy2Synchronized() {
}// 双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。
public static Lazy2Synchronized getInstance() {
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy2Synchronized();
// 不是一个原子性操作。
/**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把引用指向对象。
*
* 期望 123
* 然而 132
*A 线程先分配内存空间,引用指向了对象,
*这时 B 线程进来,会认为这个引用不为 null
*直接 return lazy;
。
*此时 lazy 还没有完成构造,return null。
* (指令重排造成问题。)
*
* 所以要在 lazy 加上 volatile。
*/
}
}
}
System.out.println("lazy = " + lazy);
return lazy;
}// 单线程下单例 ok。// 多线程并发。
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
for (int i = 0;
i < 10;
i++) {
new Thread(Lazy2Synchronized::getInstance).start();
// lazy = com.geek.singleton.Lazy2Synchronized@346827f
//lazy = com.geek.singleton.Lazy2Synchronized@346827f
//lazy = com.geek.singleton.Lazy2Synchronized@346827f
}Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance();
// 反射破坏单例。
Constructor declaredConstructor = Lazy2Synchronized.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy2Synchronized lazy2 = declaredConstructor.newInstance();
System.out.println(lazy1);
System.out.println(lazy2);
// com.geek.singleton.Lazy2Synchronized@346827f
//com.geek.singleton.Lazy2Synchronized@41629346}
// Thread-0 ok.
}
解决反射。
package com.geek.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 懒汉式单例模式。
*/
public class Lazy2Synchronized {private static volatile Lazy2Synchronized lazy;
private Lazy2Synchronized() {synchronized (Lazy2Synchronized.class) {
if (lazy != null) {
throw new RuntimeException("不要试图用反射破坏单例。");
}
}
System.out.println(Thread.currentThread().getName() + " ok.");
}// 双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。
public static Lazy2Synchronized getInstance() {
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy2Synchronized();
// 不是一个原子性操作。
/**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把引用指向对象。
*
* 期望 123
* 然而 132
*A 线程先分配内存空间,引用指向了对象,
*这时 B 线程进来,会认为这个引用不为 null
*直接 return lazy;
。
*此时 lazy 还没有完成构造,return null。
* (指令重排造成问题。)
*
* 所以要在 lazy 加上 volatile。
*/
}
}
}
System.out.println("lazy = " + lazy);
return lazy;
}// 单线程下单例 ok。// 多线程并发。
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
for (int i = 0;
i < 10;
i++) {
new Thread(Lazy2Synchronized::getInstance).start();
// lazy = com.geek.singleton.Lazy2Synchronized@346827f
//lazy = com.geek.singleton.Lazy2Synchronized@346827f
//lazy = com.geek.singleton.Lazy2Synchronized@346827f
}Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance();
// 反射破坏单例。
Constructor declaredConstructor = Lazy2Synchronized.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy2Synchronized lazy2 = declaredConstructor.newInstance();
System.out.println(lazy1);
System.out.println(lazy2);
// com.geek.singleton.Lazy2Synchronized@346827f
//com.geek.singleton.Lazy2Synchronized@41629346
// 解决:
// 在构造方法中加 synchronized 锁。
}
// Thread-0 ok.
}
破坏反射 2。
/**
* 懒汉式单例模式。
*/
public class Lazy2Synchronized {private static volatile Lazy2Synchronized lazy;
private static boolean geek = true;
// 使用加密。private Lazy2Synchronized() {synchronized (Lazy2Synchronized.class) {
//if (lazy != null) {
//throw new RuntimeException("不要试图用反射破坏单例。");
//}// 方法 2。
if (geek == true) {
geek = false;
} else {
throw new RuntimeException("不要试图用反射破坏单例。");
}}
System.out.println(Thread.currentThread().getName() + " ok.");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
for (int i = 0;
i < 10;
i++) {
new Thread(Lazy2Synchronized::getInstance).start();
// lazy = com.geek.singleton.Lazy2Synchronized@346827f
//lazy = com.geek.singleton.Lazy2Synchronized@346827f
//lazy = com.geek.singleton.Lazy2Synchronized@346827f
}Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance();
// 反射破坏单例。// ~ ~ ~ 2
Field geek = Lazy2Synchronized.class.getDeclaredField("geek");
geek.setAccessible(true);
// ~ ~ ~Constructor declaredConstructor = Lazy2Synchronized.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
// ~ ~ ~
geek.set(lazy, false);
// ~ ~ ~Lazy2Synchronized lazy2 = declaredConstructor.newInstance();
System.out.println(lazy1);
System.out.println(lazy2);
// com.geek.singleton.Lazy2Synchronized@22673956
//com.geek.singleton.Lazy2Synchronized@41629346
}
解决反射 2。枚举。
package com.geek.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
// enum 本身也是一个类 class。
public enum EnumSingle {INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
//Constructor declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
// Exception in thread "main" java.lang.NoSuchMethodException: com.geek.singleton.EnumSingle.()
// IDEA 骗了我,EnumSingle 没有空参构造。骗子 +1。
Constructor declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
// Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
// at java.lang.reflect.Constructor.newInstance(Constructor.java:417)// 想要的异常。不能反射创建枚举。declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
//EnumSingle instance2 = EnumSingle.INSTANCE;
//
//System.out.println("instance1 = " + instance1);
// INSTANCE
//System.out.println("instance2 = " + instance2);
// INSTANCE
}
}/*
可以看到,EnumSingle 本身也是 class,只是继承了 Enum 类。
这里还是有空参构造。可是运行时报错说没有空参构造。骗子 + 2。javap -p EnumSingle.class
Compiled from "EnumSingle.java"
public final class com.geek.singleton.EnumSingle extends java.lang.Enum {
public static final com.geek.singleton.EnumSingle INSTANCE;
private static final com.geek.singleton.EnumSingle[] $VALUES;
public static com.geek.singleton.EnumSingle[] values();
public static com.geek.singleton.EnumSingle valueOf(java.lang.String);
private com.geek.singleton.EnumSingle();
public com.geek.singleton.EnumSingle getInstance();
static {};
}使用 jad 反编译。
https://varaneckas.com/jad/jad158g.win.zipjad -sjava EnumSingle.class。可以看到 有参构造器。// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:EnumSingle.javapackage com.geek.singleton;
public final class EnumSingle extends Enum
{public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/geek/singleton/EnumSingle, name);
}private EnumSingle(String s, int i)
{
super(s, i);
}public EnumSingle getInstance()
{
return INSTANCE;
}public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
} */
工厂模式。
实现了创建者和调用者的分离。
- 简单工厂模式。
用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)。- 工厂方法模式。
用来生产同一等级结构中的固定产品(支持增加任意产品)。- 抽象工厂模式。
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
- OOP 七大原则。
开闭原则:一个元件的实体应当对扩展开放,对修改关闭。
依赖倒转原则:要针对接口编程,不要针对实现编程。
迪米特原则:只与你直接的朋友通信,避免和陌生人通信。
- 核心本质。
实例化对象不使用 new,用工厂方法代替。
将选择实现类、创建对象统一管理和控制。从而将调用者和我们的实现类解耦。
简单工厂模式。
package com.geek.factory.simple;
public interface Car {void name();
}
package com.geek.factory.simple;
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉。");
}
}
package com.geek.factory.simple;
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光。");
}
}
package com.geek.factory.simple;
/**
* 静态工厂模式。
* 开闭原则。
* * 增加一个新的产品,如果不修改代码,做不到。
*/
public class CarFactory {// 方法 1。
public static Car getCar(String car) {
if ("特斯拉".equals(car)) {
return new Tesla();
} else if ("五菱宏光".equals(car)) {
return new WuLing();
} else {
return null;
}
}// 方法 2。
public static Car getWuling() {
return new WuLing();
}public static Car getTesla() {
return new Tesla();
}
}
package com.geek.factory.simple;
public class Consumer {public static void main(String[] args) {
//Car car1 = new WuLing();
//Car car2 = new Tesla();
// 工厂模式。
Car car1 = CarFactory.getCar("特斯拉");
Car car2 = CarFactory.getCar("五菱宏光");
car1.name();
car2.name();
}
}
文章图片
问题:工厂 Factory 没有实现开闭原则。如果新来了大众汽车,车工厂的代码必须修改。
工厂方法模式。
package com.geek.factory.method;
/**
* 工厂方法模式。
*/
public interface CarFactory {
Car getCar();
}
package com.geek.factory.method;
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉。");
}
}
package com.geek.factory.method;
public class TeslaFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
package com.geek.factory.method;
public class Consumer {public static void main(String[] args) {
Car wuling = new WulingFactory().getCar();
Car tesla = new TeslaFactory().getCar();
wuling.name();
tesla.name();
}
}
如果需要增加新车,只需扩展车的工厂类。
package com.geek.factory.method;
public class Mobai implements Car {
@Override
public void name() {
System.out.println("摩拜。");
}
}
package com.geek.factory.method;
public class MobaiFactory implements CarFactory {
@Override
public Car getCar() {
return new Mobai();
}
}
直接从工厂拿车。(new xxxFactory(); )。
Car mobai = new MobaiFactory().getCar();
文章图片
小结。 对比选择。
结构复杂度:简单工厂模式。
代码复杂度:简单工厂模式。
编程复杂度:简单工厂模式。
管理复杂度:简单工厂模式。
【Java|设计模式。】根据设计原则:工厂方法模式。
根据实际业务:简单工厂模式。
抽象工厂模式。
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)