深入浅出了解几种简单设计模式

学习资料:狂神说Java
设计模式

一. 创建型模式

省去new,帮我们更好的创建对象

1. 单例模式
  • 构造函数私有
  • 不要new对象啦

饿汉式,DCL懒汉式,深究!

饿汉式:

//饿汉式单例
public class Hungry {
    //问题,可能浪费空间
    private byte[] data1 = new byte[1 * 1024 * 1024];
    private byte[] data2 = new byte[1 * 1024 * 1024];
    private byte[] data3 = new byte[1 * 1024 * 1024];
    private byte[] data4 = new byte[1 * 1024 * 1024];

    //构造函数私有:别人就不能new了
    private Hungry() {

    }

    //一上来就new,加载
    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}

问题:很可能会浪费内存

懒汉式:(双重检测锁模式懒汉式单例)

//懒汉式单例
public class LazyMan {
    private volatile static LazyMan lazyMan;//避免指令重排

    //构造器私有
    private LazyMan() {
        System.out.println(Thread.currentThread().getName() + "Ok");
    }

    //双重检测锁模式的懒汉式单例,DCL懒汉式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();// 不是原子性操作
                    /**
                     * 1. 分配内存空间
                     * 2. 执行构造方法,初始化对象
                     * 3. 把这个对象指向这个空间
                     *
                     * 可能指令重排的现象
                     * 原来:123
                     * 如果:132 A线程没问题
                     *           B //此时lazyMan还没有完成构造
                     */
                }
            }
        }
        return lazyMan;
    }

    //单线程ok,多线程并发下
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan.getInstance();
            }).start();
        }
    }
}

静态内部类:

package 设计模式.单例模式;
//静态内部类
//不安全有反射这个东西
public class Holder {
    private Holder(){
    }

    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

反射破坏单例:

public static void main(String[] args) throws Exception {
        LazyMan lazyMan1 = new LazyMan();
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);//无视private
        LazyMan lazyMan2 = constructor.newInstance();

        System.out.println(lazyMan1==lazyMan2);
    }
}

深入浅出了解几种简单设计模式_第1张图片

解决:最终解决,源码中枚举类型不能被反射破坏

在这里插入图片描述
测试枚举:

import java.lang.reflect.Constructor;

//枚举 是什么?本身也是一个类
public enum EnumSingle {
    INSTANCE;//默认单例

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle enumSingle1 = EnumSingle.INSTANCE;

        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        EnumSingle enumSingle2 = constructor.newInstance();

        System.out.println(enumSingle1==enumSingle2);
    }
}

编译器骗了我们枚举没有无参构造

在这里插入图片描述

枚举没有无参构造的,有有参构造(String,int)

在这里插入图片描述

