设计模式

各个设计模式的关键点

单例模式: 某个类只能有一个实例,提供一个全局的访问点。

简单工厂: 一个工厂类根据传入的参量决定创建出那一种产品类的实例。

工厂方法: 定义一个创建对象的接口,让子类决定实例化那个类。

抽象工厂: 创建相关或依赖对象的家族,而无需明确指定具体类。

建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。

原型模式: 通过复制现有的实例来创建新的实例。

适配器模式: 将一个类的方法接口转换成客户希望的另外一个接口。

组合模式: 将对象组合成树形结构以表示“”部分-整体“”的层次结构。

装饰模式: 动态的给对象添加新的功能。

代理模式: 为其他对象提供一个代理以便控制这个对象的访问。

亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。

外观模式: 对外提供一个统一的方法,来访问子系统中的一群接口。

桥接模式: 将抽象部分和它的实现部分分离,使它们都可以独立的变化。

模板模式: 定义一个算法结构,而将一些步骤延迟到子类实现。

解释器模式: 给定一个语言,定义它的文法的一种表示,并定义一个解释器。

策略模式: 定义一系列算法,把他们封装起来,并且使它们可以相互替换。

状态模式: 允许一个对象在其对象内部状态改变时改变它的行为。

观察者模式:对象间的一对多的依赖关系。

备忘录模式:在不破坏封装的前提下,保持对象的内部状态。

中介者模式:用一个中介对象来封装一系列的对象交互。

命令模式: 将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。

访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。

责任链模式: 将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。

迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

单例

  1. 意图

    保证一个类只有一个实例,并且提供一个访问他的全局访问点

  2. 主要解决

    一个全局使用的类频繁的创建与销毁

  3. 何时使用

    当想控制实例数目,节省系统资源的时候

  4. 关键代码

    构造函数是私有的

