菜鸟的java设计模式学习总结

说明

更新时间:2020/10/12 02:41,更新到了建造者模式

之前学的java的常见设计模式,一直都没有时间整理出来,本文现对这些常见的设计模式进行一次总结与记录,本文会持续更新,不断地扩充

注意:本文仅为记录学习轨迹,如有侵权,联系删除

一、单例模式

单例模式是比较常见的设计模式,在很多的主流框架中也有使用,之前在网上看单例模式的时候,网上很多都讲到了饿汉模式和饥汉模式,个人觉得去记这些概念没什么用,只是初始化的方式或者创建的时机不同,从而分为这两种模式,在学习的时候主要学习它的设计思想,而不是去学它的概念,单例模式就是单例模式,单例的初始化时机可以根据实际情况而定,仅此而已。

实现的方式也简单,首先随便创建一个类,注意该类的构造方法修改为私有,这样就无法从类外面创建该类的实例,保证该类只有一个实例,同时提供向外暴露一个接口,提供该类的唯一实例

/**
 * 单例模式之饿汉式:最常用的一种单例模式
 */
public class SingleObject {

    //创建SingleObject的一个对象
    private static  SingleObject instance = new SingleObject();

    //让构造函数为private,这样该类就不会被实例化
    private SingleObject(){}

    //获取唯一可用的对象
    public static SingleObject getInstance(){
        return instance;
    }


    public void showMessage(){
        System.out.println("hello world!");
    }

}

这样一个单例就完成了,当然这样有点简单,至于上面说到的两种模式,上面的代码是一种,下面还有一种

package demo;

/**
 * 单例模式之饿汉式:最常用的一种单例模式
 */
public class SingleObject {

    //创建SingleObject的一个对象
    private static  SingleObject instance;

    //让构造函数为private,这样该类就不会被实例化
    private SingleObject(){}

    //获取唯一可用的对象
    public static SingleObject getInstance(){
        instance =   new SingleObject();
        return instance;

    }


    public void showMessage(){
        System.out.println("hello world!");
    }

}

这两种的区别就是,实例化的时机不同,一种是初始化类的时候创建实例,一种是在返回实例的方法里面实例化。

使用的方式如下

在这里插入图片描述

这样就是实现了单例模式的创建,保证只有一个实例。

二、工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

实现的方式也简单,先创建一个接口

package ShapeInterface;

public interface Shape
{
    void draw();
}

这是一个表示形状接口,它的底下有几个实现类

package ShapeClass;

import ShapeInterface.Shape;

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}
package ShapeClass;

import ShapeInterface.Shape;

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

package ShapeClass;

import ShapeInterface.Shape;

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

这样接口以及实现类就创建完成了,下面就开始创建工厂,把对这些类的创建全部丢进这个工厂里面,之后需要什么类就直接从工厂中获取即可

public class ShapeFactory {

    //使用getShape方法获取形状类型的对象
    public Shape getShape(String shapeType){
        if(shapeType == null) return null;
        if(shapeType.equalsIgnoreCase("CIRCLE")) return new Circle();
        else if(shapeType.equalsIgnoreCase("RECTANGLE")) return new Rectangle();
        else if(shapeType.equalsIgnoreCase("SQUARE")) return new Square();

        return  null;
    }
}

如果需要创建Circle类,就只需要调用工厂类的getShape方法,参数传入字符串“CIRCLE”,当然Rectangle的创建也是如此
在这里插入图片描述
重点学习它的设计思想

三、代理模式

这个模式也是在很多主流框架中有应用到,也是一个比较常用的设计模式,这里重点讲动态代理,可以分为基于子类的动态代理和基于接口的动态代理,先简单讲一下代理模式可以做到什么,代理模式可以在不修改代码的情况下,实现对一个方法的增强,返回一个增强的方法。

(1)基于子类的动态代理

首先创建一个子类,就是一个普通的类

public class Producer {
    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money) {
        System.out.println("您好!这里是生产产家的销售,你买的产品已寄出,共花费"+money+"元!");

    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money) {
        System.out.println("您好,这里是生产产家的售后,您要咨询什么?费用"+money+"元!");
    }

}

下面新建一个代理类,实现对上面的子类的方法增强

public class MyProxy {