2. 工厂模式
  • 实现了创建者和调用者分离

  • 分类

    • 简单工厂模式

      • 用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
        深入浅出了解几种简单设计模式_第2张图片
      • 车接口
      package factory.simple;
      
      public interface Car {
          public void name();
      }
      
      • 各种车
      public class TesLa implements Car {
          @Override
          public void name() {
              System.out.println("特斯拉!");
          }
      }
      ---------------------------------------
      package edu.xalead.factory.simple;
      
      public class WuLin implements Car {
          @Override
          public void name() {
              System.out.println("五菱宏光!");
          }
      }
      
      • 买车的消费者
      package factory.simple;
      //弊端: 增加一个新的产品,需要修改原来的代码(为了满足开闭原则)
      public class Consumer {
          public static void main(String[] args) {
              //之前
      //        Car wuLin = new WuLin();
      //        Car tesLa = new TesLa();
              //使用工厂创建
              Car wuLin = CarFactory.getCar("五菱");
              Car tesLa = CarFactory.getCar("特斯拉");
      
              wuLin.name();
              tesLa.name();
          }
      }
      
      • 创建一个车工厂: 但如果来一个别的车的话需要修改代码
      package factory.simple;
      
      public class CarFactory {
          public static Car getCar(String car){
              if(car.equals("五菱")){
                  return new WuLin();
              }else if (car.equals("特斯拉")){
                  return new TesLa();
              }else
                  return null;
          }
      }
      
    • 工厂方法模式

      • 用来生产同一等级中的固定产品(支持增加任意产品)
        深入浅出了解几种简单设计模式_第3张图片
      • 车接口
      package edu.xalead.factory.method;
      
      public interface Car {
          public void name();
      }
      
      • 车接口工厂,谁来生产车不用管
      package edu.xalead.factory.method;
      
      public interface CarFactory {
          Car getCar();
      }
      
      • 各种车
      package edu.xalead.factory.method;
      
      public class TesLa implements Car {
          @Override
          public void name() {
              System.out.println("特斯拉!");
          }
      }
      
      对应车接口:
      package edu.xalead.factory.method;
      
      public class TesLaFactory implements CarFactory {
          @Override
          public Car getCar() {
              return new TesLa();
          }
      }
      --------------------------------------------------------
      package edu.xalead.factory.method;
      
      public class WuLin implements Car {
          @Override
          public void name() {
              System.out.println("五菱宏光!");
          }
      }
      对应车接口:
      package edu.xalead.factory.method;
      
      public class WuLinFactory implements CarFactory {
          @Override
          public Car getCar() {
              return new WuLin();
          }
      }
      
      • 消费者
      package edu.xalead.factory.method;
      
      public class Consumer {
          public static void main(String[] args) {
              Car car1 = new WuLinFactory().getCar();
              Car car2 = new TesLaFactory().getCar();
      
              car1.name();
              car2.name();
          }
      }
      
    • 抽象工厂模式

      • 围绕一个超级工厂创建其它工厂。该超级工厂又被称为其它工厂的工厂

深入浅出了解几种简单设计模式_第4张图片
- 大工厂

 package edu.xalead.factory.abstract1;
 //抽象产品工厂
 public interface IProductFactory {
     //生产手机
     IphoneProduct getPhone();
     //生产路由器
     IRouterProduct getRouter();
 }
- 华为工厂,总工厂已经确定需要生产哪些产品了
 package edu.xalead.factory.abstract1;
 
 public class HuaweiFactory implements IProductFactory {
     @Override
     public IphoneProduct getPhone() {
         return new HuaweiPhone();
     }
 
     @Override
     public IRouterProduct getRouter() {
         return new HuaweiRouter();
     }
 }
- 华为产品
 //手机
 package edu.xalead.factory.abstract1;
 
 public class HuaweiPhone implements IphoneProduct {
     @Override
     public void start() {
         System.out.println("开启华为手机");
     }
 
     @Override
     public void shutdown() {
         System.out.println("关闭华为手机");
     }
 
     @Override
     public void callup() {
         System.out.println("华为手机打电话");
     }
 
     @Override
     public void sendSMS() {
         System.out.println("华为发短信");
     }
 }
 -----------------------------------------------
 //路由器
 package edu.xalead.factory.abstract1;
 
 public class HuaweiRouter implements IRouterProduct {
     @Override
     public void start() {
         System.out.println("启动华为路由器");
     }
 
     @Override
     public void shutdown() {
         System.out.println("关闭华为路由器");
     }
 
     @Override
     public void setting() {
         System.out.println("设置华为路由器");
     }
 }
- 小米工厂
 package edu.xalead.factory.abstract1;
 
 public class XiaomiFactory implements IProductFactory {
     @Override
     public IphoneProduct getPhone() {
         return new XiaomiPhone();
     }
 
     @Override
     public IRouterProduct getRouter() {
         return new XiaomiRouter();
     }
 }
