Java 常用设计模式学习记录


前言

设计模式的七个原则:

  • 单一职责
    在类的级别上,一个类只负责一项职责;在方法的级别上,一个方法只做一件事。如类A负责两个不同职责:职责1、职责2.当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分为A1,A2互不影响。
  • 接口隔离
    客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。这里的接口实际上指的是“超类型”,可以是抽象类abstract class,也可以是接口interface。
  • 依赖倒置/控制反转/面向接口编程原则
    面向接口编程,而不是面向具体的实现类编程。中心思想就是面向接口编程
  • 开闭原则
    一个软件实体,如类、模板和函数应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。
  • 里式替换
    在子类中不要重写父类的方法(重写抽象类的抽象方法除外),可抽象出共同接口,将两个类变成同类,再使用聚合、组合、依赖的方式调用。
  • 迪米特法则/最少知道原则
    每个类A都避免不了与其他类B产生关系,但我们让这种关系越小越好。即类A对类B的内部了解的越少越好,如果类A要用到类B,最好只需要调用类B提供的public方法即可。不允许把另一个类的对象当做自己的局部变量。
  • 合成复用原则。
    能使用组合、依赖就不使用继承

设计模式的七个原则详解

案例

知乎详细解释及案例

带图解析

学习及实例博客

23种设计模式

创建型模式

主要用于对象的创建

单例模式(Singleton Pattern)

基础示例略

反序列化破坏单例解决方法 :

重写readResolve()方法即可

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
	private Object readResolve() {
	        return singleton;
	    }

容器式单例

当程序中的单例对象非常多的时候,则可以使用容器对所有单例对象进行管理,如下:

单独提出一个容器保存单例

public class ContainerSingleton {
    private ContainerSingleton() {}
    private static Map<String, Object> singletonMap = new ConcurrentHashMap<>();
    public static Object getInstance(Class clazz) throws Exception {
        String className = clazz.getName();
        // 当容器中不存在目标对象时则先生成对象再返回该对象
        if (!singletonMap.containsKey(className)) {
            Object instance = Class.forName(className).newInstance();
            singletonMap.put(className, instance);
            return instance;
        }
        // 否则就直接返回容器里的对象
        return singletonMap.get(className);
    }
    public static void main(String[] args) throws Exception {
        SafetyDangerLibrary instance1 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class);
        SafetyDangerLibrary instance2 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class);
        System.out.println(instance1 == instance2); // true
    }
}

ThreadLocal单例
不保证整个应用全局唯一,但保证线程内部全局唯一,以空间换时间,且线程安全。

public class ThreadLocalSingleton {
    private ThreadLocalSingleton(){}
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = ThreadLocal.withInitial(() -> new ThreadLocalSingleton());
    public static ThreadLocalSingleton getInstance(){
        return threadLocalInstance.get();
    }
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
            System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
        }).start();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
            System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
        }).start();
//        Thread-0-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@53ac93b3
//        Thread-1-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@7fe11afc
//        Thread-0-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@53ac93b3
//        Thread-1-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@7fe11afc
    }
}

静态内部类单例

/**
 * 测试静态内部类单例模式 静态内部类:线程安全,调用效率高,并且实现了延时加载
 * @author Administrator
 */
public class SingletonDemo3 {
	// 静态内部类
	public static class SingletonClassInstance {
		private static final SingletonDemo3 instance = new SingletonDemo3();
	}
 
	// 私有化构造器
	private SingletonDemo3() {
	}
 
	// 方法没有同步,调用效率高
	public static SingletonDemo3 getInstance() {
		return SingletonClassInstance.instance;
	}
}

原型模式(Prototype)

通过克隆实现

  • 浅克隆
  • 深克隆

克隆破坏单例解决方法 :

重写clone()方法即可:

	private static Clazz clazz = new Clazz();

	// 方法一
    @Override
    protected Object clone() throws CloneNotSupportedException {
    	// 返回类中原有的实例即可
        return clazz;
    }
    // 测试输出
    System.out.println(clazz1 == clazz2) // true

工厂模式(Factory Pattern)

通过一个工厂类来实现对象的创建,而无需直接暴露对象的创建逻辑给客户端。
简单工厂模式的优点在于客户端无需了解具体产品类的创建细节,只需通过工厂类来创建对象,并且工厂类可以根据客户端的需求来动态创建不同类型的对象。但是缺点也比较明显,如果需要创建的产品类数量较多,则工厂类的代码会变得很臃肿,不便于维护。

定义抽象类,定义具体类实现抽象类方法;定义一个工厂类,包含返回一个具体类的方法,根据方法入参的不同,返回不同的具体类,需要某个具体类时,调用工厂类,传入指定参数来获取具体类

abstract class Animal {
    public abstract void sound();
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

// 创建一个工厂类
class AnimalFactory {
    // 定义一个静态方法,根据传入的参数创建具体的产品类对象
    public static Animal createAnimal(String type) {
        if (type.equalsIgnoreCase("dog")) {
            return new Dog();
        } else if (type.equalsIgnoreCase("cat")) {
            return new Cat();
        } else {
            throw new IllegalArgumentException("Invalid animal type: " + type);
        }
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 使用工厂类创建不同的 Animal 对象
        Animal dog = AnimalFactory.createAnimal("dog");
        dog.sound();
        Animal cat = AnimalFactory.createAnimal("cat");
        cat.sound();
    }
}

抽象工厂模式(Abstract Factory Pattern)

通过定义一个创建对象的接口来创建对象,但将具体实现的决定留给子类来决定。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

定义抽象类,定义抽象类的具体实现类;定义一个抽象工厂类,包含返回一个抽象类的方法,再定义抽象工厂类的实现类,不同抽象工厂实现类返回不同的具体实现类。需要哪个具体实现类,由指定的抽象工厂实现类返回

不同工厂模式的是,抽象工厂模式将工厂类抽象提取出来,一个具体类由一个具体抽象工厂实现类返回。抽象工厂模式的具体抽象工厂有多个,工厂模式的工厂只有一个,根据入参不同,返回不同具体类

// 创建一个抽象产品类
abstract class Animal {
    public abstract void sound();
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵喵");
    }
}

// 创建具体产品类,继承自 Animal 类
class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪汪");
    }
}

abstract class AnimalFactory {
    // 定义一个抽象方法,用于创建 Animal 对象
    public abstract Animal createAnimal();
}

class CatFactory extends AnimalFactory {
    @Override
    public Animal createAnimal() {
        return new Cat();
    }
}

// 创建具体工厂类,实现创建 Animal 对象的接口
class DogFactory extends AnimalFactory {
    @Override
    public Animal createAnimal() {
        return new Dog();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 创建一个 Dog 对象
        AnimalFactory dogFactory = new DogFactory();
        Animal dog = dogFactory.createAnimal();
        dog.sound();
        // 创建一个 Cat 对象
        AnimalFactory catFactory = new CatFactory();
        Animal cat = catFactory.createAnimal();
        cat.sound();
    }
}