懒汉模式

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    // 用到的时候在去创建,用不到不创建,懒汉行为
    public static Singleton getInstance () {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

饿汉模式

public class Singleton {
    // 饿汉模式,要求一直都有这个东西,想用的时候立刻就能用
    private static Singleton instance = new Singleton();
    private Singleton (){}
    public static Singleton getInstance () {
        return instance;
    }
}

优点

  1. 线程安全: 在类加载的时候创建实例,不会有线程混乱的不安全问题
  2. 获取对象速度快:在类加载的时候完成初始化,获取对象的速度较快

缺点

类加载较慢:由于类加载的时候就创建实例,会延长类的加载时间

懒汉模式下实现线程安全

public class Singleton {
    private static Singleton instance;
    private Singleton () {}
    public static synchronized Singleton getInstance () {
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

创建线程安全的单例的方式

双检锁/双重校验锁 (DCL,double-checked locking)

假如不加volatile可能出现的情况

  • 生成多个对象实例
  • 生成一个没有完全实例化完整的对象
public class Singleton {
    // 声明成volatile类型。
    priavate volatile static Singleton singleton;
    private Singleton () {}
    public static Singleton getInstance () {
        if(singleton == null) {
            //  类锁
            synchronized (Singleton.class){
              if(singleton == null) {
                singleton  = new Singleton ();
               }  
            }
        }
         return singleton;
    } 
}

登记式/静态内部类

public class Singleton {
    // 内部类去托管实例的创建,其实和饿汉模式没啥区别
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance () {
        return SingletonHolder.INSTANCE;
    }
}

枚举

public enum Singleton {
    INSTANCE;
    public void whateverMethod () {
    }
}

【待整理】代理模式、观察者模式、(抽象)工厂模式

代理模式

  • 定义

    为其他对象提供一种代理,以控制对这个对象的访问

    代理对象在客户端和目标对象之间起到中介的作用

  • 适用场景

    • 保护目标对象
    • 增强目标对象
  • 优点

    • 能够将代理对象与真实被调用的目标对象分离
    • 降低了系统的耦合度,扩展性好
    • 保护目标对象
    • 增强目标对象
  • 缺点

    • 造成系统设计中类的数目增加
    • 在客户端和目标对象中间增加一个代理对象,会造成请求处理速度变慢
    • 增加系统复杂度
  • 扩展

    • 静态代理–在代码中显示指定代理
    • 动态搭理-- jdk中的动态代理只能对实现了接口的类使用动态代理,动态代理无法代理类,可以代理接口
    • CGLib代理
      • 可以代理类,针对类实现进行代理,
      • 原理:如果代理一个类,生成一个被代理类的子类,覆盖其中的方法,通过继承然后还有重写,使用继承需要考虑如果这个类是final的,那这个类是无法被继承的,如果不是final的,但是方法是final的。那么这个方法,也是无法被重写的
    • spring中的代理选择
      • 当Bean有实现接口时,Spring就会用JDK的动态代理
      • 当Bean没有实现接口时候,Spring就会使用CGLib代理
      • 可以强制使用CGLib代理
        • 在spring配置中加入<aop:aspectj-autoproxy proxy-target-class =“true”/>
  • 静态代理模式

    可以做到在不修改目标对象的功能前提下,对目标功能扩展.

    2.缺点:

    • 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

    解决静态代理中的缺点是可以使用动态代理方式

    /**
    * 接口
    */
    public interface IUserDao{
        void save();
    }
    
    /**
    * 接口实现
    * 目标对象
    */
    public class UserDao implements IUserDao {
        public void save(){
            System.out.println("----已经保存数据!----");
        }
    }
    
    /**
    * 代理对象,静态代理
    */
    public class UerDaoProxy implements IUserDao{
        // 用于保存目标对象
        private IUserDao target;
        public UserDaoProxy (IUserDao target){
            this.target = target;
        }
        public void save(){
            // 相当于增强
           System.out.println("开始事务...");
            target.save();//执行目标对象的方法
            System.out.println("提交事务...");
        }
    }
    
    
    /**
    * 测试类App.java
    */
    public class App{
        public static void main(String args[]){
            // 目标对象
            UserDao target = new UserDao();
            // 代理对象,吧目标对象传给代理对象,建立代理关系
            UserDaoProxy proxy = new UserDaoProxy(target);
            // 指向的是代理对象的方法
            proxy.save();
        }
    }
    
  • 动态代理

    动态代理有以下特点:
    1.代理对象,不需要实现接口
    2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
    3.动态代理也叫做:JDK代理,接口代理

    JDK中生成代理对象的API
    代理类所在包:java.lang.reflect.Proxy
    JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
    

    注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

    • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
    • Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
    • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

    注意:

    代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理,因为在使用newProxyInstance方法的时候,假如没有实现接口interfaces,就会为空

    /*
    接口类IUserDao.java以及接口实现类,目标对象UserDao是一样的,没有做修改.在这个基础上,增加一个代理工厂类(ProxyFactory.java),将代理类写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理对象的中同名方法
    */
    /**
    * 创建动态代理对象,动态代理不需要实现接口,但是需要指定接口类型
    */
    public class ProxyFactory {
        /**
         * 维护一个目标对象
         */
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("开始事务2");
                            // 执行目标方法,用来执行某个的对象的目标方法
                            Object returnValue = method.invoke(target, args);
                            System.out.println("结束事务2");
                            return returnValue;
                        }
                    });
        }
    
    }
    
    /**
    * 测试类APP.java
    */
    public class App{
        public static void main(String [] args){
            // 目标对象
            IUserDao target = new UserDao();
            // 【原始的类型 class cn.itcast.b_dynamic.UserDao】
            System.out.println(target.getClass());
            // 给目标对象,创建代理对象
            IUserDao proxy = (IUserDao) new ProxyFacory(target).getProxyInstance();
            // class $Proxy0   内存中动态生成的代理对象
            System.out.println(proxy.getClass());
            // 执行方法, 【代理对象】
            proxy.save();
        }
    }
    
    
  
**Cglib代理**
  
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用**以目标对象子类**的方式类实现代理,这种方法就叫做:Cglib代理
  
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
  
  - **JDK的动态代理**有一个限制,就是使用动态**代理的对象必须实现一个或多个接口**,如果想代理没有实现接口的类,就可以使用Cglib实现.
  - Cglib是一个强大的高性能的代码生成包,它可以在**运行期**扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
  
**Cglib子类代理实现方法:**
  
  1. 需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入`spring-core-3.2.5.jar`即可.
  2. 引入功能包后,就可以在内存中动态构建子类
  3. **代理的类不能为final,因为final类不允许被继承**,否则报错
4. **目标对象的方法如果为final/static,那么就不会被拦截**,即不会执行目标对象额外的业务方法.
  
  ~~~java
  /**
  * 目标对象,没有实现接口
  */
  public class UsrDao{
      public void save(){
          System.out.println("----已经保存数据!----");
      }
  }
  
  /**
  * CgLib代理工厂
  * MethodInterceptor 这个类要在spring-core-3.2.5.jar中因为未引入所以目前没办法查看
  */
  public class CgLibProxyFactory implements MethodInterceptor {
      /**
       * 维护目标对象
       */
      private Object target;
  
      public CgLibProxyFactory(Object target) {
          this.target = target;
      }
  
      // 给目标对象创建一个代理对象
      public Object getProxyInstance() {
          // 1. 工具类,也要在spring-core-3.2.5.jar中
          EnHancer en = new EnHancer();
          // 2. 设置父类
          en.setSuperclass(target.getClass());
          //3.设置回调函数
          en.setCallback(this);
          //4.创建子类(代理对象)
          return en.create();
      }
  
      @Override
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
          System.out.println("开始事务...");
  
          //执行目标对象的方法
          Object returnValue = method.invoke(target, args);
  
          System.out.println("提交事务...");
  
          return returnValue;
      }
  }
  
  /**
  * 测试类
  */
  public class App {
  
      @Test
      public void test(){
          //目标对象
          UserDao target = new UserDao();
  
          //代理对象
          UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
  
          //执行代理对象的方法
          proxy.save();
      }
  }

