常用设计模式

常用的几个设计模式

设计模式三大类

参考文章:https://blog.csdn.net/gududedabai/article/details/81989196

创建性模式:

单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

结构型模式:

适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式

行为型模式:

模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、接解释器模式、状态模式、策略模式、职责链模式、访问者模式。

OOP七大法则

开闭原则:对扩展开放,对修改关闭。

里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立。

依赖倒置原则:要面向接口编程,不要面向实现编程。

单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。

接口隔离原则:要为各个类建立他们需要专用接口。

迪米特法则:只与你的朋友直接交谈,不跟陌生人说话。

合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

单例模式

单例模式便是创建型设计模式的一种,它确保某一个类在系统中只有一个实例,并自行实例化,同时向外部提供获取这个唯一实例的接口。

  • 只有一个实例
  • 必须实例化自己
  • 向外提供实例

恶汉式

/**
 * 下面是饿汉式单例模式的标准代码,
 * EagerSingleton类的实例因为变量instance申明为static的关系,
 * 在类加载过程中便会执行。由此带来的好处是Java的类加载机制本身为我们保证了实例化过程的线程安全性,
 * 缺点是这种空间换时间的方式,即使类实例本身还未用到,实例也会被创建。
 * */
class EagerSingleton {
    // 静态变量,类在创建之初就会执行实例化动作。
    private static EagerSingleton instance = new EagerSingleton();
    // 私有化构造函数,使外界无法创建实例
    private EagerSingleton(){}
    // 为外界提供获取实例接口
    public static EagerSingleton getInstance(){
        return instance;
    }
}

懒汉式

/**
 * 懒汉式
 * 这个缺点的原因,涉及到并发编程的原子性。
 * 实例中,创建实例的代码逻辑失去了原子性从而导致可能存在多个实例创建的情况。
 * */
public class LazySingleton {
    private static LazySingleton instance = null;
    private LazySingleton(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    // 为外界提供获取实例接口
    public static LazySingleton getInstance(){
        if(instance == null){
            instance = new LazySingleton(); // 懒加载
        }
        return instance;
    }

    /**
     * 多线程并发
     * 发生了多次实例化对象
     * */
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazySingleton.getInstance();
            }).start();
        }
    }

}

懒汉式加锁

/**
 * 双重检测锁
 * */
class Singleton2 {

    //volatile 原子性操作
    private volatile static Singleton2 instance = null;

    /**
     * 设置标志位
     * */
    private static boolean youyuan = false;

    private Singleton2(){
        synchronized (Singleton2.class){
            if (youyuan == false){
                youyuan = true;
            }else {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
        System.out.println(Thread.currentThread().getName()+"ok");
    }

    public static Singleton2 getInstance(){
        //先检查实例是否存在,如果不存在才进入下面的同步块
        if(instance == null){
            //同步块,线程安全的创建实例
            synchronized (Singleton2.class) {
                //再次检查实例是否存在,如果不存在才真正的创建实例
                if(instance == null){
                    instance = new Singleton2();//不是一个原子性操作
                }
            }
        }
        return instance;
    }

    /**
     * 线程安全
     * */
//    public static void main(String[] args) {
//        for (int i = 0; i < 10; i++) {
//            new Thread(()->{
//                Singleton2.getInstance();
//            }).start();
//        }
//    }

	//可以通过反射破坏
    public static void main(String[] args) throws Exception{
        //Singleton2 instance = Singleton2.getInstance();
        Constructor<Singleton2> declaredConstructor = Singleton2.class.getDeclaredConstructor();
        Field youyuan = Singleton2.class.getDeclaredField("youyuan");
        youyuan.setAccessible(true);
        declaredConstructor.setAccessible(true);

        Singleton2 singleton2 = declaredConstructor.newInstance();
        //修改字段
        youyuan.set(singleton2,false);
        Singleton2 singleton21 = declaredConstructor.newInstance();
        System.out.println(singleton21);
        System.out.println(singleton2);
    }

}

工厂模式

实现了创建者和调用者的分离

简单工厂模式

用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)

image-20210506193612390

如图, 消费者不需要关心不同的车辆生产商, 只需要面对汽车工厂.

无论是需要生产五菱或者特斯拉, 只需要在汽车工厂(工厂类)中指定类型, 就可以生产出不同的汽车(java对象)

