Java|设计模式。

设计模式。
文章目录

    • 设计模式。
      • 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(); } }

Java|设计模式。
文章图片

问题:工厂 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|设计模式。】根据设计原则:工厂方法模式。
根据实际业务:简单工厂模式。

抽象工厂模式。

    推荐阅读