- 小米产品
 //手机
 package edu.xalead.factory.abstract1;
 
 public class XiaomiPhone implements IphoneProduct {
     @Override
     public void start() {
         System.out.println("开启小米手机");
     }
 
     @Override
     public void shutdown() {
         System.out.println("关闭小米手机");
     }
 
     @Override
     public void callup() {
         System.out.println("小米打电话");
     }
 
     @Override
     public void sendSMS() {
         System.out.println("小米发短信");
     }
 }
 //路由器
 package edu.xalead.factory.abstract1;
 
 public class XiaomiRouter implements IRouterProduct {
     @Override
     public void start() {
         System.out.println("启动小米路由器");
     }
 
     @Override
     public void shutdown() {
         System.out.println("关闭小米路由器");
     }
 
     @Override
     public void setting() {
         System.out.println("设置小米路由器");
     }
 }
 //产品接口
 package edu.xalead.factory.abstract1;
 //手机接口
 public interface IphoneProduct {
     void start();
     void shutdown();
     void callup();
     void sendSMS();
 }
 ------------------------------------
 package edu.xalead.factory.abstract1;
 //路由器接口
 public interface IRouterProduct {
     void start();
     void shutdown();
     void setting();
 }
- 客户
 package edu.xalead.factory.abstract1;
 
 public class Client {
     public static void main(String[] args) {
         //小米工厂
         XiaomiFactory xiaomiFactory = new XiaomiFactory();
         //手机
         IphoneProduct phone = xiaomiFactory.getPhone();
         phone.start();
         phone.callup();
         //路由器
         IRouterProduct router = xiaomiFactory.getRouter();
         router.start();
         router.setting();
 
         HuaweiFactory huaweiFactory = new HuaweiFactory();
         IphoneProduct phone1 = huaweiFactory.getPhone();
         phone.start();
         phone.callup();
 
         IRouterProduct router1 = huaweiFactory.getRouter();
         router1.start();
         router1.setting();
     }
 }
  • 满足oop七大原则

    • 开闭原则:一个软件实体应该对扩展开放,对修改关闭
    • 依赖倒转原则:要针对接口编程,不要针对实现编程
    • 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
  • 核心本质

    • 实例化对象不使用new,用工厂方法代替
    • 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦
  • 应用场景

    • JDK中Calendar的getInstance方法
    • JDBC中的Connection对象的获取
    • Spring中IOC容器创建管理bean对象
    • 反射中Class对象的newInstance方法
3. 建造者模式