  • car接口
public interface Car {
    public void carName();
}
  • Car实现类
public class Wuling implements Car {
    @Override
    public void carName() {
        System.out.println("Wuling");
    }
}

public class Tesla implements Car {
    @Override
    public void carName() {
        System.out.println("Tesla");
    }
}
  • 静态工厂
public class CarFactory {

     public static Car getCar(String name){
        if (name.equals("Tesla")){
            return new Tesla();
        }else if (name.equals("Wuling")){
            return new Wuling();
        }else{
            return null;
        }
    }
}

测试

public class Main {
    public static void main(String[] args) {
//        Wuling wuling = new Wuling();
//        wuling.carName();
        Car wuling = CarFactory.getCar("Wuling");
        wuling.carName();
    }
}

抽象工厂模式

围绕一个超级工厂创建其他工厂, 该超级工厂又称为其他工厂的工厂。

抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类。

使用场景:

  • 客户端不依赖产品类实例如何被创建、实现等细节

  • 强调一系列相关的产品对象(属于同一产品族),一起使用创建对象需要大量的重复代码。

  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现。

优点:

  • 具体产品在应用层的代码隔离,无需关心创建的细节。
  • 将一个系列的产品统一到一起创建。

使用案例:

  1. JDBC中Connection对象的获取
  2. Spring中IOC容器创建管理bean对象
  3. 反射Class对象的newInstance方法

核心本质:

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

建造者模式

建造者模式也属于创建者模式,他提供了一种创建对象的最佳方式

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

主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的创建过程和细节隐藏起来)。

建造者模式

image-20210506211141739

  • 指挥者
// 指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
public class Director {
    //指挥工人按照顺序建房子
    public Product build(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        return builder.build();
    }
}
  • 构造者Builder
//抽象的建造者:方法
public abstract class Builder {
    abstract void buildA();  //地基
    abstract void buildB();  //钢筋水泥
    abstract void buildC();  //铺电线

    //完工:得到产品
    abstract Product getProduct();
}

//具体的建造者:工人
public class Worker extends Builder {

    private Product product;

    public Worker() {
        this.product = new Product();
    }

    @Override
    void buildA() {
        product.setBuildA("地基");
        System.out.println("地基");
    }

    @Override
    void buildB() {
        product.setBuildA("钢筋水泥");
        System.out.println("钢筋水泥");
    }

    @Override
    void buildC() {
        product.setBuildA("铺电线");
        System.out.println("铺电线");
    }

    @Override
    Product build() {
        return product;
    }
}
  • 产品
package com.example.builder;

//产品:房子
public class Product {
    private String buildA;  //地基
    private String buildB;  //钢筋水泥
    private String buildC;  //铺电线

    public String getBuildA() {
        return buildA;
    }

    public void setBuildA(String buildA) {
        this.buildA = buildA;
    }

    public String getBuildB() {
        return buildB;
    }

    public void setBuildB(String buildB) {
        this.buildB = buildB;
    }

    public String getBuildC() {
        return buildC;
    }

    public void setBuildC(String buildC) {
        this.buildC = buildC;
    }

    @Override
    public String toString() {
        return "Product{" +
                "buildA='" + buildA + '\'' +
                ", buildB='" + buildB + '\'' +
                ", buildC='" + buildC + '\'' +
                '}';
    }
}

测试

public class Test {
    public static void main(String[] args) {
        //指挥
        Director director = new Director();
        //指挥 具体的工人完成 产品
        Product build = director.build(new Worker());
        System.out.println(build.toString());
    }
}

优点

对于对象的属性赋值方式,有两种方式:

// 方式1
Product product=new Product("地基","钢筋水泥","铺电线");

// 方式2
Product product=new Product();
animal.setBuildA("地基");
animal.setBuildB("钢筋水泥");
animal.setBuildC("铺电线");

第一种:加入参数的时候,不能明确的知道往这个对象里加入了什么属性的内容。

第二种:根据set函数名看到将要设置的值是什么值,但是这种写发,略显冗余。

构造者模式, 设计这种类时,会显得比较简洁清晰。

Product product = new Worker().setBuildA("地基").setBuildB("钢筋水泥").setBuildC("铺电线").build();

匿名模式

  • 建造者
//建造者
public abstract class Builder {
    abstract Builder builderA(String msg);  //汉堡
    abstract Builder builderB(String msg);  //可乐
    abstract Builder builderC(String msg);  //薯条

