Java进阶篇设计模式之七 ----- 享元模式和代理模式

前言

上一篇中咱们学习告终构型模式的组合模式和过滤器模式。本篇则来学习下结构型模式最后的两个模式, 享元模式和代理模式。html

享元模式

简介java

享元模式主要用于减小建立对象的数量,以减小内存占用和提升性能。这种类型的设计模式属于结构型模式,它提供了减小对象数量从而改善应用所需的对象结构的方式。git

用通俗的话来讲就是进行共用。生活中也有一些例子,好比以前很火的共享单车,更早以前的图书馆,编程中常常用的String类,数据库链接池等等。固然,享元模式主要的目的是复用,若是该对象没有的话,就会进行建立。程序员

享元模式的角色主要分为三大类,抽象享元类、具体享元类以及享元工厂类。github

  • 抽象享元类:全部具体享元类的超类或者接口,经过这个接口,能够接受并做用于外部专题。
  • 具体享元类:实现抽象享元类接口的功能并增长存储空间。
  • 享元工厂类:用来建立并管理抽象享元类对象,它主要用来确保合理地共享。每当接受到一个请求是,便会提供一个已经建立的抽象享元类对象或者新建一个。 享元模式的核心在于享元工厂类,享元工厂类的做用在于提供一个用于存储享元对象的享元池,用户须要对象时,首先从享元池中获取,若是享元池中不存在 ,则建立一个新的享元对象返回给用户,并在享元池中保存该新增对象。

其它的就再也不多说,这里依旧使用一个简单的示例来加以说明。
在咱们之前读书的时候,常常会用到笔,其中铅笔又是最先接触的,咱们最开始使用铅笔可能不是写字,而是进行画画。这里咱们能够把笔看成一个抽象享元类,铅笔看成一个具体享元类,而后再建立一个享元工厂类,用于建立和管理,最后再由调用者决定用铅笔进行干吗。spring

首先,咱们建立一个接口。数据库

interface Pen {
   void write();
}

而后再建立一个享元工厂类,指定须要内部须要作的事情。编程

class Penil implements Pen {
   private String name;
   private String something; 
   private  int i;
   
   public Penil(String name) {
       this.name = name;
       i++;
       System.out.println(name+" 第:"+i+"次建立");
   }
   
   public String getName() {
       return name;
   }
   
   public void setName(String name) {
       this.name = name;
   }
   
   public String getSomething() {
       return something;
   }
   
   public void setSomething(String something) {
       this.something = something;
   }
   
   @Override
   public void write() {
       System.out.println(name+" 用于铅笔  "+something);
   }
}

继而再建立一个工厂类,用于建立和管理。设计模式

class PenFactory {
   private static final Map<String, Penil> map = new HashMap<String, Penil>();

   public static Penil get(String name) {
       Penil penil = map.get(name);
       if (penil == null) {
           penil = new Penil(name);
           map.put(name, penil);
       }
       return penil;
   }
}

最后再来进行调用测试。安全

public class FlyweightTest {
    public static void main(String[] args) {
        String names[] = { "张三", "李四", "王五", "虚无境" };
        for (int i = 0; i < 8; i++) {
            Penil penil = PenFactory.get(names[i>3?i-4:i]);
            penil.setSomething("画了一条鱼");
            penil.write();
        }
    }
}

输出结果:

张三 第:1次建立
            张三 用于铅笔  画了一条鱼
            李四 第:1次建立
            李四 用于铅笔  画了一条鱼
            王五 第:1次建立
            王五 用于铅笔  画了一条鱼
            虚无境 第:1次建立
            虚无境 用于铅笔  画了一条鱼
            张三 用于铅笔  画了一条鱼
            李四 用于铅笔  画了一条鱼
            王五 用于铅笔  画了一条鱼
            虚无境 用于铅笔  画了一条鱼

上述示例中,每一个对象都使用了两次,可是每一个对象都只是建立了一次而已,而享元模式核心的目的其实就是复用,只要咱们理解了这一点,想必掌握该模式也就不在话下了。

享元模式优势:

极大的减小对象的建立,从而下降了系统的内存,提高了效率。

享元模式缺点:

提升了系统的复杂度,由于须要将状态进行分离成内部和外部,而且也使外部状态固有化,使得随着内部状态的变化而变化,会形成系统的混乱。

使用场景:

系统有大量类似对象。

注意事项:

须要注意划分外部状态和内部状态,不然可能会引发线程安全问题。 这些类必须有一个工厂对象加以控制。

与单例模式比较

虽然它们在某些方面很像,可是实际上倒是不一样的东西,单例模式的目的是限制建立多个对象,避免冲突,好比使用数据库链接池。而享元模式享元模式的目的是共享,避免屡次建立耗费资源,好比使用String类。

代理模式

简介

代理模式于结构型模式,主要是经过一个类表明另外一个类的功能。一般,咱们建立具备现有对象的对象,以便向外界提供功能接口。

代理模式,如其名,也就是代理做用。 咱们生活中也有很多示例,好比典型的代购,土豪专用的支票,Windows 里面的快捷方式,以及spring中的aop 等等。