提供了一种创建对象的最佳方式

  • 定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建 过程可以创建不同的表示。

  • 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

  • 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

  • 例子:

    • 工厂(建造者模式) :负责制造汽车(组装过程和细节在工厂内)
    • 汽车购买者(用户) :你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用.(不需要知道汽车是怎么组装的(车轮、 车门、发动机、方向盘等等) )
      深入浅出了解几种简单设计模式_第5张图片
  • 代码实现

    建房子的例子,必须要有个指挥

    • 抽象的Builder

      package edu.xalead.builder.demo1;
      //抽象的建造者:方法
      public abstract class Builder {
      
          abstract void builderA();//地基
          abstract void builderB();//钢筋框架
          abstract void builderC();//铺电线
          abstract void builderD();//粉刷
      
          // 经过ABCD四步,就完工了
          abstract Product getProduct();
      }
      
    • 产品

      package edu.xalead.builder.demo1;
      
      import lombok.Data;
      import lombok.ToString;
      
      //产品:房子
      @Data
      @ToString
      public class Product {
          private String buildA;
          private String buildB;
          private String buildC;
          private String buildD;
      }
      
    • 具体Builder

      package edu.xalead.builder.demo1;
      //具体的建造者:工人
      public class Worker extends Builder {
      
          private Product product;
          //工人创建产品
          public Worker(){
              product = new Product();
          }
      
          @Override
          void builderA() {
              product.setBuildA("地基");
              System.out.println("地基");
          }
      
          @Override
          void builderB() {
              product.setBuildB("钢筋框架");
              System.out.println("钢筋框架");
          }
      
          @Override
          void builderC() {
              product.setBuildC("铺设电线");
              System.out.println("铺设电线");
          }
      
          @Override
          void builderD() {
              product.setBuildD("粉刷");
              System.out.println("粉刷");
          }
      
          @Override
          Product getProduct() {
              return product;
          }
      
      }
      
    • 指挥

      package edu.xalead.builder.demo1;
      //指挥:核心,指挥一个工程的构建
      public class Director {
          //指挥工人按照顺序建造房子
          public Product build(Builder builder){
              builder.builderA();
              builder.builderB();
              builder.builderC();
              builder.builderD();
      
              return builder.getProduct();
          }
      }
      
    • 测试:

      package edu.xalead.builder.demo1;
      
      public class TestBuild {
          public static void main(String[] args) {
              //指挥
              Director director = new Director();
              //指挥具体的工人
              Product build = director.build(new Worker());
              System.out.println(build.toString());
          }
      }
      

    买东西,我们用户可以选除了套餐外的其它搭配

    • 抽象Builder

      package edu.xalead.builder.demo2;
      
      
      //建造者
      public abstract class Builder {
          abstract Builder builderA(String msg);//汉堡
          abstract Builder builderB(String msg);//可乐
          abstract Builder builderC(String msg);//薯条
          abstract Builder builderD(String msg);//甜品
      
          // 经过ABCD四步,套餐完成
          abstract Product getProduct();
      }
      
    • 产品

      package edu.xalead.builder.demo2;
      
      import lombok.Data;
      import lombok.ToString;
      
      //产品:套餐
      @Data
      @ToString
      public class Product {
          private String BuildA = "汉堡";
          private String BuildB = "可乐";
          private String BuildC = "薯条";
          private String BuildD = "甜品";
      }
      
    • 具体Builder

      package edu.xalead.builder.demo2;
      //具体的建造者
      public class Worker extends Builder {
          private Product product;
          public Worker(){
              product = new Product();
          }
      
          @Override
          Builder builderA(String msg) {
              product.setBuildA(msg);
              return this;
          }
      
          @Override
          Builder builderB(String msg) {
              product.setBuildB(msg);
              return this;
          }
      
          @Override
          Builder builderC(String msg) {
              product.setBuildC(msg);
              return this;
          }
      
          @Override
          Builder builderD(String msg) {
              product.setBuildD(msg);
              return this;
          }
      
          @Override
          Product getProduct() {
              return product;
          }
      }
      
    • 测试

      package edu.xalead.builder.demo2;
      
      public class Test {
          public static void main(String[] args) {
              //服务员
              Worker worker = new Worker();
              //链式编程:在原来基础上可以自由组合了,如果不组合就用默认的
              Product product = worker.builderA("全家桶")
                              .builderB("雪碧")
                              .builderC("薯片")
                              .getProduct();
              System.out.println(product.toString());
          }
      }
      
  • 优点:

    • 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
    • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
    • 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的
      代码,符合“开闭原则"。
  • 缺点:

    • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
    • 如果产品的内部变化复杂, 可能会导致需要定 义很多具体建造者类来实现这种变化, 导致系统变得很庞大。
  • 应用场景:

    • 需要生成的产品对象有复 杂的内部结构,这些产品对象具备共性;
    • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
    • 适合于- -个具有较多的零件(属性)的产品(对象)的创建过程。
  • 建造者与抽象工厂模式的比较:

    • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
    • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象, 返回-个完整的对象。
    • 如果将抽象工厂模式看成汽车配件生产工厂,生产-一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!
4. 原型模式

主要是克隆:

clone()

  1. 实现cloneable接口
    clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法

  2. 重写clone()方法

    clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。

分浅拷贝和深拷贝:

  • 浅拷贝:

    拷贝对象和原始对象的引用类型引用同一个对象

  • 深拷贝:

    拷贝对象和原始对象的引用类型引用不同对象。

深入浅出了解几种简单设计模式_第6张图片
浅拷贝例子:

package edu.xalead.prototype.demo1;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

/**
 * 1. 实现一个接口
 * 2. 重写一个方法
 */
//Video
@Data
@NoArgsConstructor
@ToString
public class Video implements Cloneable{
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }
}

客户端:

package edu.xalead.prototype.demo1;

import java.util.Date;

// 客户端: 克隆
public class Client {
    // 浅拷贝,公用同一个数据date
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Date date = new Date();
        Video v1 = new Video("网站",date);
        System.out.println("v1=>"+v1);
        System.out.println("v1=>hash:"+v1.hashCode());

        //v1 克隆出 v2
        Video v2 = (Video) v1.clone();
        System.out.println("v2=>"+v2);
        System.out.println("v2=>hash:"+v2.hashCode());

