• spring-spring中的设计模式


    简单工厂模式

    介绍

    工厂类拥有一个工厂方法(create),接受了一个参数,通过不同的参数实例化不同的产品类。

    优缺点

    • 优点:
      • 很明显,简单工厂的特点就是“简单粗暴”,通过一个含参的工厂方法,我们可以实例化任何产品类,上至飞机火箭,下至土豆面条,无所不能。
      • 所以简单工厂有一个别名:上帝类。
    • 缺点:
      • 任何”东西“的子类都可以被生产,负担太重。当所要生产产品种类非常多时,工厂方法的代码量可能会很庞大。
      • 遵循开闭原则(对拓展开放,对修改关闭)的条件下,简单工厂对于增加新的产品,无能为力。因为增加新产品只能通过修改工厂方法来实现。

    工厂方法正好可以解决简单工厂的这两个缺点。

    示例

    • 普通-简单工厂类:
    public class AnimalFactory {
    
        //简单工厂设计模式(负担太重、不符合开闭原则)
        public static Animal createAnimal(String name){
            if ("cat".equals(name)) {
                return new Cat();
            }else if ("dog".equals(name)) {
                return new Dog();
            }else if ("cow".equals(name)) {
                return new Dog();
            }else{
                return null;
            }
        }
    }
    • 静态方法工厂
    /该简单工厂,也称为静态方法工厂
    public class AnimalFactory2 {
    
        public static Dog createDog(){
            return new Dog();
        }
        
        public static Cat createCat(){
            return new Cat();
        }
    }

    工厂方法模式

    介绍

    工厂方法是针对每一种产品提供一个工厂类。

    通过不同的工厂实例来创建不同的产品实例。

    优缺点

    • 优点:

      • 工厂方法模式就很好的减轻了工厂类的负担,把某一类/某一种东西交由一个工厂生产;(对应简单工厂的缺点1)
      • 同时增加某一类”东西“并不需要修改工厂类,只需要添加生产这类”东西“的工厂即可,使得工厂类符合开放-封闭原则。
    • 缺点:

      • 对于某些可以形成产品族的情况处理比较复杂。

    示例

    • 抽象出来的工厂对象
    // 抽象出来的动物工厂----它只负责生产一种产品
    public abstract class AnimalFactory {
        // 工厂方法
        public abstract Animal createAnimal();
    }
    • 具体的工厂对象
    // 具体的工厂实现类
    public class CatFactory extends AnimalFactory {
    
        @Override
        public Animal createAnimal() {
            return new Cat();
        }
    }

    抽象工厂模式

    介绍

    • 抽象工厂是应对产品族概念的。

      例如,汽车可以分为轿车、SUV、MPV等,也分为奔驰、宝马等。我们可以将奔驰的所有车看作是一个产品族,而将宝马的所有车看作是另一个产品族。分别对应两个工厂,一个是奔驰的工厂,另一个是宝马的工厂。与工厂方法不同,奔驰的工厂不只是生产具体的某一个产品,而是一族产品(奔驰轿车、奔驰SUV、奔驰MPV)。“抽象工厂”的“抽象”指的是就是这个意思。

    • 上边的工厂方法模式是一种极端情况的抽象工厂模式(即只生产一种产品的抽象工厂模式),而抽象工厂模式可以看成是工厂方法模式的一种推广。

    工厂模式区别

    • 简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)

    • 工厂方法 : 用来生产同一等级结构中的固定产品。(支持拓展增加产品) 

    • 抽象工厂 : 用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)

    单例模式

    介绍

    单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:

    1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

    2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

    示例

    • 饿汉式单例
    public class Student1 {
        // 2:成员变量初始化本身对象
        private static Student1 student = new Student1();
    
        // 1:构造私有
        private Student1() {
        }
    
        // 3:对外提供公共方法获取对象
        public static Student1 getSingletonInstance() {
            return student;
        }
    }
    • 懒汉式单例
    public class Student5 {
    
        private Student5() {
        }
        /*
         * 此处使用一个内部类来维护单例 JVM在类加载的时候,是互斥的,所以可以由此保证线程安全问题
         */
        private static class SingletonFactory {
            private static Student5 student = new Student5();
        }
        /* 获取实例 */
        public static Student5 getSingletonInstance() {
            return SingletonFactory.student;
        }
    }

    原型模式

    介绍

    原型模式虽然是创建型的模式,但是与工厂模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

    示例

    • 先创建一个原型类:
    public class Prototype implements Cloneable {  
      
        public Object clone() throws CloneNotSupportedException {  
            Prototype proto = (Prototype) super.clone();  
            return proto;  
        }  
    } 

    很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,具体怎么实现,此处不再深究。

    在这儿,我将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:

    • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的

    • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底

    • 写一个深浅复制的例子
    public class Prototype implements Cloneable, Serializable {  
      
        private static final long serialVersionUID = 1L;  
        private String string;  
      
        private SerializableObject obj;  
      
        /* 浅复制 */  
        public Object clone() throws CloneNotSupportedException {  
            Prototype proto = (Prototype) super.clone();  
            return proto;  
        }  
      
        /* 深复制 */  
        public Object deepClone() throws IOException, ClassNotFoundException {  
      
            /* 写入当前对象的二进制流 */  
            ByteArrayOutputStream bos = new ByteArrayOutputStream();  
            ObjectOutputStream oos = new ObjectOutputStream(bos);  
            oos.writeObject(this);  
      
            /* 读出二进制流产生的新对象 */  
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
            ObjectInputStream ois = new ObjectInputStream(bis);  
            return ois.readObject();  
        }  
      
        public String getString() {  
            return string;  
        }  
      
        public void setString(String string) {  
            this.string = string;  
        }  
      
        public SerializableObject getObj() {  
            return obj;  
        }  
      
        public void setObj(SerializableObject obj) {  
            this.obj = obj;  
        }  
      
    }  
      
    class SerializableObject implements Serializable {  
        private static final long serialVersionUID = 1L;  
    }  

    构建者模式

    介绍

    建造者模式的定义是:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。

    建造者模式的角色定义,在建造者模式中存在以下4个角色:

    1. builder:为创建一个产品对象的各个部件指定抽象接口。
    2. ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
    3. Director:构造一个使用Builder接口的对象。
    4. Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

    工厂模式和构建者模式的区别

    构建者模式和工厂模式很类似,区别在于构建者模式是一种个性化产品的创建。而工厂模式是一种标准化的产品创建。

    • 导演类:按照一定的顺序或者一定的需求去组装一个产品。

    • 构造者类:提供对产品的不同个性化定制,最终创建出产品。

    • 产品类:最终的产品

    示例

    • 构建者
    // 构建器
    public class StudentBuilder {
    
        // 需要构建的对象
        private Student student = new Student();
    
        public StudentBuilder id(int id) {
            student.setId(id);
            return this;
        }
    
        public StudentBuilder name(String name) {
            student.setName(name);
            return this;
        }
    
        public StudentBuilder age(int age) {
            student.setAge(age);
            return this;
        }
    
        public StudentBuilder father(String fatherName) {
            Father father = new Father();
            father.setName(fatherName);
            student.setFather(father);
            return this;
        }
    
        // 构建对象
        public Student build() {
            return student;
        }
    }
    • 导演类
    // 导演类/测试类
    public class BuildDemo {
    
        public static void main(String[] args) {
    
            StudentBuilder builder = new StudentBuilder();
            // 决定如何创建一个Student
            Student student = builder.age(1).name("zhangsan").father("zhaosi").build();
            System.out.println(student);
    
        }
    }

    适配器模式

    适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式

    核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:

    public class Source {   
         public void method1() {  
             System.out.println("this is original method!");  
         }  
     }  
    public interface Targetable {  
         /* 与原类中的方法相同 */  
         public void method1();  
         /* 新类的方法 */  
         public void method2();  
     }
    public class Adapter extends Source implements Targetable {  
         @Override  
         public void method2() {  
             System.out.println("this is the targetable method!");  
         }  
     }  

    Adapter类继承Source类,实现Targetable接口,下面是测试类:

     
    public class AdapterTest {  
         public static void main(String[] args) {  
             Targetable target = new Adapter();  
             target.method1();  
             target.method2();  
         }  
     }  

    这样Targetable接口的实现类就具有了Source类的功能。

    对象的适配器模式

    基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。

    public class Wrapper implements Targetable {  
        private Source source;  
        public Wrapper(Source source){  
            super();  
            this.source = source;  
        }  
        @Override  
         public void method2() {  
             System.out.println("this is the targetable method!");  
         }    
         @Override  
         public void method1() {  
             source.method1();  
         }  
     }

    接口的适配器模式

    第三种适配器模式是接口的适配器模式,接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

    public interface Sourceable {      
         public void method1();  
         public void method2();  
     } 
    public abstract class Wrapper2 implements Sourceable{  
           
         public void method1(){}  
         public void method2(){}  
     }  
    public class SourceSub1 extends Wrapper2 {  
         public void method1(){  
             System.out.println("the sourceable interface's first Sub1!");  
         }  
     } 
    public class SourceSub2 extends Wrapper2 {  
         public void method2(){  
             System.out.println("the sourceable interface's second Sub2!");  
         }  
     } 

    讲了这么多,总结一下三种适配器模式的应用场景:

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

    • 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

    • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

    装饰模式

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:

     public interface Sourceable {  
         public void method();  
     }
     public class Source implements Sourceable {   
         @Override  
         public void method() {  
             System.out.println("the original method!");  
         }  
     }  
    public class Decorator implements Sourceable {    
         private Sourceable source;         
         public Decorator(Sourceable source){  
             super();  
             this.source = source;  
         }  
         @Override  
         public void method() {  
             System.out.println("before decorator!");  
             source.method();  
             System.out.println("after decorator!");  
         }  
     } 

    装饰器模式的应用场景:

    1. 需要扩展一个类的功能。
    2. 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

    缺点:

    ​ 产生过多相似的对象,不易排错!

    代理模式

    其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作。代理又分为动态代理和静态代理

    静态代理

    比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。

    public interface Sourceable {  
         public void method();  
     }
    public class Source implements Sourceable {    
         @Override  
         public void method() {  
             System.out.println("the original method!");  
         }  
     }
    public class Proxy implements Sourceable {    
         private Source source;  
         public Proxy(){  
             super();  
             this.source = new Source();  
         }  
         @Override  
         public void method() {  
             before();  
             source.method();  
             atfer();  
         }  
         private void atfer() {  
             System.out.println("after proxy!");  
         }  
         private void before() {  
             System.out.println("before proxy!");  
         }  
     }

    代理模式的应用场景:

    如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

    1. 修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
    2. 就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

    使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

    动态代理

    JDK动态代理
    public class JDKProxyFactory implements InvocationHandler {
    
        // 目标对象的引用
        private Object target;
    
        // 通过构造方法将目标对象注入到代理对象中
        public JDKProxyFactory(Object target) {
            super();
            this.target = target;
        }
    
        /**
         * @return
         */
        public Object getProxy() {
    
            // 如何生成一个代理类呢?
            // 1、编写源文件
            // 2、编译源文件为class文件
            // 3、将class文件加载到JVM中(ClassLoader)
            // 4、将class文件对应的对象进行实例化(反射)
    
            // Proxy是JDK中的API类
            // 第一个参数:目标对象的类加载器
            // 第二个参数:目标对象的接口
            // 第二个参数:代理对象的执行处理器
            Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    this);
    
            return object;
        }
    
        /**
         * 代理对象会执行的方法
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Method method2 = target.getClass().getMethod("saveUser", null);
            Method method3 = Class.forName("com.sun.proxy.$Proxy4").getMethod("saveUser", null);
            System.out.println("目标对象的方法:" + method2.toString());
            System.out.println("目标接口的方法:" + method.toString());
            System.out.println("代理对象的方法:" + method3.toString());
            System.out.println("这是jdk的代理方法");
            // 下面的代码,是反射中的API用法
            // 该行代码,实际调用的是[目标对象]的方法
            // 利用反射,调用[目标对象]的方法
            Object returnValue = method.invoke(target, args);
    
            return returnValue;
        }
    }
    CGLib动态代理
    public class CgLibProxyFactory implements MethodInterceptor {
    
        /**
         * @param clazz
         * @return
         */
        public Object getProxyByCgLib(Class clazz) {
            // 创建增强器
            Enhancer enhancer = new Enhancer();
            // 设置需要增强的类的类对象
            enhancer.setSuperclass(clazz);
            // 设置回调函数
            enhancer.setCallback(this);
            // 获取增强之后的代理对象
            return enhancer.create();
        }
    
        /***
         * Object proxy:这是代理对象,也就是[目标对象]的子类 
         * Method method:[目标对象]的方法 
         * Object[] arg:参数
         * MethodProxy methodProxy:代理对象的方法
         */
        @Override
        public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
            // 因为代理对象是目标对象的子类
            // 该行代码,实际调用的是父类目标对象的方法
            System.out.println("这是cglib的代理方法");
    
            // 通过调用子类[代理类]的invokeSuper方法,去实际调用[目标对象]的方法
            Object returnValue = methodProxy.invokeSuper(proxy, arg);
            // 代理对象调用代理对象的invokeSuper方法,而invokeSuper方法会去调用目标类的invoke方法完成目标对象的调用
            
            return returnValue;
        }
    }
  • 相关阅读:
    SpringBoot整合ActiveMQ同时支持P2P和发布订阅模式(三)
    SpringBoot整合ActiveMQ的publish/subscribe发布订阅模式(二)
    Windows启动ActiveMQ报Wrapper Stopped错误
    IDEA从远程仓库克隆项目
    Git的安装
    IDEA上传项目到使用github上
    Mybaits的逆向工程
    posman测试接口需要登录验证的使用
    SSM整合SpringSecurity
    SpringBoot整合MongoDB的连接用户名和密码问题
  • 原文地址:https://www.cnblogs.com/yintingting/p/10348534.html
Copyright © 2020-2023  润新知