代理模式主要由这三个角色组成,抽象角色、代理角色和真实角色。

  • 抽象角色:经过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,经过真实角色的业务逻辑方法来实现抽象方法,并能够附加本身的操做。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

代理模式又分为静态代理、动态代理。

  • 静态代理是由程序员建立或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就肯定了。
  • 动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪个对象。

这里咱们依旧用一个简单的示例来进行说明。
张三和李四是室友,某天,张三在寝室内玩游戏正带劲,感受肚子饿了,本想下楼去吃饭的,可是想起李四可能快要回来,因而打电话给李四,让李四帮本身带份盒饭。这里的李四就扮演着代理者的做用。

静态代理

首先咱们用静态代理来实现该功能。

这里实现相对而言较为简单,依旧是定义一个接口,而后定义一个真实的角色,实现该接口的功能,继而定义一个代理者,也实现该接口,可是添加该真实角色的对象进行相应的业务逻辑处理。
那么该静态代理代码实现以下:

interface Shopping {
    void buyFood();
}

class ExecutePerson implements Shopping {
    private String name;
    public ExecutePerson(String name) {
        this.name = name;
    }

    @Override
    public void buyFood() {
        System.out.println(name + " 买东西");
    }
}

class ProxyPerson implements Shopping {
    private ExecutePerson ep;
    public ProxyPerson(ExecutePerson ep) {
        this.ep = ep;
    }
    @Override
    public void buyFood() {
        ep.buyFood();
    }
}


public class ProxyTest {
    public static void main(String[] args) {    
        String name = "李四";
        Shopping shopping = new ProxyPerson(new ExecutePerson(name));
        shopping.buyFood();
    }
}

输入结果:

李四 买东西

在使用静态代理实现该功能以后,咱们发现实现起来很简单,经过一个代理类就能够在不影响目标对象的前提进行扩展使用。可是咱们也发现一个问题,若是咱们不肯定须要代理某个真实类的时候会比较麻烦,并且在类过多的时候,目标对象与代理对象都要维护,会使系统复杂度提高,维护起来也更加麻烦。
不过这时咱们就可使用动态代理来进行解决。

动态代理

所谓动态代理能够没必要强行指定某个真实的角色,只须要在运行时决定就能够了。这里咱们可使用JDK中java.lang.reflect来进行开发。

JDK对动态代理提供了如下支持:

  • java.lang.reflect.Proxy 动态生成代理类和对象
  • java.lang.reflect.InvocationHandler
    • 能够经过invoke方法实现对真实角色的代理访问;
    • 每次经过Proxy生成代理类对象时都要指定对象的处理器对象.

那么废话不在多说,开始进行代码改造,以前的接口和真实者不须要更改,咱们只须要更改代理者就能够了。
更改以后的代码以下:

class ProxyPerson2 implements InvocationHandler {
 private Shopping shopping;
 private final String methodName = "buyFood";
 public ProxyPerson2(Shopping shopping) {
     this.shopping = shopping;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     Object result = null;
     if (methodName.equals(method.getName())) {
         result = method.invoke(shopping, args);
     }
     return result;
 }
}

测试代码,注意这里调用和以前不一样!
这里经过Proxy类中的newProxyInstance方法会动态生成一个代理类,而后进行调用。其中这三个参数的说明以下:

  • ClassLoader: 生成一个类, 这个类也须要加载到方法区中, 所以须要指定ClassLoader来加载该类
  • Class[] interfaces: 要实现的接口
  • InvocationHandler: 调用处理器
public class ProxyTest {
    public static void main(String[] args) {    
        Shopping shopping2 = (Shopping)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Shopping.class}, new ProxyPerson2(new ExecutePerson(name)));
        shopping2.buyFood();
    }
}

代理模式优势:

一、职责清晰。 二、高扩展性。 三、智能化。

代理模式缺点:

一、因为在客户端和真实主题之间增长了代理对象,所以有些类型的代理模式可能会形成请求的处理速度变慢。
二、实现代理模式须要额外的工做,有些代理模式的实现很是复杂。

注意事项:

和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
和装饰器模式的区别:装饰器模式为了加强功能,而代理模式是为了加以控制。

代理模式参考:
http://www.runoob.com/design-pattern/proxy-pattern.html
https://baike.baidu.com/item/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/8374046
https://blog.csdn.net/zjf280441589/article/details/50411737

其它

音乐推荐

分享一首很带感的电音!

项目的代码

java-study是本人在学习Java过程当中记录的一些代码,也包括以前博文中使用的代码。若是感受不错,但愿顺手给个start,固然若是有不足,也但愿提出。
github地址: https://github.com/xuwujing/java-study

原创不易,若是感受不错,但愿给个推荐!您的支持是我写做的最大动力! 版权声明: 做者:虚无境 博客园出处:http://www.cnblogs.com/xuwujing CSDN出处:http://blog.csdn.net/qazwsxpcm  我的博客出处:http://www.panchengming.com

相关文章
相关标签/搜索