JAVA 学习 面试(十一)常见设计模式

news2024/11/19 23:33:04
设计模式
## 1、创建型模式
对象实例化的模式,创建型模式用于解耦对象的实例化过程。
单例模式:某个类智能有一个实例,提供一个全局的访问点。
工厂模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。

## 2、结构型模式
把类或对象结合在一起形成一个更大的结构。
装饰器模式:动态的给对象添加新的功能。
代理模式:为其它对象提供一个代理以便控制这个对象的访问。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
享元模式:通过共享技术来有效的支持大量细粒度的对象。

## 3、行为型模式
类和对象如何交互,及划分责任和算法。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
观察者模式:对象间的一对多的依赖关系。
仲裁者模式:用一个中介对象来封装一系列的对象交互。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。

设计模式是一组重复的经验,大多数人都知道,编目和代码设计经验。使用设计模式是重用代码,使代码更容易被其他人理解,并确保代码的可靠性。

单例模式

单例模式就是在程序运行中只实例化一次,创建一个全局唯一对象。

饿汉模式:不能实现懒加载,造成空间浪费

//在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
public class SingletonObject {
    // 利用静态变量来存储唯一实例
    private static final SingletonObject instance = new SingletonObject();
 
    // 私有化构造函数
    private SingletonObject(){
        // 里面可能有很多操作
    }
 
    // 提供公开获取实例接口
    public static SingletonObject getInstance(){
        return instance;
    }
}

懒汉模式:

  • 在不加锁的情况下,线程不安全,可能出现多份实例
  • 在加锁的情况下,会是程序串行化,使系统有严重的性能问题
public class SingletonObject {
    // 定义静态变量时,未初始化实例
    private static SingletonObject instance;
 
    // 私有化构造函数
    private SingletonObject(){
 
    }
 
    public static SingletonObject getInstance(){
        // 使用时,先判断实例是否为空,如果实例为空,则实例化对象
        // 这段代码在多线程的情况下是不安全的
        if (instance == null)
            instance = new SingletonObject();
        return instance;
    }
}

静态内部类单例模式:静态内部类单例模式实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。

public class SingletonObject {
    private SingletonObject(){
 
    }
    // 单例持有者
    private static class InstanceHolder{
        private  final static SingletonObject instance = new SingletonObject();
    }
    // 
    public static SingletonObject getInstance(){
        // 调用内部类属性
        return InstanceHolder.instance;
    }
}

枚举类单例模式:枚举类型是线程安全的,并且只会装载一次,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。

public class SingletonObject {
    private SingletonObject(){
 
    }
    private enum Singleton{
        INSTANCE;
        private final SingletonObject instance;
 
        Singleton(){
            instance = new SingletonObject();
        }
 
        private SingletonObject getInstance(){
            return instance;
        }
    }
    public static SingletonObject getInstance(){
        return Singleton.INSTANCE.getInstance();
    }
}

除枚举方式外,其他方法都会通过反射的方式破坏单例,反射是通过调用构造方法生成新的对象,所以如果我们想要阻止单例破坏,可以在构造方法中进行判断,若已有实例, 则阻止生成新的实例。

private SingletonObject(){
    if (instance !=null){
        throw new RuntimeException("实例已经存在,请通过 getInstance()方法获取");
    }
}

如果单例类实现了序列化接口Serializable, 就可以通过反序列化破坏单例,所以我们可以不实现序列化接口,如果非得实现序列化接口,可以重写反序列化方法readResolve(), 反序列化时直接返回相关单例对象。

public Object readResolve() throws ObjectStreamException {
    return instance;
}
工厂模式
  • 简单工厂模式:一个工厂对象决定创建哪一种产品类型的实例,只适用于工厂类负责创建的对象较少的场景
  • 工厂方法模式:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行