建造者模式(Builder)

新建类时提供一系列方法去设置新建类的参数,并提供一个总方法入口去负责调用上述的一系列方法,总方法入口给外部调用,以达到不需要开发者一个个单独地调用一系列方法去构建新建的类。

与模板方法模式类似

public class BuilderClass {
    public String a;
    public String b;
    public String c;
    
    //建造者
    public BuilderClass () {
        setA("A");
        setB("B");
        setC("C");
    }
    
 }   

结构型模式

用于处理类或对象的组合关系

适配器模式(Adapter Pattern)

Java适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行通信。适配器模式通过将一个类的接口转换为客户端所期望的另一个接口来实现这一点。这种模式可以在不修改现有代码的情况下重用现有类。
适配器模式可以帮助我们在不修改现有代码的情况下重用现有类,并且可以使不兼容的接口之间进行通信。

已有源接口没有实现目标接口,定义适配器类实现目标接口,在适配器类中组合依赖源接口,使得调用适配器类中的方法实际是调用源接口的方法。

Mybatis的日志门面Log,使用的适配器模式,它对jdbc、log4j等各种日志框架的适配实现

// 源接口
public class Adaptee {
	public void specificRequest() {
		System.out.println("Adaptee's specific request");
	}
}
public interface Target {
	public void request();
}
// 适配器类
public class Adapter implements Target {
	private Adaptee adaptee;
	public Adapter(Adaptee adaptee) {
		this.adaptee = adaptee;
	}
	public void request() {
		adaptee.specificRequest();
	}
}
// 客户端代码
public class Client {
	public static void main(String[] args) {
		Adaptee adaptee = new Adaptee();
		Target target = new Adapter(adaptee);
		target.request();
	}
}

组合模式(Composite Pattern)

在组合模式中,通常有两三种主要角色:

  • 组件(Component): 这是一个抽象类或接口,定义了单个对象和对象组合共同的操作。它可以有一些默认实现,也可以有抽象方法需要在具体子类中实现。
  • 叶子(Leaf): 继承自组件,表示单个对象。它没有子对象。
  • 复合(Composite): 继承自组件,表示对象组合。它包含了一组子对象,这些子对象可以是叶子,也可以是复合

定义组件接口,定义抽象组件(Component),抽象组件包含两个方法,入参都为组件接口:一个新增、一个删除。定义一个实现抽象组件(Component)的复合类(Composite),复合类组合依赖了一个数组,数组元素为组件接口。还有一个调用组件方法,实现逻辑为遍历调用数组内所有的抽象组件。复合类实现抽象组件的两个方法,就是针对其内部数组的新增删除。

Mybatis源码中SqlNode和各个子类ChooseSqlNode等使用的组合模式【生成树形结构数据】

public interface IComponent {
    void display();
}

public abstract class Component implements IComponent {
    protected String name;

    public Component(String name) {
        this.name = name;
    }

    public abstract void add(IComponent component);

    public abstract void remove(IComponent component);
}

// Composite.java
public class Composite extends Component {
    private List<IComponent> children = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void add(IComponent component) {
        children.add(component);
    }

    @Override
    public void remove(IComponent component) {
        children.remove(component);
    }

    @Override
    public void display() {
        System.out.println("Composite: " + name);
        for (IComponent component : children) {
            component.display();
        }
    }
}

// Leaf.java
public class Leaf implements IComponent {
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void display() {
        System.out.println("Leaf: " + name);
    }
}

public class Client {
    public static void main(String[] args) {
        Component root = new Composite("root");
        Component branch1 = new Composite("branch1");
        Component branch2 = new Composite("branch2");
        Component leaf1 = new Leaf("leaf1");
        Component leaf2 = new Leaf("leaf2");
        Component leaf3 = new Leaf("leaf3");
        root.add(branch1);
        root.add(branch2);
        branch1.add(leaf1);
        branch2.add(leaf2);
        branch2.add(leaf3);
        root.display();
    }
}

装饰器模式(Decorator Pattern)

装饰模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)