    abstract Product build();

}


public class Worker extends Builder {

    private Product product;

    public Worker( ) {
        this.product = new Product();
    }


    @Override
    Builder builderA(String msg) {
        product.setBuilderA(msg);
        return this;
    }

    @Override
    Builder builderB(String msg) {
        product.setBuilderB(msg);
        return this;
    }

    @Override
    Builder builderC(String msg) {
        product.setBuilderC(msg);
        return this;
    }

    @Override
    Product build() {
        return product;
    }
}
  • 产品
package org.javaboy.builder.demo2;

public class Product {

    private String builderA = "汉堡";
    private String builderB = "可乐";
    private String builderC = "薯条";

    public String getBuilderA() {
        return builderA;
    }

    public void setBuilderA(String builderA) {
        this.builderA = builderA;
    }

    public String getBuilderB() {
        return builderB;
    }

    public void setBuilderB(String builderB) {
        this.builderB = builderB;
    }

    public String getBuilderC() {
        return builderC;
    }

    public void setBuilderC(String builderC) {
        this.builderC = builderC;
    }
    
    @Override
    public String toString() {
        return "Product{" +
                "builderA='" + builderA + '\'' +
                ", builderB='" + builderB + '\'' +
                ", builderC='" + builderC + '\'' +
                '}';
    }
}

测试

 public class Test {
    public static void main(String[] args) {
        //服务员
        Worker worker = new Worker();
        //链式编程
        Product product = worker.builderA("冰淇淋").builderB("烤鸡腿")
                .build();
        System.out.println(product.toString());
    }
}

优缺点

优点:

  • 产品的建造和表示分离,实现了解耦。
  • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
  • 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合开闭原则。

缺点:

  • 创建的产品一般具有较多的共同点,其组成部分相似:如果产品之间差异性很大,不适用于建造者模式。

代理模式

静态代理

定义:给一个对象提供一个代理对象,并有代理对象控制对原对象的引用,类似生活中的中介(以房产中介为例)。

抽象角色:接口或者抽象类

  • 真实角色:被代理的角色(房产拥有者)

  • 代理角色:来代理真实角色,同时做一些附属的操作(房产中介)

  • 客户:访问代理对象的人(购房者)

代码实现

接口

//租房
public interface Rent {
    public void rent();
}

真实角色--房东

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

代理角色--中介

public class Proxy implements Rent{

    private Host host;

    public Proxy(){
        
    }

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        host.rent();
    }

    //看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    //签合同
    public void heTong(){
        System.out.println("签合同");
    }

    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

客户端访问代理角色--客户租房

public class Client {
    public static void main(String[] args) {
        //房东要租房子
        Host host = new Host();
        //代理,中介帮房东租房子
        Proxy proxy = new Proxy(host);
        //租房直接找中介
        proxy.rent();
    }
}

优缺点

优点:

  • 可以使真实的操作更加纯粹,不用去关注一些公共的业务。
  • 公共也就交给代理角色,实现了业务的分工。
  • 公共业务发生扩展的时候,方便集中管理。

缺点:

  • 一个真实的角色就会产生一个代理角色。代码量会翻倍,开发效率会变低

案例:AOP

image-20210506215545273

代码

public interface UserService {
    public void add();
    public void update();
    public void delete();
    public void query();
}

package org.javaboy.staticproxy.demo2;
 
//真实对象
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void query() {
        System.out.println("query");
    }
}

//代理对象
public class UserServiceProxy implements UserService {

    private UserServiceImpl userService;

    public UserServiceProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("日志");
        System.out.println("add");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void query() {
        System.out.println("query");
    }

    //日志方法
    public void log(String msg){
        System.out.println("使用了"+msg+"方法");
    }

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
}

测试

public class Client {
    public static void main(String[] args) {
        UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
        userServiceProxy.add();
    }
}

动态代理

与静态代理一样,区别在于动态代理的代理类是动态生成的,不是我们直接写好的

动态代理分为两大类:基于接口的动态代理,基于类的动态代理

  • 基于接口:JDK动态代理
  • 基于类:cglib

了解两个类:

  • Proxy:代理
  • InvocationHandler:调用处理程序。

租房实例

代码

//租房
public interface Rent {
    public void rent();
}

//房东
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

//用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现
        Object res = method.invoke(target, args);
        return res;
    }
}