    /**
     * 基于子类的动态代理,增强方法后的IProducer的代理类
     * @return 返回一个代理后的增强的方法
     */
    public Producer proxy(){
        /**
         * 动态代理:
         *  特点:字节码随用随创建,随用随加载
         *  作用:不修改源码的基础上对方法增强
         *  分类:
         *      基于接口的动态代理
         *      基于子类的动态代理
         *  基于子类的动态代理:
         *      涉及的类:Enhancer
         *      提供者:第三方cglib库
         *  如何创建代理对象:
         *      使用Enhancer类中的create方法
         *  创建代理对象的要求:
         *      被代理类不能是最终类
         *  create方法的参数:
         *      Class:字节码
         *          它是用于指定被代理对象的字节码。
         *
         *      Callback:用于提供增强的代码
         *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         *          此接口的实现类都是谁用谁写。
         *          我们一般写的都是该接口的子接口实现类:MethodInterceptor
         */


        //将要被代理的目标类
        Producer producer = new Producer();

        Producer proxyProduce = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行北地阿里对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
             * @param methodProxy :当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("代理的方法对象 : "+method);
                System.out.print("代理的方法调用时所需的参数 :");
                for (int i = 0; i < args.length; i++) {
                    System.out.print("args["+i+"] = "+args[i]+"    ");
                }

                System.out.println();
                System.out.println();

                Object invoke = null;
                //判断是不是销售方法
                if("saleProduct".equals(method.getName())){
                    //抽取20%的代理费
                    invoke = method.invoke(producer, (float)args[0]*0.8f);
                }
                return invoke;
            }
        });
        return proxyProduce;
    }

}

这种代理模式,主要通过使用第三方类库Enhancer类中的create方法,实现对子类的saleProduct方法进行了增强,上面代码实现了对子类传进来的money进行手续费20%的收取,在不修改子类的源代码的情况下,实现对原方法的增强,并返回增强方法
在这里插入图片描述

(2)基于接口的动态代理

还是以上面的例子,先创建接口

/**
 * 假设这里是产品的生产产家
 *
 */
public interface IProducer {

    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money);

    /**
     * 售后
     * @param money
     */
    public void afterService(float money);
}

然后是该接口的实现类

public class Producer extends 基于子类的动态代理.producer.Producer implements IProducer {
    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money) {
        System.out.println("您好!这里是生产产家的销售,你买的产品已寄出,共花费"+money+"元!");

    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money) {
        System.out.println("您好,这里是生产产家的售后,您要咨询什么?费用"+money+"元!");
    }
}

下面创建一个代理类,通过该代理类实现对上面的接口里面的方法的增强

/**
 * 这里是一个代理商
 */
public class MyProxy {

    /**
     * 基于接口的动态代理,增强方法后的IProducer的代理类
     * @return 返回一个代理后的增强的方法
     */
    public IProducer proxy() {
        /**
         * (1)动态代理:
         *  特点:字节码随用随创建,随用随加载
         *  作用:不修改源码的基础上对方法增强
         *  分类:
         *      基于接口的动态代理
         *      基于子类的动态代理
         *
         *
         * (2)这里讲解基于接口的动态代理:
         *  基于接口的动态代理:
         *      涉及的类:Proxy
         *      提供者:JDK官方
         *  如何创建代理对象:
         *      使用Proxy类中的newProxyInstance方法
         *  创建代理对象的要求:
         *      被代理类最少实现一个接口,如果没有则不能使用
         *  newProxyInstance方法的参数:
         *      ClassLoader:类加载器(将被代理的目标类的类加载器)
         *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
         *      Class[]:字节码数组(将被代理的目标类的接口组)
         *          它是用于让代理对象和被代理对象有相同方法。固定写法。
         *      InvocationHandler:用于提供增强的代码
         *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         *          此接口的实现类都是谁用谁写。
         */



        //将要被代理的目标类
        Producer producer = new Producer();

        //将被代理的目标类的类加载器
        ClassLoader classLoader = producer.getClass().getClassLoader();

        //将被代理的目标类的类接口组
        Class<?>[] interfaces = producer.getClass().getInterfaces();


        /**
         * 作用:执行被代理对象的任何接口方法都会经过该方法
         * 方法参数的含义
         * @param 基于接口的动态代理.proxy   代理对象的引用
         * @param method  代理的方法对象
         * @param args    代理的方法调用时所需的参数
         * @return        和被代理对象方法有相同的返回值
         * @throws Throwable
         */
        IProducer proxyIProducer = (IProducer)Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理的方法对象 : "+method);
                System.out.print("代理的方法调用时所需的参数 :");
                for (int i = 0; i < args.length; i++) {
                    System.out.print("args["+i+"] = "+args[i]+"    ");
                }

                System.out.println();
                System.out.println();

                Object invoke = null;
                //判断是不是销售方法
                if("saleProduct".equals(method.getName())){
                    //抽取20%的代理费
                    invoke = method.invoke(producer, (float)args[0]*0.8f);
                }


                return invoke;
            }
        });

        return proxyIProducer;
    }

}