装饰(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

定义一个要扩展方法的基础类,再定义一个抽象类继承基础类并通过构造方法组合依赖基础类,抽象类抽象方法就是留给子类扩展的方法,默认实现为其依赖的基础类实现。当需要对基础类扩展的时候,由子类去继承抽象类并选择性重写需要扩展的方法方法,但是该重写方法返回值必须是对调用父类(抽象类)方法的处理结果。

重复将已经扩展实现了抽象类的子类当作被依赖的对象传入其他抽象类子类构造函数(实现类组合依赖抽象类),实现链式调用。

Mybatis执行sql查询时的CachingExecutor类就是使用装饰者模式

装饰器实例 :

public class Test {

    // 测试方法
    public static void main(String[] args) {
        FriedRice friedRice = new FriedRice();
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭5元
        friedRice = new Egg(friedRice);
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭+鸡蛋7元
        friedRice = new Egg(friedRice);
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋9元
        friedRice = new Ham(friedRice);
        System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋+火腿12元
    }


}


// 炒饭类
class FriedRice {
    String getDesc() {
        return "炒饭";
    }
    Integer getPrice() {
        return 5;
    }
}

// 配料表
abstract class Ingredients extends FriedRice {
    private FriedRice friedRice;
    public Ingredients(FriedRice friedRice) {
        this.friedRice = friedRice;
    }
    String getDesc() {
        return this.friedRice.getDesc();
    }
    Integer getPrice() {
        return this.friedRice.getPrice();
    }
}

// 鸡蛋配料
class Egg extends Ingredients {
    public Egg(FriedRice friedRice) {
        super(friedRice);
    }
    String getDesc() {
        return super.getDesc() + "+鸡蛋";
    }
    Integer getPrice() {
        return super.getPrice() + 2;
    }
}

// 火腿配料
class Ham extends Ingredients {
    public Ham(FriedRice friedRice){
        super(friedRice);
    }
    String getDesc() {
        return super.getDesc() + "+火腿";
    }
    Integer getPrice() {
        return super.getPrice() + 3;
    }
}

创建装饰器抽象类实现待装饰接口类,并通过构造参数传入组合保存一个待装饰的接口类,子类继承创建装饰器抽象类并实现需要的方法

外观模式(Facade Pattern)

供一个简单的接口,将复杂的子系统调用和依赖关系进行封装,使客户端能够更轻松地与系统进行交互。

定义一个外观类,外观类组合依赖其他多个具体工作类,要调用具体工作类的某些方法时,由外观类暴露出统一方法,统一方法内部就是调用指定具体工作类的方法。

与组合模式中的统一调用相似,不同是组合模式是循环调用统一接口实现类,组合模式内调用的类没有直接关联

// 子系统:音响
class StereoSystem {
    public void turnOn() {
        System.out.println("Stereo System is turned on");
    }

    public void turnOff() {
        System.out.println("Stereo System is turned off");
    }
}

// 子系统:投影仪
class Projector {
    public void turnOn() {
        System.out.println("Projector is turned on");
    }

    public void turnOff() {
        System.out.println("Projector is turned off");
    }
}

// 子系统:灯光控制
class LightsControl {
    public void turnOn() {
        System.out.println("Lights are turned on");
    }

    public void turnOff() {
        System.out.println("Lights are turned off");
    }
}

// 外观类:家庭影院外观
class HomeTheaterFacade {
    private StereoSystem stereo;
    private Projector projector;
    private LightsControl lights;

    public HomeTheaterFacade() {
        stereo = new StereoSystem();
        projector = new Projector();
        lights = new LightsControl();
    }

    public void watchMovie() {
        System.out.println("Getting ready to watch a movie...");
        lights.turnOff();
        projector.turnOn();
        stereo.turnOn();
    }

    public void endMovie() {
        System.out.println("Ending the movie...");
        stereo.turnOff();
        projector.turnOff();
        lights.turnOn();
    }
}

// HomeTheaterFacade充当了一个外观类,封装了音响、投影仪和灯光控制等子系统的复杂操作,以便客户端可以通过简单的调用来完成观影过程。
// 这样,客户端不需要了解各个子系统的具体操作,只需通过外观类的方法来控制整个家庭影院系统的行为。
public class FacadeExample {
    public static void main(String[] args) {
        HomeTheaterFacade homeTheater = new HomeTheaterFacade();

        // 准备观影
        homeTheater.watchMovie();

        // 结束观影
        homeTheater.endMovie();
    }
}

享元模式(Flyweight Pattern)

享元模式又称为轻量级模式,是对象池的一种实现,类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。提供了减少对象数量从而改善应用所需的对象结构的方式。它通过创建一个享元工厂来管理共享对象,并在需要时返回已经存在的对象,从而减少对象的创建和销毁次数。

宗旨:共享细粒度对象,将多个对同一对象的访问集中起来

定义享元工厂类、接口类、接口实现类;享元工厂类组合依赖一个Map,value为接口实现类,key为获取需要的value实现类的标识;如果有符合的接口实现类就直接取Map中的返回,如果没有则新建放入Map再返回。

类似容器式单例,不同的是容器式单例是以Class为key访问,达到对象唯一性。享元中key为自定义,并且实现类为同一类型。

案例 :

// 享元抽象接口
public interface ITicket {
    void show(String seat);
}
// 具体享元类 
public class TrainTicket implements ITicket {
    private String from;
    private String to;
    private Integer price;
    public TrainTicket(String from, String to) {
        this.from = from;
        this.to = to;
    }
    @Override
    public void show(String seat) {
        this.price = new Random().nextInt(500);
        System.out.println(from + "->" + to + ":" + seat + "价格:" + this.price);
    }
}
// 享元工厂类
public class TicketFactory {
    private static Map<String, ITicket> pool = new ConcurrentHashMap<>();
    public static ITicket getTicket(String from, String to) {
        String key = from + "->" + to;
        if (pool.containsKey(key)) {
            System.out.println("使用缓存获取火车票:" + key);
            return pool.get(key);
        }
        System.out.println("使用数据库获取火车票:" + key);
        ITicket ticket = new TrainTicket(from, to);
        pool.put(key, ticket);
        return ticket;
    }
}
    // 测试
    public static void main(String[] args) {
        ITicket ticket = getTicket("北京", "上海");
        //使用数据库获取火车票:北京->上海
        //北京->上海:二等座价格:20
        ticket.show("二等座");
        ITicket ticket1 = getTicket("北京", "上海");
        //使用缓存获取火车票:北京->上海
        //北京->上海:商务座价格:69
        ticket1.show("商务座");
        ITicket ticket2 = getTicket("上海", "北京");
        //使用数据库获取火车票:上海->北京
        //上海->北京:一等座价格:406
        ticket2.show("一等座");
        System.out.println(ticket == ticket1);//true
        System.out.println(ticket == ticket2);//false
    }

代理模式(Proxy Pattern)

代理模式是项目中常用的一种设计模式。提供了间接访问目标对象的一种方式;即通过代理对象访问目标对象。
这样做的好处是,可以在不改变原有目标对象的基础上,对目标对象增加额外的扩展功能

代理模式三种实现方式:

  • 静态代理
  • jdk动态代理
  • cglib动态代理。

三种实现方式各有优点,以及适用的场景:

  • 静态代理:代理类必须非常明确,所以无法做到通用,但是效率也是最高的
  • jdk动态代理:必须基于接口代理,有一定局限性;动态生成字节码文件,可以用于通用业务(性能日志等)
  • cglig动态代理:也是动态生成字节码文件,生成的代理类继承了目标对象
  • spring aop默认代理策略是:如果目标对象实现了接口,则使用jdk动态代理,否则使用cglib代理
  • jdk8之后,jdk动态代理效率要高于cglib代理

Spring的AOP、Mybatis的MapperProxy、ConnectionLogger使用的JDK动态代理

静态代理

定义代理接口,创建代理接口实现类实现代理接口,创建静态代理类也实现代理接口,并且组合依赖一个代理接口实现类,静态代理类的接口实现实际是调用其内部依赖的代理接口实现类,在静态代理类中可以对代理接口实现类扩展。

被代理对象与代理对象需要实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类。

和适配器模式类似,静态代理类类似适配器类;不同是静态代理类中依赖的被代理类也实现了代理接口,适配器类中依赖的被适配类没有实现已有接口

/**
 * 代理接口
 */
public interface IHello {
    String hi(String key);
}

/**
 * 代理接口实现类
 */
public class HelloImpl implements IHello {
    @Override
    public String hi(String key) {
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}

/**
 * 静态代理类
 */
public class HelloStaticProxy implements IHello {
    private IHello hello;// 组合依赖一个代理接口实现

    public HelloStaticProxy(IHello hello) {
        this.hello = hello;
    }

    @Override
    public String hi(String key) {
        System.out.println(">>> static proxy start");
        String result = hello.hi(key);// 实际是调用其组合依赖的代理接口
        System.out.println(">>> static proxy end");
        return result;
    }
}

/**
 * 测试
 */
public class DemoTest {
    public static void main(String[] args) {
        IHello helloProxy = new HelloStaticProxy(new HelloImpl());
        helloProxy.hi("world");
    }
}

JDK动态代理

jdk动态代理是基于接口的一种代理方式,目标对象一定要实现接口。
原理是,利用反射机制,动态生成匿名类继承Proxy类并且实现了要代理的接口,由于java不支持多继承,所以JDK动态代理不能代理单独的类。

最总要的是代理接口需要实现InvocationHandler接口并实现invoke方法,在invoke方法内实现代理的逻辑,getProxy方法返回的就是实际代理类,调用实际代理类的任意方法都会进入代理类的invoke方法。

/**
 * 代理接口
 */
public interface IHello {
    String hi(String key);
}

/**
 * 代理接口实现类
 */
public class HelloImpl implements IHello {
    @Override
    public String hi(String key) {
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}

/**
 * jdk动态代理类
 */
public class JdkProxy implements InvocationHandler {
    private Object target;

    public JdkProxy(Object target) {
        this.target = target; // 保存被代理的原类
    }

    /*** 获取被代理接口实例对象
     * @param <T> 
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

	// proxy
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>> JdkProxy start");
        Object result = method.invoke(target, args);// 调用原类的方法并返回原值
        System.out.println(">>> JdkProxy end");
        return result;
    }
}

/**
 * 测试
 */
public class Demo2Test {
    public static void main(String[] args) {
        JdkProxy proxy = new JdkProxy(new HelloImpl());
        IHello helloProxy = proxy.getProxy();
        helloProxy.hi(" jdk proxy !");
    }
}

CGLIB动态代理

目标对象可以不用实现接口,不能针对final类进行代理。
原理是,动态生成class继承目标对象。使用cglib必须引入对应的jar包

<dependency>
	 <groupId>cglib</groupId>
	 <artifactId>cglib</artifactId>
	 <version>3.2.7</version>
</dependency>
/**
 * 目标类
 */
public class HelloImpl {
    public String hi(String key) {
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}

/**
 * cglib代理类
 */
public class CglibProxy implements InvocationHandler {
    private Object target;

    /*** 获取被代理接口实例对象*/
    public <T> T getProxy() {//
        // 1创建增强器对象
        Enhancer enhancer = new Enhancer();
        // 2设置增强器的类加载器
        enhancer.setClassLoader(target.getClass().getClassLoader());
        // 3设置代理对象父类类型
        enhancer.setSuperclass(target.getClass());
        // 4设置回调函数
        enhancer.setCallback(this);
        // 5创建代理对象
        return (T) enhancer.create();
    }

    public CglibProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>> cglib start");
        Object obj = method.invoke(target, args);
        System.out.println(">>> cglib end");
        return obj;
    }
}

/**
 * 测试
 */
public class Demo3Test {
    public static void main(String[] args) {
        HelloImpl hello = new HelloImpl();
        CglibProxy cglibProxy = new CglibProxy(hello);
        HelloImpl proxy = cglibProxy.getProxy();
        proxy.hi(" cglib ");
    }
}

桥接模式(Bridge Pattern)

桥接模式通过将抽象部分和具体部分分离,使它们可以独立地变化。在桥接模式中,通过创建一个桥接接口(或抽象类),其中包含一个指向具体实现的引用,将抽象部分和具体部分连接起来。这样,抽象部分和具体部分可以独立地进行扩展,而不会相互影响。这种方式也被称为“组合优于继承”。

定义基础接口,及基础接口实现类;再定义一个抽象类,组合依赖基础接口,抽象类抽象出一个方法给子类实现;抽象类子类实现抽象类,并为抽象类设置其依赖基础接口的具体实现类,抽象类子类实现抽象方法,并在抽象方法中某个阶段调用抽象类中调用抽象类的依赖接口。

与静态代理模式类似,抽象实现类就是静态代理类,不同是桥接模式将静态代理类抽象化了。

// 实现部分 - 颜色接口;我们定义一个 Color 接口,它表示颜色:
interface Color {
    void applyColor();
}

class Red implements Color {
    public void applyColor() {
        System.out.println("Applying red color");
    }
}

class Blue implements Color {
    public void applyColor() {
        System.out.println("Applying blue color");
    }
}

// 抽象部分 - 形状类;它包含了一个 Color 对象:
abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }

    public void draw() {
        System.out.print("Drawing a circle. ");// 定义自己的实现
        color.applyColor();// 调用抽象类依赖的接口方法
    }
}

class Square extends Shape {
    public Square(Color color) {
        super(color);
    }

    public void draw() {
        System.out.print("Drawing a square. ");
        color.applyColor();
    }
}

// 在这个示例中,Color 接口代表颜色的实现部分,Red 和 Blue 分别是实现了颜色接口的具体颜色类。
// Shape 是形状的抽象部分,具有一个颜色引用,而 Circle 和 Square 是继承自 Shape 的具体形状类。
// 这种设计允许我们在不改变形状或颜色的情况下,独立地对它们进行扩展和变化。
public class BridgePatternExample {
    public static void main(String[] args) {
        Color redColor = new Red();
        Color blueColor = new Blue();

        Shape redCircle = new Circle(redColor);
        Shape blueSquare = new Square(blueColor);

        redCircle.draw();
        blueSquare.draw();
    }
}

这是一个简单的桥接模式实现,它允许我们在运行时动态地改变 Shape 类的颜色而不用影响到 Shape 子类,同时也允许我们增加新的颜色和形状类而无需改变其它现有的类。

行为型模式

用于描述对类或对象怎样交互和怎样分配职责。

责任链模式(Chain of Responsibility Pattern)

责任链模式提供了一种通过一系列处理对象来处理请求的方法。每个处理对象都包含一个对下一个处理对象的引用,形成一个链式结构。每个对象都可能处理该请求或将其传递给下一个对象。当一个请求到达时,它首先被传递给链中的第一个处理对象,如果该对象不能处理该请求或者还需要下面处理器继续处理,它会将请求传递给下一个处理对象,依此类推,直到找到能够处理完成请求的对象为止。

定义一个抽象处理者(Handler)类,该类包含一个对下一个处理者的引用(也是Handler类型),并声明一个处理请求的方法。
具体的处理者类继承自抽象处理者类,实现处理请求的方法。在该方法中,处理者可以决定是否处理请求,如果不能处理,则将请求传递给下一个处理者。
客户端创建一个处理链,将处理者按照一定的顺序连接起来。

// 责任链基础方法
public interface Handler {
    Handler setNextHandler(Handler nextHandler);

    void handleRequest(Request request);
}

// 创建抽象处理器类(主要保存对下一个链的引用及获取)
public abstract class AbstractHandler implements Handler {
    private Handler nextHandler;

    public Handler setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
        return this.nextHandler;
    }

    public Handler getNextHandler() {
        return nextHandler;
    }
}

// 创建具体的处理器类
public class ConcreteHandler1 extends AbstractHandler {
    public void handleRequest(Request request) {
        if (request.getType().equals("Type1")) {
            System.out.println("ConcreteHandler1 handles request " + request);// 完成自己的处理逻辑
        } else {// 可根据条件判断是否要继续向下处理或者直接向下传递
            getNextHandler().handleRequest(request);
        }
    }
}

public class ConcreteHandler2 extends AbstractHandler {
    public void handleRequest(Request request) {
        if (request.getType().equals("Type2")) {
            System.out.println("ConcreteHandler2 handles request " + request);
        } else {
            getNextHandler().handleRequest(request);
        }
    }
}

public class ConcreteHandler3 extends AbstractHandler {
    public void handleRequest(Request request) {
        if (request.getType().equals("Type3")) {
            System.out.println("ConcreteHandler3 handles request " + request);
        } else {
            getNextHandler().handleRequest(request);
        }
    }
}

// 创建请求类(被处理的类,可以为任意形式不一定非要对象)
public class Request {
    private String type;

    public Request(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    public String toString() {
        return "Request [type=" + type + "]";
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        Handler handler3 = new ConcreteHandler3();
        handler1.setNextHandler(handler2).setNextHandler(handler3);// 设置链条的前后关系
        handler1.handleRequest(new Request("Type1"));// 当前传递关系handler1 -> handler2 -> handler3
        handler1.handleRequest(new Request("Type2"));
        handler1.handleRequest(new Request("Type3"));
    }
}

第二个示例 :

职责链模式是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。

在一条已经定义好的责任链上,只能往链子开头新增,无法对链子结尾新增,因为在链结尾已经定义好责任链的结束并返回逻辑,不会再往后执行。

// 用户实体类
@Data
public class User {
    private String username;
    private String password;
    private String role;
}
// handler抽象类
public abstract class Handler {
    protected Handler next;
    // 设置下一个责任类,返回handler方便链式操作
    public void next(Handler next) {
        this.next = next;
    }
    // 流程开始的方法
    public abstract void doHandler(User user);
}
// 校验用户名或者密码是否为空
public class ValidateHandler extends Handler {
    @Override
    public void doHandler(User user) {
        if (StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())) {
            System.out.println("用户名或者密码为空!");
            return;
        }
        System.out.println("校验通过");
        next.doHandler(user);
    }
}
// 登录校验,校验用户名是否匹配密码
public class LoginHandler extends Handler {
    @Override
    public void doHandler(User user) {
        if (!"pyy52hz".equals(user.getUsername()) || !"123456".equals(user.getPassword())) {
            System.out.println("用户名或者密码不正确!请检查!");
            return;
        }
        user.setRole("admin");
        System.out.println("登陆成功!角色为管理员!");
        next.doHandler(user);
    }
}
// 权限校验
public class AuthHandler extends Handler {
    @Override
    public void doHandler(User user) {
        if (!"admin".equals(user.getRole())) {
            System.out.println("无权限操作!");
            return;
        }
        System.out.println("角色为管理员,可以进行下一步操作!");
        // 职责完成;必须要由最低的责任类
    }
}
// 登录流程
public class LoginService {
    public void login(User user) {
        Handler validateHandler = new ValidateHandler();
        Handler loginHandler = new LoginHandler();
        Handler authHandler = new AuthHandler();
        validateHandler.next(loginHandler);// 设置下一个责任类
        loginHandler.next(authHandler);// 设置下一个责任类
        validateHandler.doHandler(user);
    }
}
    // 测试方法
    public static void main(String[] args){
      User user = new User();
      //校验通过
      //用户名或者密码不正确!请检查!
      user.setUsername("pyy52hz");
      user.setPassword("1234567");
      LoginService loginService = new LoginService();
      loginService.login(user);
      //校验通过
      //登陆成功!角色为管理员!
      //角色为管理员,可以进行下一步操作!
      user.setUsername("pyy52hz");
      user.setPassword("123456");
      loginService.login(user);
    }

结合建造者模式:

// handler抽象类
public abstract class Handler<T> {
    protected Handler next;
    // 返回handler方便链式操作
    public Handler next(Handler next) {
        this.next = next;
        return next;
    }
    // 流程开始的方法
    public abstract void doHandler(User user);
    static class Builder<T> {
        private Handler<T> head;
        private Handler<T> tail;
        public Builder<T> addHandler(Handler<T> handler) {
            if (this.head == null) {
                this.head = this.tail = handler;
                return this;
            }
            this.tail.next(handler);
            this.tail = handler;
            return this;
        }
        public Handler<T> build() {
            return this.head;
        }
    }
}
public class LoginService {
    public void login(User user) {
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ValidateHandler())
            .addHandler(new LoginHandler())
            .addHandler(new AuthHandler());
        builder.build().doHandler(user);
    }
}