//测试
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色:未创建
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        handler.setTarget(host);
        //这里的proxy就是动态生成的,并没有写(面向接口编程)
        Rent proxy = (Rent) handler.getProxy();
        proxy.rent();
    }
}

封装接口

//用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName());
        //动态代理的本质,就是使用反射机制实现
        Object res = method.invoke(target, args);
        return res;
    }

}

//测试
public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //设置代理对象
        handler.setTarget(userService);
        //动态生成代理类
        UserService proxy = (UserService) handler.getProxy();
        proxy.query();
    }
}

原型模式

原型模式使用原型实例指定创建对象的种类,并且通过拷贝原型对象创建新的对象。

实际上就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

原型模式角色

  • 抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定)

  • 具体原型(ConcretePrototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象,需要实现抽象原型角色所要求的接口。

  • 客户(Client)角色:使用原型对象的客户程序

image-20210506222450497

浅拷贝

实体类

/**
 * 1、实现一个接口 Cloneable
 * 2、重写一个方法
 * */
//video
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;
    }

    public Video() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

克隆

public static void main(String[] args) throws CloneNotSupportedException {
    //原型对象 v1
    Date date = new Date();
    Video v1 = new Video("youyuan", date);
    //v1 克隆 v2
    Video v2 = (Video) v1.clone();//克隆出来的对象和原来是一模一样的
    System.out.println("v1="+v1);
    System.out.println("v2="+v2);
    System.out.println("=========================");
    date.setTime(12435);
    System.out.println("v1="+v1);
    System.out.println("v2="+v2);
}

深克隆

  • 序列化与反序列化
  • 改造clone()方法

实例类

 /**
 * 1、实现一个接口 Cloneable
 * 2、重写一个方法
 * */
//video
public class Video implements Cloneable {
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        //实现深克隆~
        Video v = (Video) obj;
        v.createTime = ((Date) this.createTime.clone());
        return obj;
    }

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

    public Video() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

克隆

public static void main(String[] args) throws CloneNotSupportedException {
    //原型对象 v1
    Date date = new Date();
    Video v1 = new Video("youyuan", date);
    //v1 克隆 v2
    Video v2 = (Video) v1.clone();//克隆出来的对象和原来是一模一样的
    System.out.println("v1="+v1);
    System.out.println("v2="+v2);
    System.out.println("=========================");
    date.setTime(12435);
    System.out.println("v1="+v1);
    System.out.println("v2="+v2);
}

装饰者模式

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

使用场景

  1. 扩展一个类的功能或者给一个类添加附加职责
  2. 给一个对象动态的添加功能,或动态撤销功能。

实现

创建一个接口

public interface Shape {
   void draw();
}

创建实现接口的实体类

public class Rectangle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Shape: Rectangle");
   }
}
public class Rectangle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Shape: Rectangle");
   }
}

创建实现Shape接口的抽象装饰类

public abstract class ShapeDecorator implements Shape {
   protected Shape decoratedShape;
 
   public ShapeDecorator(Shape decoratedShape){
      this.decoratedShape = decoratedShape;
   }
 
   public void draw(){
      decoratedShape.draw();
   }  
}

创建扩展了ShapeDecorator类的实体装饰类

public class RedShapeDecorator extends ShapeDecorator {
 
   public RedShapeDecorator(Shape decoratedShape) {
      super(decoratedShape);     
   }
 
   @Override
   public void draw() {
      decoratedShape.draw();         
      setRedBorder(decoratedShape);
   }
 
   private void setRedBorder(Shape decoratedShape){
      System.out.println("Border Color: Red");
   }
}

测试

public class DecoratorPatternDemo {
   public static void main(String[] args) {
 
      Shape circle = new Circle();
      // 装饰Circle
      ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
      // 装饰Rectangle
      ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
      //Shape redCircle = new RedShapeDecorator(new Circle());
      //Shape redRectangle = new RedShapeDecorator(new Rectangle());
       
      System.out.println("Circle with normal border");
      circle.draw();
 
      System.out.println("\nCircle of red border");
      redCircle.draw();
 
      System.out.println("\nRectangle of red border");
      redRectangle.draw();
   }
}

结果

Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red

装饰者模式案例

  1. java的IO输入流,典型的装饰者模式

image-20210311125949936

  1. spark中rdd算子

image-20210506225215288

posted @ 2021-05-06 22:54  ryxiong728  阅读(128)  评论(0编辑  收藏  举报