public class CarFactory { //简单工厂,我们的调用者不必依赖我们的创建者,仅仅需要告诉工厂你要什么,无需知道工厂去哪里帮你实现你的需求
	static final int CAR_BYD = 0;
  static final int CAR_TSL = 1;
	public static Car createCar(int type){
		switch (type) {
		case 0:
			return new Byd();
		case 1:
			return new Tsl();
		default:
			System.out.println("无法识别的品牌");
			return null;
		}
	}
}
//抽象工厂
interface CarFactory {
	Car createCar();
}
public class BydFactory implements CarFactory{
	@Override
	public Car createCar() {
		return new Byd();
	}
}
public class TslFactory implements CarFactory{
	@Override
	public Car createCar() {
		return new Tsl();
	}
}
//调用者
public class CarUse {
	public static void main(String[] args) {
		Car c1 = new BydFactory().createCar();
		Car c2 = new TslFactory().createCar();
		c1.run();
		c2.run();
	}
}
//工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个工厂类,而工厂方法模式有一组实现了相同接口的工厂类。调用者不是什么都找一个工厂,而是需要什么就找什么工厂提供,这样可选择的范围更大
抽象工厂模式
  • 抽象工厂模式建议为系列中的每件产品明确声明接口(例如椅子、沙发或咖啡桌)。然后,确保所有产品变体都继承这些接口。例如,所有风格的椅子都实现椅子接口;所有风格的咖啡桌都实现咖啡桌接口,以此类推。
  • 接下来,我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。例如create­Chair创建椅子、create­Sofa创建沙发和create­Coffee­Table创建咖啡桌。这些方法必须返回抽象产品类型,即我们之前抽取的那些接口:椅子,沙发和咖啡桌等等。
  • 我们都将基于抽象工厂接口创建不同的工厂类。每个工厂类都只能返回特定类别的产品,例如,现代家具工厂 Modern­Furniture­Factory 只能创建现代椅子 Modern­Chair 、现代沙发 Modern­Sofa 和现代咖啡桌Modern­Coffee­Table 对象。
  • 客户端代码可以通过相应的抽象接口调用工厂和产品类。你无需修改实际客户端代码,就能更改传递给客户端的工厂类,也能更改客户端代码接收的产品变体。
  • 最后一点说明:如果客户端仅接触抽象接口,那么谁来创建实际的工厂对象呢?一般情况下, 应用程序会在初始化阶段创建具体工厂对象。而在此之前,应用程序必须根据配置文件或环境设定选择工厂类别。
抽象产品(Abstract Product)为构成系列产品的一组不同但相关的产品声明接口。
具体产品(Concrete Product)是抽象产品的多种不同类型实现。所有变体(维多利亚/现代)都必须实现相应的抽象产品(椅子/沙发)。
抽象工厂(Abstract Factory)接口声明了一组创建各种抽象产品的方法。
具体工厂(Concrete Factory)实现抽象工厂的构建方法。每个具体工厂都对应特定产品变体,且仅创建此种产品变体。

//----在构造组合这些产品的工厂
interface BydFactory{ //比亚迪工厂
	Tyre addTyre();  //装配轮胎
	Seat addSeat(); //装配座椅
	Engine addEngine(); //装配发动机
}
//高端比亚迪工厂
class HighBydFactory implements BydFactory{
	@Override
	public Engine addEngine() {
		return new HighEngine();
	}
	@Override
	public Seat addSeat() {
		return new HighSeat();
	}
	@Override
	public Tyre addTyre() {
		return new HighTyre();
	}
}
//低端比亚迪工厂
class LowBydFactory implements BydFactory{
	@Override
	public Engine addEngine() {
		return new LowEngine();
	}
	@Override
	public Seat addSeat() {
		return new LowSeat();
	}
	@Override
	public Tyre addTyre() {
		return new LowTyre();
	}
}
## 简单工厂、工厂方法、抽象工厂对比
​ 1)简单工厂:生产同一产品等级的任意指定的产品。(不支持增加新产品,增加新产品需要修改代码)
​ 2)工厂方法:生产同一产品等级的固定工厂产品。(支持增加新产品)
​ 3)抽象工厂:生产不同产品族的全部产品。(支持增加产品族,但不支持增加新产品)
建造者模式

img

一般端直接和Director导向器沟通,通过向Director传入不同的ConcreteBuilder(具体建造者)构建不同表示的Product(产品)。所以建造者模式把对象的构建(由Builder的实现来负责的)和装配(由Director负责的)进行了解耦,不同的构建器,相同的装配,也可以做出不同的产品。

  • ​ 1)可以逐步构建对象、延迟构建步骤,将构建和表示分离;
  • ​ 2)各个具体的建造者相互独立,您可以重用相同的构建代码;
//抽象建造者
public interface Builder {
 void setTyre(Tyre tyre);
 void setSeat(Seat seat);
 void setEngine(Engine engine);
}
//实际建造者,提供方法设置不同类型的组件
public class BydBuilder implements Builder {
 private Tyre tyre;
 private Seat seat;
 private Engine engine;
 
 @Override
 public void setEngine(Engine engine) {
  this.engine = engine;
 }

 @Override
 public void setSeat(Seat seat) {
        this.seat = seat; 
 }

 @Override
 public void setTyre(Tyre tyre) {
        this.tyre = tyre;
 }
    //得到产品
 public BydCar getMyBydCar(){
  return new BydCar(tyre, seat, engine);
 }
}
class BydCar{
 private Tyre tyre;
 private Seat seat;
 private Engine engine;
 