也是实现跟基于子类的动态代理同样的功能,即对saleProduct方法的手续费收取20%
在这里插入图片描述

四、适配者模式

一句话介绍适配者模式:作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另一接口

例子:电脑想要读取内存卡的信息,但是电脑的硬件接口无法直接读取内存卡,这个时候就可以用一个读卡器,这个读卡器就是一个适配器,这就是适配器模式的设计思想,下面用代码实现这个需求。

关于适配者模式的实现,就是不说这个设计模式,简单想一下就有解决方案,通过java的继承机制的接口实现就可以实现这个需求,下面根据不同的解决方案可以简单分为对象实例适配器和类适配器。

(1)对象实例适配器

首先创建一个类表示内存卡存储信息

public class MemoryCard {
    /**
     * 表示这张内存卡的内容
     * @return
     */
    public String content(){
        return "学习资料\n\t--日韩\n\t--欧美\n\t--国产\n\t--主播";
    }
}

然后一个电脑接口,该接口要读取内存卡信息,但是我们知道的电脑无法直接读取内存卡的

/**
 * @ClassName : Computer
 * @Description : 电脑
 * @Author : CJH
 * @Date: 2020-10-12 15:59
 */
public interface Computer {
    public String getContent();

    public void readContent();
}

这个时候就需要一个读卡器类,也就是适配器类,用来将内存卡装进读卡器,再将读卡器插到电脑就可以实现电脑读取内存卡信息了

/**
 * @ClassName : Adapter01
 * @Description : 对象适配器模式,适配器(读卡器)
 * @Author : CJH
 * @Date: 2020-10-12 15:56
 */
public class Adapter01 implements Computer{

    //采用对象适配器的模式,或者叫对象实例适配器,创建该实例
    MemoryCard memoryCard = new MemoryCard();


    public String getContent() {
        System.out.println("内存卡内容读取中...");
        String theContent = memoryCard.content();
        System.out.println("内存卡内容读取完成...");
        return theContent;
    }

    public void readContent() {
        String content = getContent();
        System.out.println("电脑正在输出读取到的内存卡信息...");
        System.out.println("电脑读取到的内容:\n" );
        System.out.println(content);
    }
}

注意,该适配器类实现了电脑接口,然后再里面实例化一个内存卡类,这样就实现了将内存卡装进读卡器,再将读卡器插到电脑读取信息,这种基于对象的实例进行的适配就是对象实例适配器,下面开始测试

/**
 * @ClassName : Test
 * @Description :
 * @Author : CJH
 * @Date: 2020-10-12 16:18
 */
public class Test {
    public static void main(String[] args) {
        Adapter01 adapter01 = new Adapter01();
        adapter01.readContent();
    }
}

在这里插入图片描述

(2)类适配器

还是上面的例子,除了适配器那里不同之外,其他的基本一样
首先创建一个类表示内存卡存储信息

public class MemoryCard {
    /**
     * 表示这张内存卡的内容
     * @return
     */
    public String content(){
        return "学习资料\n\t--日韩\n\t--欧美\n\t--国产\n\t--主播";
    }
}

然后一个电脑接口,该接口要读取内存卡信息,但是我们知道的电脑无法直接读取内存卡的

/**
 * @ClassName : Computer
 * @Description : 电脑
 * @Author : CJH
 * @Date: 2020-10-12 15:59
 */
public interface Computer {
    public String getContent();

    public void readContent();
}

这个时候就需要一个读卡器类,也就是适配器类,用来将内存卡装进读卡器,再将读卡器插到电脑就可以实现电脑读取内存卡信息了,下面的实现的方式就跟上面的不一样了

/**
 * @ClassName : Adapter01
 * @Description : 对象适配器模式,适配器(读卡器)
 * @Author : CJH
 * @Date: 2020-10-12 15:56
 */
public class Adapter02 extends MemoryCard implements Computer{


    public String getContent() {
        System.out.println("内存卡内容读取中...");
        String theContent = super.content();
        System.out.println("内存卡内容读取完成...");
        return theContent;
    }

    public void readContent() {
        String content = getContent();
        System.out.println("电脑正在输出读取到的内存卡信息...");
        System.out.println("电脑读取到的内容:\n" );
        System.out.println(content);
    }
}