命令模式(Command Pattern)

命令模式包含以下主要角色:

  • 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
  • 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  • 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

定义命令接口,具体命令类,控制器类。
具体命令类是真正执行具体方法的类,命令接口根据不同需求实现接口并且组合依赖一个具体命令类,实现的接口就是调用被依赖的具体命令类的某个方法。
控制器类依赖命令接口,负责去调用命令接口的执行方法。
调用路径为:控制器类->命令接口实现类->具体命令类;

// 播放器类(>具体命令类)
public class Player {
    public void play() {
        System.out.println("正常播放");
    }
    public void pause() {
        System.out.println("暂停播放");
    }
    public void stop() {
        System.out.println("停止播放");
    }
}
// 命令接口
public interface IAction {
    void excuse();
}
// 播放命令类(命令接口实现类)
@AllArgsConstructor
public class PlayAction implements IAction {
    private Player player;
    @Override
    public void excuse() {
        this.player.play();
    }
}
// 暂停命令类
@AllArgsConstructor
public class PauseAction implements IAction {
    private Player player;
    @Override
    public void excuse() {
        this.player.pause();
    }
}
// 停止命令类
@AllArgsConstructor
public class StopAction implements IAction{
    private Player player;
    @Override
    public void excuse() {
        this.player.stop();
    }
}
// 控制器
public class Controller {
    public void excuse(IAction action) {
        action.excuse();
    }
}
   // 测试方法
   public static void main(String[] args) {
        // 正常播放
        new Controller().excuse(new PlayAction(new Player()));
        // 暂停播放
        new Controller().excuse(new PauseAction(new Player()));
        // 停止播放
        new Controller().excuse(new StopAction(new Player()));
    }