 public BydCar(Tyre tyre,Seat seat,Engine engine){
  this.tyre = tyre;
  this.seat = seat;
  this.engine = engine;
  System.out.println(this.toString());
 }
}

//创建导向器,提供不同的方法组装不同类型的组件
public class Director {

 public void highBydCar(Builder builder){
  builder.setEngine(new Engine("高端发动机"));
  builder.setTyre(new Tyre("高端轮胎,可用10年"));
  builder.setSeat(new Seat("高端真皮座椅"));
 }
 
 public void lowBydCar(Builder builder){
  builder.setEngine(new Engine());
  builder.setTyre(new Tyre());
  builder.setSeat(new Seat());
 }
 //目前预售中端款比亚迪
 public void MiddleBydCar(Builder builder){
  builder.setEngine(new Engine("高端发动机"));
  builder.setTyre(new Tyre()); //低端轮胎
  builder.setSeat(new Seat()); //低端座椅
 }
}
public class Client { // 客户端调用
 public static void main(String[] args) {
   //导向器
   Director director = new Director();
   //建造者
   BydBuilder builder = new BydBuilder();
   //产品
   System.out.println("顾客1,看中了高端比亚迪");
   director.highBydCar(builder);
   builder.getMyBydCar();
   System.out.println("顾客2,看中了低端比亚迪");
   director.lowBydCar(builder);
   builder.getMyBydCar();
   System.out.println("顾客3,看中了中端比亚迪");
   director.MiddleBydCar(builder);
   builder.getMyBydCar();
   
   System.out.println("顾客4,希望按照自己的意愿定制比亚迪");
   builder.setEngine(new Engine("高端发动机"));
   builder.setSeat(new Seat("高端座椅"));
   builder.setTyre(new Tyre());//低端轮胎
   builder.getMyBydCar();
 }
}
装饰器模式

装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

装饰器模式的主要优点有:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则
package decorator;
import java.awt.*;
import javax.swing.*;
public class MorriganAensland {
    public static void main(String[] args) {
        Morrigan m0 = new original();
        m0.display();
        Morrigan m1 = new Succubus(m0);
        m1.display();
        Morrigan m2 = new Girl(m0);
        m2.display();
    }
}
//抽象构件角色:莫莉卡
interface Morrigan {
    public void display();
}
//具体构件角色:原身
class original extends JFrame implements Morrigan {
    private static final long serialVersionUID = 1L;
    private String t = "Morrigan0.jpg";
    public original() {
        super("《恶魔战士》中的莫莉卡·安斯兰");
    }
    public void setImage(String t) {
        this.t = t;
    }
    public void display() {
        this.setLayout(new FlowLayout());
        JLabel l1 = new JLabel(new ImageIcon("src/decorator/" + t));
        this.add(l1);
        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}
//抽象装饰角色:变形
class Changer implements Morrigan {
    Morrigan m;
    public Changer(Morrigan m) {
        this.m = m;
    }
    public void display() {
        m.display();
    }
}
//具体装饰角色:女妖
class Succubus extends Changer {
    public Succubus(Morrigan m) {
        super(m);
    }
    public void display() {
        setChanger();
        super.display();
    }
    public void setChanger() {
        ((original) super.m).setImage("Morrigan1.jpg");
    }
}
//具体装饰角色:少女
class Girl extends Changer {
    public Girl(Morrigan m) {
        super(m);
    }
    public void display() {
        setChanger();
        super.display();
    }
    public void setChanger() {
        ((original) super.m).setImage("Morrigan2.jpg");
    }
}

动图

桥接模式
  • 定义:就是把抽象和实现分离出来,然后中间通过组合来搭建他们之间的桥梁。
  • 桥接模式可以取代多重继承的方案,多重继承违背了单一职责原则,复用性较差
  • 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时,可以使用桥接模式可以解耦这些变化的维度
// 有 电脑品牌 和 电脑类型 两个维度需要扩展
// 电脑品牌
public interface IComputerBrand {
 void open(); //都能开机
 void close(); //都能关机
 void playGame(); //都能玩游戏
}
// 戴尔品牌
public class Dell implements IComputerBrand{
 @Override
 public void close() {
  System.out.print("戴尔电脑关机");
 }
 @Override
 public void open() {
  System.out.print("戴尔电脑开机");
 }
 @Override
 public void playGame() {
  System.out.print("戴尔电脑玩游戏");
 }
}
// 电脑类型
public abstract class IComputerType {
 private IComputerBrand icb; //聚合 电脑品牌
 public IComputerType(IComputerBrand icb) {
     this.icb = icb;
 } 
 public void close() {
  icb.close();
 }
 public void open() {
  icb.open();
 }
 public void playGame() {
  icb.playGame();
 }
}
// 台式机类型
public class TaiSComputer extends IComputerType{
 private String name;
 public TaiSComputer(IComputerBrand icb) {
  super(icb);
  this.name = "台式机";
 }
 @Override
 public void close() {
  super.close();
  System.out.println("-"+name);
 }
 @Override
 public void open() {
  super.open();
  System.out.println("-"+name);
 }
 @Override
 public void playGame() {
  super.playGame();
  System.out.println("-"+name);
 }
}
main{ //使用
  IComputerType tsj1 = new TaiSComputer(new Dell());
  tsj1.open();
  tsj1.playGame();
  tsj1.close();
  System.out.println("--------");
  IComputerType tsj2 = new BjbComputer(new Apple());
  tsj2.open();
  tsj2.playGame();
  tsj2.close();
}
// 输出:
  戴尔电脑开机-台式机
  戴尔电脑玩游戏-台式机
  戴尔电脑关机-台式机
  --------
  苹果电脑开机-笔记本
  苹果电脑玩游戏-笔记本
  苹果电脑关机-笔记本
代理模式

代理对象为另一个对象提供替代或占位符,代理控制对原始对象的访问,允许您在请求到达原始对象之前或之后执行某些操作。这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

**静态代理:**在编译时就已经实现,编译完成后代理类是一个实际的class文件。

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
public interface ChinaMobileCompany { //公共接口
 public boolean handleSIMCard();
}
/**
 * 移动营业厅
 */
public class MobileBusinessHall implements ChinaMobileCompany{ // 委托类
 private String name;
 public MobileBusinessHall(String name){
  this.name = name;
 }
 @Override
 public boolean handleSIMCard() {
        System.out.println(name+"办理SIM卡");
  return true;  
 }
}
public class MobileProxy implements ChinaMobileCompany{ //代理商
 private MobileBusinessHall mbh;  // 持有 实际对象 并隐藏
 public MobileProxy(String name){
  if(mbh==null){
   mbh = new MobileBusinessHall(name);
  }
 }
 @Override
 public boolean handleSIMCard() {
  return mbh.handleSIMCard();
 }
}
main{ //使用
  MobileProxy mbp = new MobileProxy("中山路代理店");
  mbp.rechargeCallFee();
  mbp.handleSIMCard();
  mbp.numberLogout();
}
输出:
  中山路代理店充值话费
  中山路代理店办理sIM卡
  请到移动营业大厅办理:注销手机号业务

缺点:那会产生很多的代理类,如果实际类中新增了功能,那么所有的代理都得跟着改。

**动态代理:**在运行时生成"虚拟"的代理类,被ClassLoader加载