观察者模式

何谓观察者模式?观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。

在这里,发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。所以观察者提供了一种对象设计,让主题和观察者之间以松耦合的方式结合。

img

观察者模式包含如下角色:
Subject: 目标
ConcreteSubject: 具体目标
Observer: 观察者
ConcreteObserver: 具体观察者

观察者模式和发布订阅模式的区别https://www.jianshu.com/p/594f018b68e7

主题接口

/**
 * 主题对象接口,所有的主题都实现主题接口
 */
public interface Subject {
    /**
     * 添加观察者
     */
     void addObserver(Observer observer);

    /**
     * 删除指定观察者
     * @param observer
     */
     void deleteObserver(Observer observer);

    /**
     * 通知所有观察者
     */
     void notifyObservers();
}



具体主题

/**
 * 主题类,也就是我们的被观察者
 */
public class WeatherData implements  Subject{
    /**温度*/
    private float temperature;
    /** 湿度 */
    private float humidity;
    /** 气压 */
    private float airpressure;
    /** 观察者列表 */
    private ArrayList<Observer> observerArrayList;

    public WeatherData() {
        this.observerArrayList = new ArrayList<>();
    }

    /**
     * 添加指定观察者对象
     * @param observer
     */
    @Override
    public void addObserver(Observer observer) {
        this.observerArrayList.add(observer);
    }

    /**
     * 删除指定观察者对象
     * @param observer
     */
    @Override
    public void deleteObserver(Observer observer) {
        int i;
        if((i = observerArrayList.indexOf(observer)) != -1) {
            this.observerArrayList.remove(i);
        }
    }

    /**
     * 通知观察者
     */
    @Override
    public void notifyObservers() {
        for(Observer observer : this.observerArrayList) {
            observer.update(this.temperature, this.humidity, this.airpressure);
        }
    }

    /**
     * 被观察者数据发生改变
     * @param temperature
     * @param humidity
     * @param airpressure
     */
    public void setMeasurements(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.measurementsChanged();
    }

    /**
     * 修改后,通知观察者
     */
    public void measurementsChanged() {
        this.notifyObservers();
    }
}

观察者接口

/**
 * 观察者接口,所有的观察者都实现此接口,称为观察者对象
 */
public interface Observer {
    /**
     * 调用观察者者更新接口
     * @param temperature
     * @param humidity
     * @param airpressure
     */
     void update(float temperature, float humidity, float airpressure);
}

具体观察者

/**
 * 当前版观察者对象:观察者对象,在初始化构造的时候,注册主题(成为观察主题的对象)
 */
public class CurrentConditionsDisplay implements  Observer,DisplayElement{
    private float temperature;
    private float humidity;
    private float airpressure;
    private Subject subject;

