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;
    }
}

 

 

posted @ 2019-02-02 15:29  Emyin  阅读(696)  评论(0编辑  收藏  举报