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

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

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

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

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

单例模式

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

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

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

懒汉模式:

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

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

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

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

java 复制代码
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();
    }
}

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

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

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

java 复制代码
public Object readResolve() throws ObjectStreamException {
    return instance;
}
工厂模式
  • 简单工厂模式:一个工厂对象决定创建哪一种产品类型的实例,只适用于工厂类负责创建的对象较少的场景
  • 工厂方法模式:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行
java 复制代码
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创建咖啡桌。这些方法必须返回抽象产品类型,即我们之前抽取的那些接口:椅子,沙发和咖啡桌等等。
  • 我们都将基于抽象工厂接口创建不同的工厂类。每个工厂类都只能返回特定类别的产品,例如,现代家具工厂 ModernFurnitureFactory 只能创建现代椅子 ModernChair 、现代沙发 ModernSofa 和现代咖啡桌ModernCoffeeTable 对象。
  • 客户端代码可以通过相应的抽象接口调用工厂和产品类。你无需修改实际客户端代码,就能更改传递给客户端的工厂类,也能更改客户端代码接收的产品变体。
  • 最后一点说明:如果客户端仅接触抽象接口,那么谁来创建实际的工厂对象呢?一般情况下, 应用程序会在初始化阶段创建具体工厂对象。而在此之前,应用程序必须根据配置文件或环境设定选择工厂类别。
java 复制代码
抽象产品(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();
	}
}
markdown 复制代码
## 简单工厂、工厂方法、抽象工厂对比
​ 1)简单工厂:生产同一产品等级的任意指定的产品。(不支持增加新产品,增加新产品需要修改代码)
​ 2)工厂方法:生产同一产品等级的固定工厂产品。(支持增加新产品)
​ 3)抽象工厂:生产不同产品族的全部产品。(支持增加产品族,但不支持增加新产品)
建造者模式

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

  • 1)可以逐步构建对象、延迟构建步骤,将构建和表示分离;
  • 2)各个具体的建造者相互独立,您可以重用相同的构建代码;
java 复制代码
//抽象建造者
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)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

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

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则
java 复制代码
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");
    }
}
桥接模式
  • 定义:就是把抽象和实现分离出来,然后中间通过组合来搭建他们之间的桥梁。
  • 桥接模式可以取代多重继承的方案,多重继承违背了单一职责原则,复用性较差
  • 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时,可以使用桥接模式可以解耦这些变化的维度
java 复制代码
// 有 电脑品牌 和 电脑类型 两个维度需要扩展
// 电脑品牌
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文件。

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
java 复制代码
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实例对象传为参数传入;

java 复制代码
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 的适配。

java 复制代码
 // 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 的适配

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

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

java 复制代码
//抽象观察者
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(); //观察者做出反应
  }
 }
}
策略模式

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

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

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

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

责任链模式

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

java 复制代码
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)角色:对备忘录进行管理、保存和提供备忘录,但其不能对备忘录的内容进行访问与修改。
java 复制代码
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)当需要为遍历不同的聚合结构提供一个统一的接口时,可以使用迭代器模式。
java 复制代码
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}
java 复制代码
//先声明我们的频道枚举
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());
  }
  
 }
 
}
相关推荐
坚硬果壳_5 分钟前
《硬件架构的艺术》笔记(一):亚稳态
笔记·学习
醉颜凉28 分钟前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
糊涂君-Q30 分钟前
Python小白学习教程从入门到入坑------第三十一课 迭代器(语法进阶)
python·学习·程序人生·考研·职场和发展·学习方法·改行学it
阿维的博客日记33 分钟前
java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程
java·jvm
qiyi.sky33 分钟前
JavaWeb——Web入门(8/9)- Tomcat:基本使用(下载与安装、目录结构介绍、启动与关闭、可能出现的问题及解决方案、总结)
java·前端·笔记·学习·tomcat
lapiii35837 分钟前
图论-代码随想录刷题记录[JAVA]
java·数据结构·算法·图论
RainbowSea40 分钟前
4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明
java·spring·spring cloud
程序员小明z40 分钟前
基于Java的药店管理系统
java·开发语言·spring boot·毕业设计·毕设
dal118网工任子仪1 小时前
web安全漏洞之ssrf入门
笔记·学习·计算机网络·网络安全
爱敲代码的小冰1 小时前
spring boot 请求
java·spring boot·后端