    public CurrentConditionsDisplay(Subject subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void display() {
        System.out.println("当前版收听到数据:气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }

    @Override
    public void update(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.display();
    }
}


/**
 * 预测板观察者对象:观察者对象,在初始化构造的时候,注册主题(成为观察主题的对象)
 */
public class ForecastDisplay  implements Observer, DisplayElement{


    private float temperature;
    private float humidity;
    private float airpressure;
    private Subject subject;

    public ForecastDisplay(Subject subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.display();
    }

    @Override
    public void display() {
        System.out.println("预测板收听到数据:气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}

/**
 * 统计板观察者对象:观察者对象,在初始化构造的时候,注册主题(成为观察主题的对象)
 */
public class StatisticsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float airpressure;
    private Subject subject;

    public StatisticsDisplay(Subject subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float airpressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.airpressure = airpressure;
        this.display();
    }

    @Override
    public void display() {
        System.out.println("统计板收听到数据:气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}

测试类

public class APP {
    public static void main(String[] args) {
        //被观察者
        WeatherData weatherData = new WeatherData();

        //观察者对象
        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

        weatherData.setMeasurements(80, 85, 90);
    }
}

结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjahtpD9-1592924161437)(E:\文档\学习资料\笔记\面经\设计模式.assets\1586360540564.png)]

第二种实现方式

第二种实现方式,用Java的内置的API实现观察者模式,主题对象继承Observable类,观察者对象实现Observer接口即可和以上方法类似。

// 主题对象继承Observable类
public class WeatherData extends Observable

// 观察者对象实现Observer接口

public class CurrentConditionsDisplay implements Observer, DisplayElement {
 
    private float temperature;
    private float humidity;
    private float airpressure;
    private Observable observable;
 
    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }
 
    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData) {
            this.temperature = ((WeatherData) o).getTemperature();
            this.humidity = ((WeatherData) o).getHumidity();
            this.airpressure = ((WeatherData) o).getAirpressure();
            this.display();
        }
    }
 
    @Override
    public void display() {
        System.out.println("气温:"+ this.temperature+"\t"+"湿度:"+this.humidity+"\t"+"气压:"+this.airpressure);
    }
}


中介模式

所谓中介者模式就是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。在中介者模式中,中介对象用来封装对象之间的关系,各个对象可以不需要知道具体的信息通过中介者对象就可以实现相互通信。它减少了对象之间的互相关系,提供了系统可复用性,简化了系统的结构。

在中介者模式中,各个对象不需要互相知道了解,他们只需要知道中介者对象即可,但是中介者对象就必须要知道所有的对象和他们之间的关联关系,正是因为这样就导致了中介者对象的结构过于复杂,承担了过多的职责,同时它也是整个系统的核心所在,它有问题将会导致整个系统的问题。所以如果在系统的设计过程中如果出现“多对多”的复杂关系群时,千万别急着使用中介者模式,而是要仔细思考是不是您设计的系统存在问题。

img

Mediator: 抽象中介者
ConcreteMediator: 具体中介者
Colleague: 抽象同事类
ConcreteColleague: 具体同事类

中介接口*

/**
 * 中介者接口类
 */
public interface Mediator {
    // 注册委员
    void register(String name,ClassLeader c);
    //班长通过委员的名字获得通知请求
    void command(String lname);
}

具体中介类

/**
 * 班长  具体的中介
 */
public class ClassMonitor implements Mediator{
    //利用map集合存放学委的相关属性
    private Map<String, ClassLeader> map = new HashMap<>();
    @Override
    public void register(String name, ClassLeader c) {
        map.put(name, c);
    }

    @Override
    public void command(String lname) {
        map.get(lname).job();
    }
}

抽象同事类

/**
 * 班级干部接口
 */
public interface ClassLeader {
    /**
     * 本职工作
     */
    void job();

    /**
     * 向班长发送请求
     */
    void sendRequest();
}

具体同事类

/**
 * 生活委员
 */
public class LifeLeader implements ClassLeader {
    // 持有班长的引用
    private Mediator media;

    public LifeLeader(Mediator media) {
                  super();
                  this.media = media;
                  media.register("LifeLeader", this);
     }

    @Override
    public void job() {
        System.out.println("生活委员->小张最近生活作风有点问题,需要我的帮助!");
    }

    @Override
    public void sendRequest() {
        System.out.println("生活委员->小张是不是有什么精神负担,班长大人去叫心理委员去看看什么情况吧!");
        media.command("phycologic");
    }
}


/**
 * 心理委员
 */
public class PhychologicalLeader implements ClassLeader {
    //持有对班长大人的引用
    private Mediator media;
    public PhychologicalLeader(Mediator media) {
        super();
        this.media = media;
        media.register("phycologic", this);
    }

    @Override
    public void job() {
        System.out.println("心理委员->小张最近心情好像不太好,需要我的帮助!");
    }

    @Override
    public void sendRequest() {
        System.out.println("心理委员->小张是不是生活上有什么问题,班长大人叫生活委员多关注一下吧!");
        media.command("LifeLeader");
    }

}


/**
 * 学习委员
 */
public class StudyLeader implements ClassLeader {
    //持有对班长大人的引用
    private Mediator media;

    public StudyLeader(Mediator media) {
        super();
        this.media = media;
        media.register("StudyLeader", this);
    }

    @Override
    public void job() {
        System.out.println("学习委员->小张最近成绩突飞猛进,果然在我的英明指导下没有人能不起飞!");
    }

    @Override
    public void sendRequest() {
        System.out.println("学习委员->小张为了成绩居然还搞抄袭,怎么变成这样了?班长大人快去通知生活委员和心理委员看看情况!");
        media.command("LifeLeader");
        media.command("phycologic");
    }
}


** 测试类**

public class App {
    public static void main(String[] args) {
        Mediator m=new ClassMonitor();
        ClassLeader life=new LifeLeader(m);
        ClassLeader study=new StudyLeader(m);
        ClassLeader phycho=new PhychologicalLeader(m);
        //当前委员发送请求给班长然后通过班长与其他委员通信
        System.out.println("------生活委员->班长->心理委员------");
        life.sendRequest();
        System.out.println("--------------------------");
        System.out.println("------学习委员->班长->心理委员,生活委员------");
        study.sendRequest();
        System.out.println("--------------------------");
        System.out.println("------心理委员->班长->生活委员------");
        phycho.sendRequest();
        System.out.println("--------------------------");
    }
}


结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-42fpBCdB-1592924161450)(E:\文档\学习资料\笔记\面经\设计模式.assets\1586421314732.png)]

责任链模式

职责链模式描述的请求如何沿着对象所组成的链来传递的。它将对象组成一条链,发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个对象来处理它或者直到最后也没有对象处理而留在链末尾端。

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,这就是职责链模式。在职责链模式中,使得每一个对象都有可能来处理请求,从而实现了请求的发送者和接收者之间的解耦。同时职责链模式简化了对象的结构,它使得每个对象都只需要引用它的后继者即可,而不必了解整条链,这样既提高了系统的灵活性也使得增加新的请求处理类也比较方便。但是在职责链中我们不能保证所有的请求都能够被处理,而且不利于观察运行时特征。

img

职责链模式包含如下角色:
抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
Client: 客户类

应用:

Java异常处理机制

JavaScript事件模型

JavaEE的Filter拦截器

SpringSecurity Spring安全框架

抽象处理者角色

/**
 * 抽象处理者角色
 */
public abstract class Handler {
    /**
     * 持有后继的责任链
     */
    protected  Handler successor;