        v2.setName("克隆网站");
        System.out.println(v2);
    }
}

深拷贝例子:

package edu.xalead.prototype.demo2;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

/**
 * 1. 实现一个接口
 * 2. 重写一个方法
 */
//Video
@Data
@NoArgsConstructor
@ToString
public class Video implements Cloneable{
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object clone = super.clone();
        //实现深克隆
        Video v = (Video) clone;
        //将这个对象的属性也克隆一份
        v.createTime = (Date) this.createTime.clone();

        return v;
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }
}

客户端:

package edu.xalead.prototype.demo2;

import java.util.Date;
// 客户端: 克隆

/**
 * Spring Bean :单例模式,原型模式创建
 *
 * 原型模式 + 工厂 ==》 new <=> 原型模式
 */
public class Client {
    // 深拷贝
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Date date = new Date();
        Video v1 = new Video("网站",date);
        Video v2 = (Video) v1.clone();

        System.out.println("v1=>"+v1);
        System.out.println("v2=>"+v2);
        //
        date.setTime(152645789);
        System.out.println("--------------------------");
        System.out.println("v1=>"+v1);
        System.out.println("v2=>"+v2);


    }
}

二. 结构型模式

作用: 从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题

1. 适配器模式
  • 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能- -起工作的
    那些类可以在一起工作!

  • 角色分析

    • 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。
    • 需要适配的类:需要适配的类或适配者类。
    • 适配器:通过包装一一个需要适配的对象, 把原接口转换成目标对象!
      深入浅出了解几种简单设计模式_第7张图片
  • 对象适配器优点
    ◆一个对象适配器可以把多个不同的适配者适配到同-一个目标
    ◆可以适配一-个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者
    的子类也可通过该适配器进行适配。

  • 类适配器缺点

    • 对于Java、 C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;

    • 在Java、 C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

  • 适用场景

    • 系统需要使用一-些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
    • 想创建一一个可以重复使用的类,用于与- -些彼此之间没有太大关联的一些类,包括- -些可能在将来引进的类一起作。
  • 代码

当我们需要用网线上网的时候,但电脑是薄的无法插网线

网线:

package edu.xalead.adapter;
//要被适配的类:比如网线
public class Adaptered {
    public void request(){
        System.out.println("网线上网");
    }
}

适配器:

//抽象接口
package edu.xalead.adapter;
//转换器的抽象实现
public interface NetToUsb {
    //作用处理请求,把网线插到适配器上适配器插到usb接口
    void handleRequest();
}
------------------------------------
package edu.xalead.adapter;

/**
 * 1. 继承(类适配器,单继承)
 */
//真正的适配器,需要连接USB,连接网线
public class Adapter extends Adaptered implements NetToUsb {
    @Override
    public void handleRequest() {
        super.request();//可以上网了
    }
}
-------------------------------------
package edu.xalead.adapter;

/**
 * 2. 组合(对象适配器,常用)
 */
//真正的适配器,需要连接USB,连接网线
public class Adapter2 implements NetToUsb {
    private Adaptered adaptered;//网线组合进来了

    public Adapter2(Adaptered adaptered) {
        this.adaptered = adaptered;
    }

    @Override
    public void handleRequest() {
        adaptered.request();//可以上网了
    }
}

电脑连网:

package edu.xalead.adapter;
//客户端类:想上网,插不上网线
public class Computer {

    public void net(NetToUsb adapter){
        //上网但插不上网线,找一个转接头
        adapter.handleRequest();
    }

    public static void main(String[] args) {
        //电脑,适配器,网线
        Computer computer = new Computer();
        Adaptered adaptered = new Adaptered();//网线
//        Adapter adapter = new Adapter();
        Adapter2 adapter = new Adapter2(adaptered);//

        computer.net(adapter);
    }
}
2. 桥接模式

有两个以上变化的维度:比如联想,苹果,戴尔,他们都有台式机,笔记本,平板