可以看到上面并没有实例化任何对象,而是通过继承内存卡类,同时实现了电脑接口,这样也可以实现上面的需求,下面开始测试

/**
 * @ClassName : Test
 * @Description :
 * @Author : CJH
 * @Date: 2020-10-12 16:18
 */
public class Test {
    public static void main(String[] args) {
        Adapter02 adapter02 = new Adapter02();
        adapter02.readContent();
    }
}

在这里插入图片描述

五、建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

注意:这个一般适用于复杂对象的创建上。

下面以一个房子的创建为例,虽然这个对象并不复杂,而且也没必要用建造者模式,但是用来演示建造者模式的话还是很合适的。
首先创建一个房屋类

/**
 * @ClassName : House
 * @Description : 房子
 * @Author : CJH
 * @Date: 2020-10-12 17:55
 */

public class House {
    private String ground;//地基
    private String groundBeam;//地梁
    private String fixture;//钢筋
    private String cement;//水泥
    private String roof;//屋顶


    public String getGround() {
        return ground;
    }

    @Override
    public String toString() {
        return "House{" +
                "ground='" + ground + '\'' +
                ", groundBeam='" + groundBeam + '\'' +
                ", fixture='" + fixture + '\'' +
                ", cement='" + cement + '\'' +
                ", roof='" + roof + '\'' +
                '}';
    }

    public void setGround(String ground) {
        this.ground = ground;
    }

    public String getGroundBeam() {
        return groundBeam;
    }

    public void setGroundBeam(String groundBeam) {
        this.groundBeam = groundBeam;
    }

    public String getFixture() {
        return fixture;
    }

    public void setFixture(String fixture) {
        this.fixture = fixture;
    }

    public String getCement() {
        return cement;
    }

    public void setCement(String cement) {
        this.cement = cement;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }
}

要是实例化该类的话很简单,只要new一下就可以了,那建造者模式怎么玩呢!!!建造者模式它把一个复杂的类的实例化分成了很多的一个一个小的对象实例化,将这些小的对象实例化组成一个复杂的对象实例化,下面用建造者模式实例化上面的这个房屋类
创建一个建造者接口,并且实现该接口

/**
 * @ClassName : Builder01
 * @Description : 建造者
 * @Author : CJH
 * @Date: 2020-10-12 17:55
 */
public interface IBuilder {
    public void buildGround();//打地基
    public void buildGroundBeam();//地梁施工
    public void buildFixture();//钢筋施工
    public void buildCement();//水泥施工
    public void buildRoof();//屋顶施工


    public House getHouse();//房屋施工结束,进行房屋交付
}

实现类

/**
 * @ClassName : DefaultBuilder
 * @Description : 默认建造者实现类
 * @Author : CJH
 * @Date: 2020-10-12 18:11
 */
public class DefaultBuilder implements IBuilder {
    /**
     * 在这样的建造者类里面可以根据需要自己创建该类
     */
    House house;

    //先把要构建的房屋类创建出来,之后再具体建造即可
    public DefaultBuilder() {
        this.house = new House();
    }


    @Override
    public void buildGround() {
        house.setGround("打地基完成");
    }

    @Override
    public void buildGroundBeam() {
        house.setGroundBeam("地梁施工完成");
    }

    @Override
    public void buildFixture() {
        house.setFixture("钢筋施工完成");
    }

    @Override
    public void buildCement() {
        house.setCement("水泥施工完成");
    }

    @Override
    public void buildRoof() {
        house.setRoof("屋顶施工完成");
    }

    @Override
    public House getHouse() {
        return house;
    }
}

创建完之后,开始实例化房屋对象,这里可以理解为房屋的组装,建一个类,用来将实例化的小的对象组成一个大的对象

/**
 * @ClassName : Director
 * @Description : 创建指定房屋的类
 * @Author : CJH
 * @Date: 2020-10-12 18:23
 */
public class Director {
    public static House create(IBuilder builder){
        /**
         * 根据需要建造出自己心仪的房屋
         */
        builder.buildGround();
        builder.buildGroundBeam();
        builder.buildFixture();
        builder.buildCement();
        builder.buildRoof();

        return builder.getHouse();
    }
}

这样用建造者模式实例化复杂对象的过程就完成了,下面开始测试


/**
 * @ClassName : Test
 * @Description :
 * @Author : CJH
 * @Date: 2020-10-12 18:21
 */
public class Test {
    public static void main(String[] args) {
        IBuilder defaultBuilder = new DefaultBuilder();
        House house = Director.create(defaultBuilder);
        System.out.println(house);

    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值