    /**
     * 示意处理请求的方法,虽然这个示意方法是没有传入参数的
     * 但实际是可以传入参数的,根据具体需要来选择是否传递参数
     */
    public abstract  void handleRequest();

    public Handler getSuccessor(){
        return successor;
    }

    /**
     * 设置后继的责任对象
     * @param successor
     */
    public void setSuccessor(Handler successor){
        this.successor = successor;
    }
}


具体处理者角色

/**
 * 具体处理者角色
 */
public class ConcreteHandler extends Handler{
    /**
     * 处理方法,调用此方法处理请求
     */
    @Override
    public void handleRequest() {
        if (getSuccessor() != null) {
            System.out.println("放过请求");
            getSuccessor().handleRequest();
        }else{
            System.out.println("处理请求");
        }
    }
}

测试类

/**
 */
public class App {
    public static void main(String[] args) {
        // 组装责任链
        Handler handler1 = new ConcreteHandler();
        Handler handler2 = new ConcreteHandler();
        handler1.setSuccessor(handler2);
        handler1.handleRequest();
    }
}

// 结果
放过请求
处理请求

装饰模式

  • 例子

    outputstream

    支持子类继承他完成装饰添加新功能

  • 我们可以通过继承和组合的方式来给一个对象添加行为,虽然使用继承能够很好拥有父类的行为,但是它存在几个缺陷:一、对象之间的关系复杂的话,系统变得复杂不利于维护。二、容易产生“类爆炸”现象。三、是静态的。在这里我们可以通过使用装饰者模式来解决这个问题。

    装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类更为灵活,虽然装饰者模式能够动态将责任附加到对象上,但是他会产生许多的细小对象,增加了系统的复杂度。

    img

    装饰模式包含如下角色:
    Component: 抽象构件,作为具体构件和装饰类的公共父类,定义相关的业务接口,主要为面向对象编程
    ConcreteComponent: 具体构件,作为抽象构件的子类,为具体业务的实现者;
    Decorator: 抽象装饰类,抽象构件的子类,定义为抽象类。用于扩展具体构件的业务功能,作为抽象存在只是为了用户端使用时面向接口编程。其内部维护一个抽象构件的引用,通过该引用来调用具体构件的方法,并通过其子类来扩展业务方法,实现装饰的目的。
    ConcreteDecorator: 具体装饰类,作为抽象装饰类的子类,完成对具体构件业务功能的扩展装饰功能。

  • 装饰模式的优缺点及使用场景

    (1)优点:

    ①装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

    ②通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

    (2)缺点:

    ①这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
    ②装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。

    ③装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

    (3)应用场景:

    ①需要扩展一个类的功能,或给一个类添加附加职责。
    ②需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
    ③需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
    ④当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类

  • 装饰模式与代理模式的区别。

    装饰模式意义在于对原有系统业务功能的扩展或者是装饰,而代理模式常用在原有系统中增加一些与业务无关的操作,例如日志、验证等功能。

    /**
     * 具体构建角色[ConcreteComponent]
     */
    public abstract class Person {
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        public abstract void show();
    }
    
    /**
     * 服饰类[Decorator]
     */
    public class Finery extends Person {
        private Person person;
    
        public Person getPerson() {
            return this.person;
        }
    
        public void setPerson(Person person) {
            this.person = person;
        }
    
        // 打扮
        public void Decorate(Person person) {
            this.person = person;
        }
    
        @Override
        public void show() {
            if (person != null) {
                person.show();
            }
        }
    }
    