  • 通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)

  • 通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入;

public class JDKMobileProxy{ //JDK代理
 
 private Object bean;
 public JDKMobileProxy(Object obj){
  this.bean = obj;
 }
 public Object getProxy(){
        //newProxyInstance的三个参数:
        //第一个参数:ClassLoader loader,定义了由哪个ClassLoader来对生成的代理对象进行加载
        //第二个参数:Class[] interfaces,需要代理的类的接口,就像我们自己写静态代理类一样
        //第三个参数:InvocationHandler h,它也是一个接口,动态代理对象在调用方法的时候,决定关联到哪一个InvocationHandler对象上的invoke方法
        //byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces),生成字节码,然后通过这些字节码实现代理类的生成。
  return Proxy.newProxyInstance(this.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if("numberLogout".equals(method.getName())){
     throw new UndeclaredThrowableException(null, "不能代理注销");
    }
    Object invoke = null;
    try {
     invoke = method.invoke(bean, args);
    } catch (InvocationTargetException e) {
     throw e.getCause();
    }
    return invoke;
   }
  });
 }
}
main{
  ChinaMobileCompany cm = new MobileBusinessHall("中山路代理店");
  JDKMobileProxy jdkProxy = new JDKMobileProxy(cm);
        //获取代理对象
  ChinaMobileCompany yddl = (ChinaMobileCompany) jdkProxy.getProxy();
  yddl.handleSIMCard();
}

JDK代理:只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了,解决方案: 使用 CGLIB,Code Generation Library是一个基于ASM的字节码生成库。

适配器模式

类适配器:Adapter 类,通过继承 src 类,实现 dst 类接口,完成 src->dst 的适配。

 // src
public class Voltage220V{
    public int output220V() {
        int src = 220;
        System.out.println("电压:" + src);
        return src;
    }
}
// dst
public interface IVoltage5V {
    int output5V();
}
// adapter
public class VoltageAdapter extends Voltage220V implements IVoltage5V{
    @Override
    public int output5V() {
        int srcV = output220V();
        int dstV = srcV / 44;
        return dstV;
    }  
}
//使用
public class Client {