解释器模式(Interpreter Pattern)

解释器模式适用于需要解释和处理特定语言或表达式的情况,它通过将语句表示为抽象语法树并提供解释器来执行解释。这有助于实现定制的语言处理逻辑。

定义表达式接口,定义表达式接口实现类。表达式实现类中根据当前实现类的逻辑可与保存零个或多个表达式接口实现类的依赖,根据不同需求调用依赖的实现类。

// 表达式接口
interface Expression {
    int interpret();
}

// 数字表达式类
class NumberExpression implements Expression {
    private int value;
    
    public NumberExpression(int value) {
        this.value = value;
    }
    
    @Override
    public int interpret() {
        return value;
    }
}

// 加法表达式类
class AddExpression implements Expression {
    private Expression leftOperand;// 保存着对其他表达式的引用
    private Expression rightOperand;
    
    public AddExpression(Expression leftOperand, Expression rightOperand) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
    }
    
    @Override
    public int interpret() {
        return leftOperand.interpret() + rightOperand.interpret();// 调用自己依赖的实现类
    }
}

// 减法表达式类
class SubtractExpression implements Expression {
    private Expression leftOperand;
    private Expression rightOperand;
    
    public SubtractExpression(Expression leftOperand, Expression rightOperand) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
    }
    
    @Override
    public int interpret() {
        return leftOperand.interpret() - rightOperand.interpret();
    }
}