    /**
     * 具体服饰类[ConcreteDecorator]之‘T恤’
     */
    public class TShirts extends Finery {
        @Override
        public void show() {
            // 在实际开发中,此应该为对人数据进行处理的过程。
            System.out.print("T恤    ");
            // 父类处理的过程
            super.show();
        }
    }
    
    /**
     * 具体服饰类[ConcreteDecorator]之‘垮裤’
     */
    class BigTrouser extends Finery {
        @Override
        public void show() {
            System.out.print("垮裤    ");
            super.show();
        }
    }
    
    /**
     * 具体服饰类[ConcreteDecorator]之‘破球鞋’
     */
    class Sneakers extends Finery {
        @Override
        public void show() {
            System.out.print("破球鞋    ");
            super.show();
        }
    }
    
    /**
     * 具体服饰类[ConcreteDecorator]之‘西装’
     */
    class Suit extends Finery {
        @Override
        public void show() {
            System.out.print("西装    ");
            super.show();
        }
    }
    
    /**
     * 具体服饰类[ConcreteDecorator]之‘领带’
     */
    class Tie extends Finery {
        @Override
        public void show() {
            System.out.print("领带    ");
            super.show();
        }
    }
    
    /**
     * 具体服饰类[ConcreteDecorator]之‘皮鞋’
     */
    class LeatherShoes extends Finery {
        @Override
        public void show() {
            System.out.print("皮鞋    ");
            super.show();
        }
    }
    
    /**
     * 测试类
     */
    public class App {
        public static void main(String[] args) {
            String name = "小菜";
            Person person = new Person() {
                @Override
                public void show() {
                    System.out.print("装扮的" + name);
                }
            };
    
            System.out.print("第一种装扮:");
            //破球鞋
            Sneakers sneakers = new Sneakers();
            //垮裤
            BigTrouser bigTrouser = new BigTrouser();
            //T恤
            TShirts tShirts = new TShirts();
    
            sneakers.Decorate(person);
            bigTrouser.Decorate(sneakers);
            tShirts.Decorate(bigTrouser);
            tShirts.show();
    
    
            System.out.print("\n第二种装扮:");
            //皮鞋
            LeatherShoes leatherShoes = new LeatherShoes();
            //领带
            Tie tie = new Tie();
            //西装
            Suit suit = new Suit();
    
            leatherShoes.Decorate(person);
            tie.Decorate(leatherShoes);
            suit.Decorate(tie);
            suit.show();
        }
    }
    
    
    

适配器模式

在我们的应用程序中我们可能需要将两个不同接口的类来进行通信,在不修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。这个中间件就是适配器。所谓适配器模式就是将一个类的接口,转换成客户期望的另一个接口。它可以让原本两个不兼容的接口能够无缝完成对接。

作为中间件的适配器将目标类和适配者解耦,增加了类的透明性和可复用性。

img

适配器模式包含如下角色:
Target:目标抽象类
Adapter:适配器类
Adaptee:适配者类
Client:客户类

实现适配器的三种方式

  1. 当希望将一个类转换为满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可

    /**
     * 视频源
     */
    public class Phone {
        public void typecPhone() {
            System.out.println("信息从Typec口的手机输出。");
        }
    }
    
    
    /**
    * 实现接口
    */
    public interface Vga {
        void vgaInterface();
    }
    
    /**
     * 类的适配器模式,原理:通过继承特性来实现适配器功能。
     */
    public class Typec2Vga1 extends Phone implements Vga {
        @Override
        public void vgaInterface() {
            typecPhone();
            System.out.println("接收到Type-c口信息,信息转换成VGA接口中...");
            System.out.println("信息已转换成VGA接口,显示屏可以对接。");
        }
    }
    
    
  2. 当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Typec2Vga2 类,持有原类的一个实例,在Typec2Vga2 类的方法中,调用实例的方法就行

    /**
     * 对象的适配器模式 原理:通过组合方式来实现适配器功能。
     */
    public class Typec2Vga2 implements Vga{
        private Phone phone;
    
        public Typec2Vga2(Phone phone) {
            this.phone = phone;
        }
    
        @Override
        public void vgaInterface() {
            if(phone != null){
                phone.typecPhone();
                System.out.println("接收到Type-c口信息,信息转换成VGA接口中...");
                System.out.println("信息已转换成VGA接口,显示屏可以对接。");
            }
        }
    }
    
    
  3. 当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法,我们写别的类的时候,继承抽象类即可

    /**
     * 接口的适配器模式 原理:借助抽象类来实现适配器功能。
     */
    public interface Target {
        void typec();
        void typec2vga();
        void typec2hdmi();
    }
    
    /**
     * 抽象类
     */
    public abstract  class Adapter implements  Target{
        @Override
        public void typec() {
    
        }
    
