Flyweight Mode and Proxy Mode

flyweight pattern

Show website project requirements

For a small outsourcing project, we will build a product display website for Client A. Client A's friends feel that the effect is good, and also hope to build such a product display website, but the requirements are somewhat different:

  1. There are customer requests to publish in the form of news

  2. A client requested to publish in the form of a blog

  3. Some customers want to publish in the form of WeChat public account

Traditional solutions for website display projects

  1. Copy and paste a copy directly, and then customize and modify it according to the different requirements of customers

  2. Rent a space for each website

  3. Schematic diagram of scheme design

xy.png

Traditional solution to website display project-problem analysis

Traditional solution to website display project-problem analysis

  1. The required website structure is very similar , and none of them are high-traffic websites. If they are divided into multiple virtual spaces for processing, it is equivalent to a lot of instance objects of the same website, resulting in a waste of server resources.

  2. Solution: Integrate into a website, share its related code and data, and share server resources such as hard disk, memory, CPU, database space, etc., reducing server resources

  3. For the code, it is easier to maintain and extend because it is an instance

  4. The above solution can be solved by using the flyweight mode

Basic introduction to flyweight mode

basic introduction

  1. Flyweight Pattern (Flyweight Pattern), also known as Flyweight Pattern : uses sharing techniques to efficiently support a large number of fine-grained objects

  2. It is often used in the bottom-level development of the system to solve the performance problem of the system. Like the database connection pool , there are created connection objects in it. In these connection objects, we need to use them directly to avoid re-creation. If there is no need for us, create one.

  3. The Flyweight pattern can solve the problem of memory waste of duplicate objects when there are a large number of similar objects in the system and a buffer pool is needed. Instead of always creating new objects, you can take them from the buffer pool. This reduces system memory while increasing efficiency

  4. 享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

xy1.png

享元模式的原理类图

xy2.png

对原理图的说明-即(模式的角色及职责)

  1. FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现

  2. ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务

  3. UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。

内部状态和外部状态

比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态

  1. 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态外部状态

  2. 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变

  3. 外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

  4. 举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题

享元模式解决网站展现项目

享元模式应用实例

  1. 应用实例要求使用享元模式完成,前面提出的网站外包问题

  2. 思路分析和图解(类图)

  3. 代码实现

User

@Data
@AllArgsConstructor
public class User {
    private String name;
}
复制代码

WebSite

public abstract class WebSite {

    public abstract void use(User user);//抽象方法
}
复制代码

ConcreteWebSite

// 聚体网站
public class ConcreteWebSite extends WebSite{

    //共享的部分,内部状态
    private String type = ""; //网站发布的形式(类型)

    //构造器
    public ConcreteWebSite(String type) {
        this.type = type;
    }

    @Override
    public void use(User user) {
        System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
    }
}
复制代码

WebSiteFactory

// 网站工厂类,根据需要返回压一个网站
public class WebSiteFactory {

    // 集合,充当池的作用
    private Map<String,ConcreteWebSite> pool = new HashMap<String,ConcreteWebSite>();

    // 根据网站的类型,返回一个网站,如果没有就创建一个网站,并放入到池中,并返回
    public WebSite getWebSiteCategory(String type){
        if (!pool.containsKey(type)){
            // 就创建一个网站,并放入池中
            pool.put(type, new ConcreteWebSite(type));
        }

        return (WebSite) pool.get(type);
    }

    //获取网站分类的总数 (池中有多少个网站类型)
    public int getWebSiteCount() {
        return pool.size();
    }
}
复制代码

Client

public class Client {

    public static void main(String[] args) {
        // 创建一个工厂类
        WebSiteFactory factory = new WebSiteFactory();

        // 客户要一个以新闻形式发布的网站
        WebSite webSite1 = factory.getWebSiteCategory("新闻");


        webSite1.use(new User("tom"));

        // 客户要一个以博客形式发布的网站
        WebSite webSite2 = factory.getWebSiteCategory("博客");

        webSite2.use(new User("jack"));

        // 客户要一个以博客形式发布的网站
        WebSite webSite3 = factory.getWebSiteCategory("博客");

        webSite3.use(new User("smith"));

        // 客户要一个以博客形式发布的网站
        WebSite webSite4 = factory.getWebSiteCategory("博客");

        webSite4.use(new User("king"));

        System.out.println("网站的分类共=" + factory.getWebSiteCount());
    }
}
复制代码

享元模式在JDK-Interger的应用源码分析

  1. Integer中的享元模式
  2. 代码分析+Debug源码+说明

xy3.png

public class FlyWeight {

    public static void main(String[] args) {
        //如果 Integer.valueOf(x) x 在  -128 --- 127 直接,就是使用享元模式返回,如果不在
        //范围类,则仍然 new 

        //小结:
        //1. 在valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的Integer(new), 否则,就直接从 缓存池返回
        //2. valueOf 方法,就使用到享元模式
        //3. 如果使用valueOf 方法得到一个Integer 实例,范围在 -128 - 127 ,执行速度比 new 快


        Integer x = Integer.valueOf(127); // 得到 x实例,类型 Integer
        Integer y = new Integer(127); // 得到 y 实例,类型 Integer
        Integer z = Integer.valueOf(127);//..
        Integer w = new Integer(127);


        System.out.println(x.equals(y)); // 大小,true
        System.out.println(x == y); //  false
        System.out.println(x == z); // true
        System.out.println(w == x); // false
        System.out.println(w == y); // false


        Integer x1 = Integer.valueOf(200);
        Integer x2 = Integer.valueOf(200);
        System.out.println("x1==x2" + (x1 == x2)); // false
    }
}
复制代码