// 在这个示例中,我们构建了一个简单的数学表达式解释器,用于解释并计算基本的加法和减法表达式。
// 这展示了解释器模式如何工作,将表达式解释成实际的结果。
// 在实际应用中,解释器模式可以用于更复杂的领域,如编程语言解释器或规则引擎。
public class InterpreterPatternExample {
    public static void main(String[] args) {
        // 构建表达式:2 + (3 - 1)
        Expression expression = new AddExpression(
            new NumberExpression(2),
            new SubtractExpression(
                new NumberExpression(3),
                new NumberExpression(1)
            )
        );
        
        // 解释并计算表达式的值
        int result = expression.interpret();
        System.out.println("Result: " + result); // 输出: Result: 4
    }
}

在Java中,可以使用抽象语法树(AST)来表示语言表达式,并使用解释器来执行这些表达式。解释器模式通常包括以下几个组件:

  • 抽象表达式(Abstract Expression):定义了一个抽象的解释器接口,该接口包含了解释器需要实现的方法。
  • 终结符表达式(Terminal Expression):实现了抽象表达式接口的终结符,用于表示语言中的基本操作或值。
  • 非终结符表达式(Non-Terminal Expression):实现了抽象表达式接口的非终结符,用于表示语言中的复杂操作。
  • 上下文(Context):包含了解释器需要的一些全局信息,例如变量、函数等。
  • 解释器(Interpreter):使用上述组件来解释语言表达式,并执行相应的操作。

另一个示例 :

interface Expression {
    int interpret(Context context);
}

// 终结符表达式
class NumberExpression implements Expression {
    private int value;

    public NumberExpression(int value) {
        this.value = value;
    }

    public int interpret(Context context) {
        return value;
    }
}

// 非终结符表达式
class AddExpression implements Expression {
    private Expression left;
    private Expression right;

    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }
}

// 上下文
class Context {
    private Map<String, Integer> variables = new HashMap<>();

    public void setVariable(String name, int value) {
        variables.put(name, value);
    }

    public int getVariable(String name) {
        return variables.get(name);
    }
}

// 解释器
class Interpreter {
    private Expression expression;

    public Interpreter(Expression expression) {
        this.expression = expression;
    }

    public int interpret(Context context) {
        return expression.interpret(context);
    }
}

// 使用解释器执行表达式
public class Main {
    public static void main(String[] args) {// 创建上下文
        Context context = new Context();
        context.setVariable("a", 10);
        context.setVariable("b", 20);
        // 创建表达式
        Expression expression = new AddExpression(new NumberExpression(context.getVariable("a")), new NumberExpression(context.getVariable("b")));
        // 创建解释器并执行表达式
        Interpreter interpreter = new Interpreter(expression);
        int result = interpreter.interpret(context);
        System.out.println("Result: " + result);
    }
}

迭代器模式(Iterator Pattern)

在软件开发中,经常需要遍历集合(如列表、数组、树等)中的元素,但不同集合可能有不同的遍历方式,这导致在客户端代码中需要编写不同的遍历逻辑,使代码变得复杂且难以维护。此外,有时候还需要在遍历过程中支持添加、删除等操作,这可能会影响遍历的一致性和正确性。

定义一个迭代器接口,包含一个集合元素,一个当前遍历的游标。

// 迭代器接口
public interface Iterator<T> {
    Boolean hasNext();
    T next();
}
// 迭代器接口实现类
public class IteratorImpl<T> implements Iterator<T> {
    private List<T> list;// 迭代的数组
    private Integer cursor;// 游标
    private T element;// 当前迭代游标对应的元素
 
    public IteratorImpl(List<T> list) {
        this.list = list;
    }
    @Override
    public Boolean hasNext() {
        return cursor < list.size();
    }
    @Override
    public T next() {
        element = list.get(cursor);
        cursor++;
        return element;
    }
}
// 容器接口
public interface Aggregate<T> {
    void add(T t);
    void remove(T t);
    Iterator<T> iterator();
}
// 容器接口实现类
public class AggregateImpl<T> implements Aggregate<T> {
    private List<T> list = new ArrayList<>();
    @Override
    public void add(T t) {
        list.add(t);
    }
    @Override
    public void remove(T t) {
        list.remove(t);
    }
    @Override
    public Iterator<T> iterator() {
        return new IteratorImpl<>(list);
    }
}

中介者模式(mediator pattern)

定义一个被关联接口,有多个实现类称为具体类,再定义一个中介类,将具体类都组合在中介类中,中介类暴露一个方法,其返回值为组合内某个具体类的属性,通过入参不同来确定的具体类。

// 抽象同事类
@AllArgsConstructor
public class Person {
    protected String name;
    protected MediatorCompany mediatorCompany;
}
// 房主
public class HouseOwner extends Person {
    public HouseOwner(String name, MediatorCompany mediatorCompany) {
        super(name, mediatorCompany);
    }
    // 联络方法
    public void connection(String message) {
        mediatorCompany.connection(this, message);
    }
    // 获取消息
    public void getMessage(String message) {
        System.out.println("房主" + name + "获取到的信息:" + message);
    }
}
// 租客
public class Tenant extends Person {
    public Tenant(String name, MediatorCompany mediatorCompany) {
        super(name, mediatorCompany);
    }
    public void connection(String message) {
        mediatorCompany.connection(this, message);
    }
    public void getMessage(String message) {
        System.out.println("租客" + name + "获取到的信息:" + message);
    }
}
// 中介公司(中介者)
@Data
public class MediatorCompany {
	// 组合
    private HouseOwner houseOwner;
    private Tenant tenant;
    public void connection(Person person, String message) {
        // 房主需要通过中介获取租客信息
        if (person.equals(houseOwner)) {
            this.tenant.getMessage(message);
        } else { // 反之租客通过中介获取房主信息
            this.houseOwner.getMessage(message);
        }
    }
}
    // 测试
    public static void main(String[] args){
        // 先创建三个角色,中介公司,房主,租客
        MediatorCompany mediatorCompany = new MediatorCompany();
        // 房主和租客都在同一家中介公司
        HouseOwner houseOwner = new HouseOwner("张三", mediatorCompany);
        Tenant tenant = new Tenant("李四", mediatorCompany);
        // 中介公司获取房主和租客的信息
        mediatorCompany.setHouseOwner(houseOwner);
        mediatorCompany.setTenant(tenant);
        // 房主和租客都在这家中介公司发布消息,获取到对应的消息
        tenant.connection(tenant.name + "想租一房一厅!");
        houseOwner.connection(houseOwner.name + "这里有!来看看呗!");
        // 测试结果
        // 房主张三获取到的信息:李四想租一房一厅!
        // 租客李四获取到的信息:张三这里有!来看看呗!
    }