深入浅出了解几种简单设计模式_第8张图片

  • 好处分析:

    • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本

    • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!

  • 劣势分析:

    • 桥接模式的引入会增加系 统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
    • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一 定的局限性。
  • 最佳实践:

    • 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建
      立静态的继承联系,通过桥接模式可以使它们在抽象层建立-个关联关系。抽象化角色和实现化角色
      可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将-个抽象化子类的对象和一一个实现
      化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
    • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
    • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需
      要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,
      桥接模式尤为适用。
  • 场景:

    • Java语言通过Java虚拟机实现了平台的无关性。

    深入浅出了解几种简单设计模式_第9张图片

    • AWT中的Peer架构

    • JDBC驱动程序也是桥接模式的应用之一。

思考?桥接模式+适配器模式

3. 代理模式

SpringAOP底层

分类:

  • 静态代理
  • 动态代理

深入浅出了解几种简单设计模式_第10张图片

3.1 静态代理

角色分析:

  • 抽象角色:一般会用接口活着抽象类解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色的同时,还可以做一些附属的操作
  • 客户:访问代理对象的人!

上代码:

  • 抽象角色:

    package edu.xalead.proxy.demo1;
    //抽象的接口,出租房子这件事
    public interface Rent {
        void rent();
    }
    
  • 真实角色:

    package edu.xalead.proxy.demo1;
    //房东,真实角色
    public class Hoster implements Rent {
        @Override
        public void rent() {
            System.out.println("本房东有房子出租");
        }
    }
    
  • 代理角色:

    package edu.xalead.proxy.demo1;
    
    public class ProxyRent implements Rent {
        private Hoster hoster;
    
        public ProxyRent(Hoster hoster) {
            this.hoster = hoster;
        }
    
        @Override
        public void rent() {
            seeHouse();
            hoster.rent();
            hetong();
        }
    
        //还可以做一些其它的事
        public void seeHouse(){
            System.out.println("中介带你看房子");
        }
    
        public void hetong(){
            System.out.println("签合同");
        }
    }
    
  • 客户:

    package edu.xalead.proxy.demo1;
    //客户来租房了
    public class Client {
        public static void main(String[] args) {
            //房东出租房子
            Hoster hoster = new Hoster();
            //代理角色,中介帮房东租房子,但是呢,代理角色还有些附属操作
            ProxyRent proxyRent = new ProxyRent(hoster);
            //你看不到房东,直接找中介租房子
            proxyRent.rent();
        }
    }
    

代理模式的好处: .

  • 可以使真实角色的操作更加纯粹!不用去关注- -些公共的业务

  • 公共也就就交给代理角色!实现了业务的分工!

  • 公共业务发生扩展的时候,方便集中管理!

缺点:

  • 一个真实角色就会产生-个代理角色;代码量会翻倍.开发效率会变低~
3.2 动态代理
  • 动态代理与静态代理角色一样
  • 动态代理是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口的—JDK动态代理
    • 基于类的----cglib
    • Java字节码实现----javassist

我们在这里使用基于接口的动态代理

我们在这里了解两个类:

  • Proxy: 生成动态代理实例
  • InovactionHandler: 调用处理程序并返回一个结果

上代码:

  • 抽象角色:
package edu.xalead.proxy.demo4active;

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void del() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询出一个用户");
    }
}
  • 真实角色:
package edu.xalead.proxy.demo4active;

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void del() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询出一个用户");
    }
}
  • 代理角色:动态生成的,通用的,代理的是接口
package edu.xalead.proxy.demo4active;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }
    //Proxy,生成代理类
    public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    //InvocationHandler
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质就是利用反射实现
        Object result = method.invoke(target, args);
        return result;
    }
}
  • 客户端:
package edu.xalead.proxy.demo4active;

public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //生成代理角色
        ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler();
        invocationHandler.setTarget(userService);

        UserService proxy = (UserService)invocationHandler.getProxy();//动态生成代理类
        proxy.add();
    }
}

好处,一个动态代理类,可以代理多个类,只要是实现了同一个接口即可。

后续会继续更新的,
欢迎访问我的个人博客交流:http://www.ayjup.cn

你可能感兴趣的:(设计模式)