        @Override
        public void typec2vga() {
    
        }
    
        @Override
        public void typec2hdmi() {
    
        }
    }
    
    
    /**
     * 实现一个VGA适配器
     */
    public class VgaAdapter extends Adapter {
        @Override
        public void typec() {
            System.out.println("信息从Typec口的手机输出。");
        }
    
        @Override
        public void typec2vga() {
            System.out.println("接收到Type-c口信息,信息转换成VGA接口中...");
            System.out.println("信息已转换成VGA接口,显示屏可以对接。");
        }
    }
    
    
    
    /**
     * 测试类
     */
    
    public class Screen {
        public static void main(String[] args) {
            //第一种适配器用法
            System.out.println("-------------第一种适配器------------");
            Vga vga = new Typec2Vga1();
            //适配器将typec转换成vga
            vga.vgaInterface();
            System.out.println("显示屏对接适配器,手机成功投影到显示屏!");
    
            //第二种适配器用法
            System.out.println("-------------第二种适配器------------");
            Typec2Vga2 typec2Vga1 = new Typec2Vga2(new Phone());
            //适配器将typec转换成vga
            typec2Vga1.vgaInterface();
            System.out.println("显示屏对接适配器,手机成功投影到显示屏!");
    
            //第三种适配器用法
            System.out.println("-------------第三种适配器------------");
            VgaAdapter vgaAdapter = new VgaAdapter();
            vgaAdapter.typec();
            //适配器将typec转换成vga
            vgaAdapter.typec2vga();
            System.out.println("显示屏对接适配器,手机成功投影到显示屏!");
        }
    }
    
    
    

适配器模式在源码中的应用:

  1. JDK源码的IO模块用到,例如 java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b7aLuovX-1592924161456)(E:\文档\学习资料\笔记\面经\设计模式.assets\1586415925623.png)]

    InputStreamReader和OutputStreamWriter做了InputStream/OutputStream字节流类到Reader/Writer之间的转换。而从如上Sun JDK中的实现类关系结构中可以看出,是StreamDecoder和StreamEncoder的设计实现在实际上采用了适配器模式。

  2. MyBatis源码日志模块用到对象适配器模式。

    MyBatis的日志功能源码包logging就是适配器模式的应用,主要是为了将市场的各种日志插件,转换为MyBatis的日志接口,统一使用,下面是日志包的包结构

    img

    ​ 在LogFactory类加载的时候会执行静态代码块,其逻辑是按序加载并实例化对应的日志组件的适配器,然后使用Logfactory.logConstructor这个静态字段,记录当前使用的的第三方日志组件的适配器

    ​ 首先会检查logConstructor这个是不是空,如果为空则调用runnable.run方法,(注意不是start方法)

    ​ Slf4jImpl实现了Log接口并在类中使用组合的方式引入Log的引入结合上面的适配模式可以分析,Slf4jImpl就是Apdater类,Log接口就是Target LoggerFactory.getLogger(clazz)这个操作就是获取 org.slf4j.Logger 他就是Adaptee类

迭代器模式

对于迭代在编程过程中我们经常用到,能够游走于聚合内的每一个元素,同时还可以提供多种不同的遍历方式,这就是迭代器模式的设计动机。在我们实际的开发过程中,我们可能会需要根据不同的需求以不同的方式来遍历整个对象,但是我们又不希望在聚合对象的抽象接口中充斥着各种不同的遍历操作,于是我们就希望有某个东西能够以多种不同的方式来遍历一个聚合对象,这时迭代器模式出现了。

何为迭代器模式?所谓迭代器模式就是提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。迭代器模式是将迭代元素的责任交给迭代器,而不是聚合对象,我们甚至在不需要知道该聚合对象的内部结构就可以实现该聚合对象的迭代。

通过迭代器模式,使得聚合对象的结构更加简单,它不需要关注它元素的遍历,只需要专注它应该专注的事情,这样就更加符合单一职责原则了。

img

迭代器模式包含如下角色:
Iterator: 抽象迭代器
ConcreteIterator: 具体迭代器
Aggregate: 抽象聚合类
ConcreteAggregate: 具体聚合类

集合接口

/**
 * 自定义集合接口,类似java.util.Collection用于存储数据
 */

public interface ICollection<T> {
    //返回迭代器
    IIterator<T> iterator();
    void add(T t);
    T get(int index);
}

迭代器接口

/**
 * 自定义迭代接口,类似于java.util.Iterator
 * 用于遍历集合类ICollection的数据
 */
public interface IIterator<T> {
    boolean hasNext();
    boolean hasPrevious();
    T next();
    T previous();
}


集合类

/**
 * 集合类, 依赖于MyIterator
 */