观察者模式(Observer Mode)

观察者模式,又叫发布-订阅(Publish/Subscribe)模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式或从属者(Dependents)模式。定义一种一对多的依赖关系,一个主题对象可被多个观察者同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被自动更新。

定义被观察者接口,通过组合方式里面保存着一个全是观察者的集合,被观察者提供一个方法循环调用集合中观察者的观察接口

SpringBoot中SpringApplicationRunListeners就是使用观察者模式

// 抽象观察者接口
public interface Observer {
    void update(String message);
}

// 微信用户类 具体的观察者
@AllArgsConstructor
public class WeixinUser implements Observer {
    private String name;
    @Override
    public void update(String message) {
        System.out.println(name + "接收到了微信消息(观察到了):" + message);
    }
}

// 被观察者接口
public interface Observable {
    // 新增用户(新增观察者)
    void add(Observer observer);
    // 移除用户,或者说用户取消订阅(移除观察者)
    void del(Observer observer);
    // 发布 推送消息
    void notify(String message);
}

// 具体的被观察者(公众号)
public class Subject implements Observable {
    // 观察者列表(需要发送消息的订阅用户)
    private List<Observer> list = new ArrayList<>();
    @Override
    public void add(Observer observer) {
        list.add(observer);
    }
    @Override
    public void del(Observer observer) {
        list.remove(observer);
    }
    // 给每一个观察者(订阅者)推送消息
    @Override
    public void notify(String message) {
        list.forEach(observer -> observer.update(message));
    }
 
}
    // 测试
    public static void main(String[] args){
      Observable o = new Subject();
      WeixinUser user1 = new WeixinUser("张三");
      WeixinUser user2 = new WeixinUser("李四");
      WeixinUser user3 = new WeixinUser("王五");
      o.add(user1);
      o.add(user2);
      o.add(user3);
      o.notify("薛之谦演唱会要来到广州啦!");
      // 运行结果
      // 张三接收到了微信消息(观察到了):薛之谦演唱会要来到广州啦!
      // 李四接收到了微信消息(观察到了):薛之谦演唱会要来到广州啦!
      // 王五接收到了微信消息(观察到了):薛之谦演唱会要来到广州啦!
    }
 

Java自带观察者模式

Java提供的一种内置的观察者模式实现。它使用了Java中的Observable类和Observer接口来实现观察者模式。

Observable类是一个抽象类,它表示一个可观察的对象,具有添加、删除和通知观察者的方法。当Observable对象的状态发生改变时,会调用它的notifyObservers()方法,通知所有的观察者对象,并更新它们的状态。Observable类还提供了setChanged()方法和clearChanged()方法,用于标记Observable对象的状态是否发生了改变。

Observer接口表示观察者对象,具有更新状态的方法update()。当Observable对象的状态发生改变时,会调用观察者对象的update()方法,传递更新的数据。

import java.util.Observable;
import java.util.Observer;

// 具体主题类
class WeatherStation extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        setChanged();
        notifyObservers();//遍历调用
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

// 具体观察者类
class Display implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;

    public void update(Observable o, Object arg) {
        if (o instanceof WeatherStation) {
            WeatherStation weatherStation = (WeatherStation) o;
            this.temperature = weatherStation.getTemperature();
            this.humidity = weatherStation.getHumidity();
            this.pressure = weatherStation.getPressure();
            display();
        }
    }

    public void display() {
        System.out.println("Temperature: " + temperature);
        System.out.println("Humidity: " + humidity);
        System.out.println("Pressure: " + pressure);
    }
}

// 使用JDK自带观察者模式实现气象站
public class Main {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        Display display1 = new Display();// 定义一个观察者
        Display display2 = new Display();
        weatherStation.addObserver(display1);// 添加观察者
        weatherStation.addObserver(display2);// 添加观察者
        weatherStation.setMeasurements(25.0f, 60.0f, 1013.0f);// 遍历调用观察者的提醒方法
        weatherStation.deleteObserver(display2);
        weatherStation.setMeasurements(26.0f, 65.0f, 1012.0f);
    }
}

状态模式(State Pattern)

它允许对象在内部状态改变时改变它的行为。状态模式将状态封装成独立的类,并将请求委托给当前状态对象,从而实现状态的切换和状态行为的变化。

略,就是通过调用不同方法改变改变类中依赖类的属性,依赖类定义在顶级的抽象类中。

定义状态接口,定义具体状态类实现实现状态接口;再定义普通类,组合依赖状态接口并定义一个修改其依赖状态的方法。

interface State {
    void handle(); 
}

// 具体状态类1
class ConcreteState1 implements State {
    public void handle() {
        System.out.println("ConcreteState1 is handling.");
    }
}

// 具体状态类2
class ConcreteState2 implements State {
    public void handle() {
        System.out.println("ConcreteState2 is handling.");
    }
}

// 环境类
class Context {
    private State state;

	// 设置当前状态
    public void setState(State state) {
        this.state = state;
    }

    public void request() {
        state.handle(); // 返回当前状态,类型可以自定义
    }
}

// 使用状态模式实现的客户端代码
public class Main {
    public static void main(String[] args) {
        Context context = new Context();
        State state1 = new ConcreteState1();
        State state2 = new ConcreteState2();
        context.setState(state1);// 修改状态
        context.request();// 查询状态
        context.setState(state2);
        context.request();
    }
}

策略模式(Strategy Pattern)

通过一个Map,实现不同入参返回不同的类型,可以避免多重分支的if…else和switch语句。

与享元模式类似