    public static void main(String[] args) {
        System.out.println("适配器模式");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}
  1. Java 是单继承机制,所以类适配器需要继承 src 类这一点算是一个缺点, 因为这要求 dst 必须是接口,有一定局限性;

  2. src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本。

对象适配器:基本思路和类的适配器模式相同,只是将 Adapter 类作修改,不是继承 src 类,而是持有 src 类的实例,以解决兼容性的问题。 即:持有 src 类,实现 dst 类接口,完成 src->dst 的适配

public class Client {
    public static void main(String[] args) {
        System.out.println("适配器模式");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}
观察者模式

观察者模式是一种行为设计模式,指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 这种模式和发布-订阅模式很像,不同的是,发布-订阅模式,一般有一个调度中心,调度中心有拉模式和推模式来推送消息。

//抽象观察者
public interface Observer {
 void response(); //反应
}
public class ConcreteStudentA implements Observer{
 @Override
 public void response() {
  System.out.println("学生A,收起自己的零食");
 }
}
//抽象主题,提供保存观察者的类,可以新增或移除观察者
public abstract class Subject {
  protected List<Observer> observers = new ArrayList<Observer>();
 //增加观察者方法
    public void add(Observer observer) {
        observers.add(observer);
    }
    //删除观察者方法
    public void remove(Observer observer) {
        observers.remove(observer);
    }
    public abstract void notifyObserver(); //通知观察者方法
}
//具体主题
public class ConcreteSubject extends Subject{

 @Override
 public void notifyObserver() {
  System.out.println("被观察者看到班主任来了,喊了一句:老师来了!");
  for (Object obs : observers) {
   ((Observer)obs).response(); //观察者做出反应
  }
 }
}
策略模式

对象有某个行为,但是在不同的场景中,该行为有不同的实现算法.。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法.定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

思想:在运行时(非编译时)改变软件的算法行为,定义一个通用的问题,使用不同的算法来实现,然后将这些算法都封装在一个统一接口。

Strategy bySubway = new BySubwayStrategy();
//乘地铁
Context ctx = new Context(bySubway);
ctx.goToPark();
//乘公交
Strategy byBus = new ByBusStrategy();
ctx.setStrategy(byBus);
ctx.goToPark();

应用:当有大量条件语句在同一算法的不同变体之间切换时,可将每个条件分支移入它们各自的策略类中替代这些条件语句,来消除这种条件。

责任链模式

在面向对象的开发中,往往力求对象之间保持松耦合,确保对象各自的责任最小化、具体化。经常用Filter做拦截器,即把所有的Filter都放在FilterChain中,然后依次执行每个过滤器,有符合条件的就处理,直到没有其他过滤器,就访问请求的资源。

public interface LeaderChain {
 public abstract void setNextChain(LeaderChain nextChain);
 public abstract void handleRequest(int days);
}
//组长
public class GroupLeader implements LeaderChain{
 private LeaderChain nextChain;
    //这部分可以提取BaseHandle
 @Override
 public void setNextChain(LeaderChain nextChain) {
  this.nextChain=nextChain;
 }

 @Override
 public void handleRequest(int days) {
  if(1==days){
   System.out.println("组长同意");
  }else{
   //交给上一级处理
   this.nextChain.handleRequest(days);
  }
 }
}
//...部门经理,部门总监,总经理
public static void Leaveapplication(int days){
  LeaderChain gl = new GroupLeader();
  LeaderChain dp = new DpManager();
  LeaderChain cf = new CfInspector();
  LeaderChain gm = new GeneralManager();
  gl.setNextChain(dp);
  dp.setNextChain(cf);
  cf.setNextChain(gm);
  //提交请求
  gl.handleRequest(days);
 }
}
备忘录模式

​ 备忘录模式是一种行为设计模式,又叫快照模式,是指在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后需要时能将该对象恢复到原先保存的状态。具体采用哪种方法来存储对象状态,取决于对象需要保存时间的长短。

  • ​ 1)发起人(Originator)角色:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据,它可以访问备忘录里所有的信息;
  • ​ 2)备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人;
  • ​ 3)管理者(Caretaker)角色:对备忘录进行管理、保存和提供备忘录,但其不能对备忘录的内容进行访问与修改。
public class LolOriginator {  // 发起者
  public LolOriginator(String name,String type,Integer fightingNum){
    this.name = name;
    this.type = type;
    this.fightingNum = fightingNum;
   }

   //创建一个备忘录,进行备忘操作
   public LolMemento createMemento(){
    return new LolMemento(this);
   }

   //恢复成指定备忘录
   public void restoreMemento(LolMemento memento){
    this.name = memento.getName();
    this.type = memento.getType();
    this.fightingNum = memento.getFightingNum();
   }
 }