享元模式的注意事项和细节

  1. 在享元模式这样理解,“享”就表示共享,“元”表示对象

  2. 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式

  3. 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储

  4. 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率

  5. 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.

  6. 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。

  7. 享元模式经典的应用场景是需要缓冲池的场景,比如 String常量池、数据库连接池

代理模式(Proxy)

代理模式的基本介绍

  1. 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

  2. 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象

  3. 代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代理)Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴)

  4. 代理模式示意图

xy5.png

一、静态代理

静态代码模式的基本介绍

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类

具体要求

  1. 定义一个接口:ITeacherDao

  2. 目标对象TeacherDAO实现接口ITeacherDAO

  3. 使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO

  4. 调用的时候通过调用代理对象的方法来调用目标对象.

  5. 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

xy7.png

应用实例

ITeacherDao

public interface ITeacherDao {
    void teach(); // 授课的方法
}
复制代码

TeacherDao

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println(" 老师授课中  。。。。。");
    }
}
复制代码

TeacherDaoProxy

//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{

    private ITeacherDao target;  // 目标对象,通过接口来聚合

    // 构造器
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("开始代理 完成某些操作。。。。。。。");
        target.teach();
        System.out.println("提交");
    }
}
复制代码

Client

public class Client {

    public static void main(String[] args) {
        // 创建目标对象(被代理对象)
        TeacherDao teacherDao = new TeacherDao();

        // 创建代理对象,同时将被代理对象传递给代理对象
        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);

        // 通过代理对象,调用到被代理对象的方法
        // 即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
        teacherDaoProxy.teach();
    }
}
复制代码

静态代理优缺点

  1. 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展

  2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类

  3. 一旦接口增加方法,目标对象与代理对象都要维护

二、动态代理

动态代理模式的基本介绍

  1. 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
  3. 动态代理也叫做:JDK代理、接口代理

JDK中生成代理对象的API

  1. 代理类所在包:java.lang.reflect.Proxy
  2. JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[]

interfaces,InvocationHandler h )
复制代码
  1. 动态代理应用实例

将前面的静态代理改进成动态代理模式(即:JDK代理模式)

xy8.png ITeacherDao

public interface ITeacherDao {

    void teach(); // 授课方法
    void sayHello(String name);
}
复制代码

TeacherDao

public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println(" 老师授课中.... ");
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }

}
复制代码

ProxyFactory

public class ProxyFactory {
    //维护一个目标对象 , Object
    private Object target;

    //构造器 , 对target 进行初始化
    public ProxyFactory(Object target) {

        this.target = target;
    }

    //给目标对象 生成一个代理对象
    public Object getProxyInstance() {

        //说明
      /*
       *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

            //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
            //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
            //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
       */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // TODO Auto-generated method stub
                        System.out.println("JDK代理开始~~");
                        //反射机制调用目标对象的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("JDK代理提交");
                        return returnVal;
                    }
                });
    }

}
复制代码

Client

public class Client {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建目标对象
        ITeacherDao target = new TeacherDao();

        //给目标对象,创建代理对象, 可以转成 ITeacherDao
        ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();

        // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
        System.out.println("proxyInstance=" + proxyInstance.getClass());

        //通过代理对象,调用目标对象的方法
        //proxyInstance.teach();

        proxyInstance.sayHello(" tom ");
    }
}
复制代码

思路图解(类图)

三、Cglib代理

Cglib代理模式的基本介绍

  • 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理

  • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截

  • 在AOP编程中如何选择代理模式:

    • 目标对象需要实现接口,用JDK代理

    • 目标对象不需要实现接口,用Cglib代理

  • The bottom layer of the Cglib package is to convert bytecode and generate new classes by using the bytecode processing framework ASM

Cglib proxy mode application example

xy9.png Application Example Requirements

Implement TeacherDao in the previous case with Cglib proxy mode

public class TeacherDao {
    public String teach() {
        System.out.println(" 老师授课中  , 我是cglib代理,不需要实现接口 ");
        return "hello";
    }
}
复制代码
public class ProxyFactory implements MethodInterceptor {

    //维护一个目标对象
    private Object target;

    //构造器,传入一个被代理的对象
    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回一个代理对象:  是 target 对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();

    }


    //重写  intercept 方法,会调用目标对象的方法
    @Override
    public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("Cglib代理模式 ~~ 开始");
        Object returnVal = method.invoke(target, args);
        System.out.println("Cglib代理模式 ~~ 提交");
        return returnVal;
    }
}
复制代码

Variants of proxy mode (Proxy)

Introduction to several common proxy patterns—several variants

  1. Firewall proxy

The intranet penetrates the firewall through the proxy to achieve access to the public network.

  1. caching proxy

For example: when requesting resources such as image files, first go to the cache proxy to get them, if the resources are obtained, it is ok.

  1. remote proxy

The local representation of the remote object, through which the remote object can be called as a local object. Remote agents communicate with real remote objects over the network.

xy6.png4) Synchronization agent is mainly used in multi-threaded programming to complete synchronization between multi-threads

Guess you like

Origin juejin.im/post/7080560636228272135