// 会员卡接口
public interface VipCard {
    public void discount();
}
public class GoldCard implements VipCard {
    @Override
    public void discount() {
        System.out.println("金卡打7折");
    }
}
public class SilverCard implements VipCard {
    @Override
    public void discount() {
        System.out.println("银卡打8折");
    }
}
public class CopperCard implements VipCard {
    @Override
    public void discount() {
        System.out.println("铜卡打9折");
    }
}
public class Normal implements VipCard {
    @Override
    public void discount() {
        System.out.println("普通会员没有折扣");
    }
}
// 会员卡容器类
public class VipCardFactory {
    private static Map<String, VipCard> map = new ConcurrentHashMap<>();
    static {
        map.put("gold", new GoldCard());
        map.put("silver", new SilverCard());
        map.put("copper", new CopperCard());
    }
    public static VipCard getVIPCard(String level) {
        return map.get(level) != null ? map.get(level) : new Normal();
    }
 
}
    // 测试方法
    public static void main(String[] args) {
        //金卡打7折
        VipCardFactory.getVIPCard("gold").discount();
        //银卡打8折
        VipCardFactory.getVIPCard("silver").discount();
        //普通会员没有折扣
        VipCardFactory.getVIPCard("other").discount();
    }

模板方法模式(Template method pattern)

定义一个算法的骨架,并允许之类为其中的一个或者多个步骤提供实现。模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,将可变的行为留给子类来实现,各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

模板方法是由具体方法和抽象方法组成的,它定义了程序的骨架,而具体方法则实现了算法的部分步骤。

首先定义一个抽象类,在抽象类中先定义好执行的总体方法,在总体方法内细化每个执行步骤,每个步骤就是对应抽象类中的一个抽象方法,由总体方法取统一调用抽象方法。不同子类通过继承抽象类重写需要的不同抽象方法以达到子类的定制化,再调用总体执行方法即可。

Spring容器刷新方法AbstractApplicationContext#refresh()就是使用模板方法模式

类似建造者模式:不同是模板方法模式是面向类中的某个方法的实现,建造者模式面向类的创建

public abstract class DayOffProcess {
    // 请假模板
    public final void dayOffProcess() {
        // 领取申请表
        this.pickUpForm();
        // 填写申请信息
        this.writeInfo();
        // 签名
        this.signUp();
        // 提交到不同部门审批
        this.summit();
        // 行政部备案
        this.filing();
    }
    // 可以定义默认实现,也可全交由子类实现
    private void filing() {
        System.out.println("行政部备案");
    }
    protected abstract void summit();
    protected abstract void signUp();
    private void writeInfo() {
        System.out.println("填写申请信息");
    }
    private void pickUpForm() {
        System.out.println("领取申请表");
    }
}

上例可通过不同类实现不同方法,达到不同目的

public class ZhangSan extends DayOffProcess {
    @Override
    protected void summit() {
        System.out.println("张三签名");
    }
    @Override
    protected void signUp() {
        System.out.println("提交到技术部审批");
    }
}

public class Lisi extends DayOffProcess {
    @Override
    protected void summit() {
        System.out.println("李四签名");
    }
    @Override
    protected void signUp() {
        System.out.println("提交到市场部审批");
    }

访问者模式(Visitor Pattern)

允许你在不修改对象结构的前提下定义新的操作。访问者模式将对象结构和操作分离开来,使得操作可以独立地应用于对象结构中的元素。在访问者模式中,有两个主要角色:访问者和元素。访问者定义了对元素进行操作的方法,而元素则提供了接受访问者的方法。

// 首先,我们需要定义图形形状的接口和具体类
// 图形形状接口
interface Shape {
    void accept(ShapeVisitor visitor);
}

// 圆形类
class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

// 矩形类
class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

// 接下来,定义一个访问者接口和具体的访问者实现
// 访问者接口
interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

// 面积计算访问者
class AreaCalculator implements ShapeVisitor {
    private double area;

    @Override
    public void visit(Circle circle) {
        area += Math.PI * circle.getRadius() * circle.getRadius();
    }

    @Override
    public void visit(Rectangle rectangle) {
        area += rectangle.getWidth() * rectangle.getHeight();
    }

    public double getArea() {
        return area;
    }
}
// 在这个示例中,访问者模式允许我们在不修改形状类的情况下,通过实现不同的访问者来执行不同的操作,例如计算面积。
// 这样,我们可以轻松地添加新的访问者来执行其他操作,同时保持形状类的不变。
public class VisitorPatternExample {
    public static void main(String[] args) {
        Circle circle = new Circle(5);
        Rectangle rectangle = new Rectangle(4, 6);

        AreaCalculator areaCalculator = new AreaCalculator();
        circle.accept(areaCalculator);
        rectangle.accept(areaCalculator);

        System.out.println("Total area: " + areaCalculator.getArea());
    }
}

备忘录模式(Memento Pattern)

备忘录模式通常用于需要撤销操作或恢复先前状态的情况下。

定义一个主类,一个备份类,备份类保存着主类需要备份的属性。主类中必须包含两个方法,一个存档方法,一个恢复方法。存档方法返回一个备份类,并将主类需要备份的属性赋值给备份类;恢复方法输入一个备份类,负责将备份类的属性赋值给当前主类。

// 游戏角色类
@Data
public class GameRole {
    private Integer vit; // 生命力
    private Integer atk; // 攻击力
    private Integer def; // 防御力
    // 初始化状态
    public void init() {
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }
    // 战斗到0
    public void fight() {
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }
    // 保存角色状态
    public RoleStateMemento saveState() {
        return new RoleStateMemento(this.vit, this.atk, this.def);
    }
    // 回复角色状态
    public void recoverState(RoleStateMemento roleStateMemento) {
        this.vit = roleStateMemento.getVit();
        this.atk = roleStateMemento.getAtk();
        this.def = roleStateMemento.getDef();
    }
    // 展示状态
    public void showState() {
        System.out.println("角色生命力:" + this.vit);
        System.out.println("角色攻击力:" + this.atk);
        System.out.println("角色防御力:" + this.def);
    }
}
// 游戏状态存储类(备忘录类)
@Data
@AllArgsConstructor
public class RoleStateMemento {
    private Integer vit; // 生命力
    private Integer atk; // 攻击力
    private Integer def; // 防御力
}
// 角色状态管理者类
@Data
public class RoleStateCaretaker {
    private RoleStateMemento roleStateMemento;
}
    // 测试结果
    public static void main(String[] args){
      System.out.println("===========打boss前状态===========");
      GameRole gameRole = new GameRole();
      gameRole.init();
      gameRole.showState();
      // 保存进度
      RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
      roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
      System.out.println("===========打boss后状态===========");
      gameRole.fight();
      gameRole.showState();
      System.out.println("===========恢复状态===========");
      gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
      gameRole.showState();
      // ===========打boss前状态===========
      // 角色生命力:100
      // 角色攻击力:100
      // 角色防御力:100
      // ===========打boss后状态===========
      // 角色生命力:0
      // 角色攻击力:0
      // 角色防御力:0
      // ===========恢复状态===========
      // 角色生命力:100
      // 角色攻击力:100
      // 角色防御力:100
    }
 
  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值