//备忘录
public class LolMemento {

 private String name; //英雄名字
 private String type; //英雄类型
 private Integer  fightingNum; //英雄战斗力
 
 public LolMemento(LolOriginator lol){
  this.name=lol.getName();
  this.type=lol.getType();
  this.fightingNum = lol.getFightingNum();
 }
}
//管理者,负责管理备忘录对象
public class LolCaretaker {
 private LolMemento memento;
 //如果有一连串的状态加成,可以保存多个状态
 //private List<LolMemento> mementoList = new ArrayList<LolMemento>();
 public LolMemento getMemento() {
  return memento;
 }
 public void setMemento(LolMemento memento) {
  this.memento = memento;
 }
}
//使用
 public static void main(String[] args) {
  //备忘录管理者
  LolCaretaker taker = new LolCaretaker();
  //创建一个英雄
  System.out.println("当前的英雄信息:");
  LolOriginator lol = new LolOriginator("德玛西亚之力", "战士", 666);
  System.out.println("  "+lol.toString());
  //进行一次备忘
  taker.setMemento(lol.createMemento());

  System.out.println("吃了一个红buff:");
  lol.setFightingNum(700);
  System.out.println("  "+lol.toString());
  
  System.out.println("红buff已经失效:");
  lol.restoreMemento(taker.getMemento());  //恢复到之前的状态
 }
}
迭代器模式

提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示(列表、堆栈、树…)的情况下遍历集合的元素。 所有实现了Iterator接口的都包含一个Iterator<E> iterator()方法,它使得调用者并不知道迭代的对象具体由哪个类来实例化的。

  • ​ 1)如果希望提供一种标准方法来迭代集合并隐藏客户端程序的实现逻辑时,可以使用迭代器模式;
  • ​ 2)当需要为遍历不同的聚合结构提供一个统一的接口时,可以使用迭代器模式。
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}
//先声明我们的频道枚举
public enum ChannelTypeEnum {
 CCTV1, CCTV2, CCTV3, JXTV1, JXTV2 ,ALL;
}

//Channel是一个简单的 POJO 类,包含频道号码和频道
public class Channel {
 private Integer number;
 private ChannelTypeEnum TYPE;
 public Channel(Integer number,ChannelTypeEnum TYPE){
  this.number = number;
  this.TYPE = TYPE;
 }
 public Integer getNumber() {
  return number;
 }
 public void setNumber(Integer number) {
  this.number = number;
 }
 public ChannelTypeEnum getTYPE() {
  return TYPE;
 }
 public void setTYPE(ChannelTypeEnum tYPE) {
  TYPE = tYPE;
 }
 @Override
 public String toString() {
  return this.number+"频道是"+this.TYPE;
 }
}
public interface ChannelIterator {
 //判断是否还有元素
 public boolean hasNext();
 //下个元素
 public Channel next();
 //是否是最后一个
 public boolean isLast();
}
public interface ChannelCollection { // 抽象聚合(Aggregate)角色
 public void addChannel(Channel c);
 public void removeChannel(Channel c);
 public ChannelIterator iterator(ChannelTypeEnum type);
}
public class ChannelCollectionImpl implements ChannelCollection {

 //频道列表
 private List<Channel> channelsList;

 public ChannelCollectionImpl() {
  channelsList = new ArrayList();
 }

 //添加频道
 public void addChannel(Channel c) {
  this.channelsList.add(c);
 }

 //移除频道
 public void removeChannel(Channel c) {
  this.channelsList.remove(c);
 }

 //遍历频道
 @Override
 public ChannelIterator iterator(ChannelTypeEnum type) {
  //通过一个内部类实现,以便该实现不被其他类修改(继承重写等)所影响
  return new ChannelIteratorImpl(type, this.channelsList);
 }

 //具体聚合(ConcreteAggregate)角色
 private class ChannelIteratorImpl implements ChannelIterator {

  private ChannelTypeEnum type;
  private List<Channel> channels;
  private int cursor; //定义游标
  public ChannelIteratorImpl(ChannelTypeEnum ty,
    List<Channel> channelsList) {
   this.type = ty;
   this.channels = channelsList;
  }
  @Override
  public boolean hasNext() {
   //通过比较大小来实现
   while (cursor < channels.size()) {
    Channel c = channels.get(cursor);
    if (c.getTYPE().equals(type) || type.equals(ChannelTypeEnum.ALL)) {
     return true;
    } else
     cursor++;
   }
   return false;
  }
  @Override
  public Channel next() {
   Channel c = channels.get(cursor);
   cursor++;
   return c;
  }
  @Override
  public boolean isLast() {
   if(cursor==(channels.size()-1)){
    return true;
   }
   return false;
  }
 }
}
// 使用
public class Client {