public class MyCollection<T> implements ICollection<T> {
    private T[] arys;
    private int index = -1;
    private int capacity = 5;
    private int realCapacity;

    public MyCollection() {
        this.arys = (T[]) new Object[capacity];
    }

    @Override
    public IIterator<T> iterator() {
        return new MyIterator<T>(this);
    }

    @Override
    public void add(T t) {
        index++;
        if (index == capacity) {
            capacity *= 2;
            this.arys = Arrays.copyOf(arys, capacity);

        }
        this.arys[index] = t;
        realCapacity++;
    }

    @Override
    public T get(int index) {
        return this.arys[index];
    }

    private int getCount() {
        return realCapacity;
    }

    private static class MyIterator<T> implements IIterator<T> {

        private MyCollection<T> collection;
        private int cursor = 0;

        MyIterator(MyCollection<T> collection) {
            this.collection = collection;
        }

        @Override
        public boolean hasNext() {
            if (cursor < collection.getCount()) {
                return true;
            }
            return false;
        }

        @Override
        public boolean hasPrevious() {
            if (cursor > 0) {
                return true;
            }
            return false;
        }

        @Override
        public T next() {
            return collection.get(cursor++);
        }

        @Override
        public T previous() {
            return collection.get(cursor--);
        }
    }
}


测试类

/**
 * 测试类
 */
public class App {
    public static void main(String[] args) {
        ICollection<Integer> collection = new MyCollection<>();
        add(collection, 3, 5, 8, 12, 3, 3, 5);
        for(IIterator<Integer> iIterator = collection.iterator();iIterator.hasNext();){
            System.out.println(iIterator.next());
        }
        System.out.println("------------------------");
        ICollection collection2 = new MyCollection();
        add(collection2, "a", "b", "c", 3, 8, 12, 3, 5);
        for (IIterator iterator = collection2.iterator(); iterator.hasNext();) {
            System.out.println(iterator.next());
        }
    }
    static <T> void  add(ICollection<T> c, T ...a) {
        for (T i : a) {
            c.add(i);
        }
    }
}

备忘录模式

备忘录模式就是一种后悔药,它给我们的软件提供后悔药的机制,通过它可以使系统恢复到某一特定的历史状态。

所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它实现了对信息的封装,使得客户不需要关心状态保存的细节。保存就要消耗资源,所以备忘录模式的缺点就在于消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

img

备忘录模式包含如下角色:
Originator: 原发器
Memento: 备忘录
Caretaker: 负责人

存储卡,保存角色数据

public class MemoryCard {

    private int health;

    private int mana;

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getMana() {
        return mana;
    }

    public void setMana(int mana) {
        this.mana = mana;
    }

    public MemoryCard(int health,int mana){
        this.health =health;
        this.mana = mana;
    }
}

角色状态管理者,用于保存存储卡,并且不能对存储卡的内容进行操作

public class MemoryCardCreataker {

    private MemoryCard memoryCard;

    public MemoryCard getMemoryCard() {
        return memoryCard;
    }

    public void setMemoryCard(MemoryCard memoryCard) {
        this.memoryCard = memoryCard;
    }
}

游戏角色类

//游戏鬼泣的主角Dante类
public class Dante {
    //生命值
    private int health;
    //魔法值
    private int mana;

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getMana() {
        return mana;
    }

    public void setMana(int mana) {
        this.mana = mana;
    }

    //角色的初始状态
    public void getInitState(){
        this.health = 100;
        this.mana = 100;
    }

    public void deathState(){
        this.health = 0;
        this.mana = 0;
    }

    public void showState(){
        System.out.println("health:"+health+",mana:"+mana);
    }

    //将角色状态保存在存储卡中
    public MemoryCard saveState(){
        return new MemoryCard(health,mana);
    }

    //回档
    public void recoveryState(MemoryCard memoryCard){
        this.health = memoryCard.getHealth();
        this.mana = memoryCard.getMana();
    }
}

测试类

public class Test  {
    public static void main(String[] args) {
        //初始化状态
        Dante dante = new Dante();
        dante.getInitState();
        dante.showState();

        //存档
        MemoryCardCreataker memoryCardCreataker = new MemoryCardCreataker();
        memoryCardCreataker.setMemoryCard(dante.saveState());

        //角色死亡时的状态
        dante.deathState();
        dante.showState();

        //恢复存档
        dante.recoveryState(memoryCardCreataker.getMemoryCard());
        dante.showState();
    }
}

优点:使用备忘录模式很好地将被保存的对象和保存的状态分隔开来,并且该对象状态备份和恢复不需要自己处理,而是转交给客户端处理,并且如何保存的细节封装到了存储卡中,不再影响客户端。

缺点:由于备忘录对象会消耗内存,如果需要保存的状态占用的内存较大时,资源的消耗也就越大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值