 public static void main(String[] args) {
  //新建集合,并添加频道(1-6频道)
  ChannelCollection channels = new ChannelCollectionImpl();
  channels.addChannel(new Channel(1,ChannelTypeEnum.CCTV1));
  channels.addChannel(new Channel(2,ChannelTypeEnum.CCTV2));
  channels.addChannel(new Channel(3,ChannelTypeEnum.CCTV3));
  channels.addChannel(new Channel(4,ChannelTypeEnum.JXTV1));
  channels.addChannel(new Channel(5,ChannelTypeEnum.JXTV2));
  channels.addChannel(new Channel(6,ChannelTypeEnum.JXTV2));
  //遍历所有频道
  ChannelIterator iterator = channels.iterator(ChannelTypeEnum.ALL);
  while(iterator.hasNext()){
   Channel channel = iterator.next();
   System.out.println(channel.toString());
   if(iterator.isLast()){
    System.out.println("我是最后一个了---------------");
   }
  }
  System.out.println("遍历指定的频道---------------");
  //发现有两个JXTV1,只遍历JXTV2
  ChannelIterator jxtv1 = channels.iterator(ChannelTypeEnum.JXTV2);
  while(jxtv1.hasNext()){
   Channel channel = jxtv1.next();
   System.out.println(channel.toString());
  }
  
 }
 
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1417204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android学习之路(25) Theme和Style

1、官方详细解读 样式和主题背景 | Android 开发者 | Android Developers 2、应用场景 类似web设计中css样式。将应用设计的细节与界面的结构和行为分开。 样式style &#xff1a;应用于 单个 View 的外观。样式可以指定字体颜色、字号、背景颜色等属性 主题theme&…

OpenHarmony—不支持解构赋值

规则&#xff1a;arkts-no-destruct-assignment 级别&#xff1a;错误 ArkTS不支持解构赋值。可使用其他替代方法&#xff0c;例如&#xff0c;使用临时变量。 TypeScript let [one, two] [1, 2]; // 此处需要分号 [one, two] [two, one];let head, tail [head, ...tail]…

WordPress如何使用SQL实现一键关闭/开启评论功能(已有评论)

WordPress本人就自带评论功能&#xff0c;不过由于种种原因&#xff0c;有些站长不想开启评论功能&#xff0c;那么应该怎么实现一键关闭评论功能或开启评论功能呢&#xff1f;或者针对已有评论功能的文章进行一键关闭或开启评论功能应该怎么操作&#xff1f; 如果你使用的Wor…

每日一道面试题:Java中序列化与反序列化

写在开头 哈喽大家好&#xff0c;在高铁上码字的感觉是真不爽啊&#xff0c;小桌板又拥挤&#xff0c;旁边的小朋友也比较的吵闹&#xff0c;影响思绪&#xff0c;但这丝毫不影响咱学习的劲头&#xff01;哈哈哈&#xff0c;在这喧哗的车厢中&#xff0c;思考着这样的一个问题…

Spring - 基本用法参考

Spring 官方文档 Spring容器启动流程&#xff08;源码解读&#xff09; BeanFactoryPostProcessor vs BeanPostProcessor vs BeanDefinitionRegistryPostProcessor&#xff1a; From java doc&#xff1a; BeanFactoryPostProcessor may interact with and modify bean defin…

MyBatis 如何整合 Druid 连接池?

Mybatis 如何整合 Druid 数据连接池呢&#xff1f;首先打开创建的 Maven 工程&#xff0c;找到 pom.xml 文件&#xff0c;添加 Druid 依赖。 <!--druid连接池--> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId&…

Mac思维导图软件XMind for mac 中文版

XMind for Mac是一款高效、易于使用的思维导图软件&#xff0c;能够帮助用户更好地组织思维和创意。无论您需要制作工作报告、演讲稿还是学习笔记&#xff0c;XMind都能够为您提供卓越的支持和帮助。 软件下载&#xff1a;XMind for mac 中文版下载 XMind for Mac具有直观的界面…

精通Python第18篇—数据之美:Pyecharts水球图绘制与交互的完整教程

Pyecharts水球图绘制与交互的完整教程 在数据可视化领域&#xff0c;Pyecharts是一个强大而灵活的工具&#xff0c;它能够以美观的方式呈现各种图表&#xff0c;其中之一就是炫酷水球图。水球图能够生动地展示数据的比例关系&#xff0c;给用户一种直观的感受。本文将深入介绍…

爬虫基础-计算机网络协议

一个数据的传输 这些设备的数据转发是通过协议来完成的&#xff0c;整个互联网可以说是完全由网络协议来维持的 不同的协议分工不同&#xff0c;比如ip协议确保了ip寻址&#xff0c;tcp协议确保了数据完整性 IP地址和URL ip地址 整个网络传输可以比作快递&#xff0c;数据就…

C51 单片机学习(一):基础外设

参考 51单片机入门教程 1. 单片机简介 1.1 定义 单片机&#xff08;Micro Controller Unit&#xff0c;简称 MCU&#xff09; 内部集成了 CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能单片机的任务是信息采集&#xff08;依靠传感器&#xff09;、处…

【Spark系列2】Spark编程模型RDD

RDD概述 RDD最初的概述来源于一片论文-伯克利实验室的Resilient Distributed Datasets&#xff1a;A Fault-Tolerant Abstraction for In-Memory Cluster Computing。这篇论文奠定了RDD基本功能的思想 RDD实际为Resilient Distribution Datasets的简称&#xff0c;意为弹性分…

Linux提权:Docker组挂载 Rsync未授权 Sudo-CVE Polkit-CVE

目录 Rsync未授权访问 docker组挂载 Sudo-CVE漏洞 Polkit-CVE漏洞 这里的提权手法是需要有一个普通用户的权限&#xff0c;一般情况下取得的webshell权限可能不够 Rsync未授权访问 Rsync是linux下一款数据备份工具&#xff0c;默认开启873端口 https://vulhub.org/#/envir…

Linux:共享内存

文章目录 System V共享内存的原理管理共享内存shmgetshmatshmdtshmctl 共享内存和管道实现进程间同步通信 前面介绍完了匿名管道和命名管道&#xff0c;那么本篇要引入的主题是共享内存 System V 作为进程通信部分的内容&#xff0c;共享内存必然有其存在的意义和价值&#x…

RabbitMQ快速实战

目录 什么是消息队列&#xff1f; 消息队列的优势 应用解耦 异步提速 削峰填谷 总结 主流MQ产品特点比较 Rabbitmq快速上手 创建用户admin Exchange和Queue Connection和Channel RabbitMQ中的核心概念总结 什么是消息队列&#xff1f; MQ全称Message Queue&#xf…

从零学习Linux操作系统 第二十二部分 企业域名解析服务的部署及安全优化

# 一、dns的主要信息 关于dns的名词解释&#xff1a;dns: domain name service(域名解析服务) 关于客户端: /etc/resolv.conf dns指向文件 A记录 ##ip地址叫做域名的Address 记录 SOA ##授权起始主机 关于服务端 bind安装包named服务名称/etc/named.conf主配置文件/var/na…

【深度学习:多关节嵌入模型】 Meta 解释的 ImageBind 多关节嵌入模型

【深度学习&#xff1a;多关节嵌入模型】 Meta 解释的 ImageBind 多关节嵌入模型 Meta 发布开源人工智能工具的历史分段任何模型DINOv2 什么是多模态学习&#xff1f;什么是嵌入&#xff1f;什么是 ImageBind&#xff1f;集成在 ImageBind 中的模式图像绑定架构特定模式编码器跨…

window下如何安装ffmpeg(跨平台多媒体处理工具)

ffmpeg是什么? FFmpeg是一个开源的跨平台多媒体处理工具&#xff0c;可以用于录制、转换和流媒体处理音视频。它包含了几个核心库和工具&#xff0c;可以在命令行下执行各种音视频处理操作&#xff0c;如剪辑、分割、合并、媒体格式转换、编解码、流媒体传输等。FFmpeg支持多…

java设计模式:工厂模式

1&#xff1a;在平常的开发工作中&#xff0c;我们可能会用到不同的设计模式&#xff0c;合理的使用设计模式&#xff0c;可以提高开发效率&#xff0c;提高代码质量&#xff0c;提高系统的可拓展性&#xff0c;今天来简单聊聊工厂模式。 2&#xff1a;工厂模式是一种创建对象的…

Java TemporalAdjusters 时间调节器

提供了非常多处理日期相关的函数&#xff1a; 使用示例&#xff1a; /*** JCccc* param args*/public static void main(String[] args) {DateTimeFormatter pattern DateTimeFormatter.ofPattern("yyyy-MM-dd");LocalDateTime now LocalDateTime.now();//获取当月…

备战蓝桥杯---二分(入门)

话不多说&#xff0c;先来个模板题来回顾一下上次讲的&#xff1a; 下面是AC代码&#xff1a; 下面进入正题&#xff1a; 本题对1&#xff0c;2行与3&#xff0c;4行组合&#xff0c;再用二分查找即可实现n^2logn的复杂度。 下面是AC代码&#xff1a; 接题&#xff1a; 让我们…