zoukankan      html  css  js  c++  java
  • 设计模式之代理模式

    终于……看到了……代理模式……

    我觉得……

    这个模式……

    真的……

    好难啊……

    首先是远程代理

    Java RMI 概观

    ↑都是这个东西好难

    代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。

    使用代理模式创建代表(representative)对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象,创建开销大的对象或者需要安全控制的对象

    类图:

    RealSubject是真正做事的对象,它是被Proxy代理和控制访问的对象。客户与RealSubject的交互都必须通过Proxy。

    远程代理:远程代理可以作为;另一个JVM上的对象的本地代表。调用代理方法,会代理利用网络转发到远程执行,并将结果通过网络返回给代理,再由代理将结果转给客户。

    虚拟代理:虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。

    栗子:

    写一个程序展示CD封面,当封面没有加载好的时候,就显示“加载中。。。”

    创建一个代理类,当照片没有加载好的时候,就显示加载中,但加载好之后,就好所有方法委托给真正的照片类。

    为什么要虚拟代理呢?因为没加载好的时候,还没有图片,这个代理就是一个虚拟的图片,,,

    import java.awt.Component;
    import java.awt.Graphics;
    import java.net.URL;
    
    import javax.swing.Icon;
    import javax.swing.ImageIcon;
    
    // 代理Icon也要实现Icon接口
    public class ImageProxy implements Icon {
        ImageIcon imageIcon;
        URL imageURL;
        Thread retrievalThread;
        boolean retrieving = false;
        
        // URL是图片资源位置
        public ImageProxy(URL url) {
            imageURL = url;
        }
        // 在图像加载完毕前  返回默认的宽和高
        public int getIconWidth() {
            if (imageIcon != null) {
                return imageIcon.getIconWidth();
            } else {
                return 800;
            }
        }
        
        public int getIconHeight() {
            if (imageIcon != null) {
                return imageIcon.getIconHeight();
            } else {
                return 600;
            }
        }
    
        public void paintIcon(final Component c, Graphics g, int x, int y) {
            
            if (imageIcon != null) {
                // 如果已经有Icon  就告诉它画出自己
                imageIcon.paintIcon(c, g, x, y);
            } else {
                // 否则就显示加载中
                g.drawString("Loading CD cover, please wait...", x+300, y+190);
                if (!retrieving) {
                    // 如果我们还有试着取出图像  那么就开始取图像
                    retrieving = true;
                    retrievalThread = new Thread(new Runnable() {
                        public void run() {
                            try {
                                // 要在这里加载真正的Icon图像
                                // 请注意  加载图像和ImageIcon是同步的  也就是说  只有在加载完成之后  ImageIcon构造器才会返回
                                // 这样  我们的程序就会耗在这里  动弹不得  也没办法显示消息
                                // 我们不希望挂起整个用户界面  所以用另一个线程取出图像
                                // 在线程中  我们实例化此Icon对象  其构造器会在图像加载完成后才返回
                                // 当图像准备好时  我们告诉Swing要重绘
                                imageIcon = new ImageIcon(imageURL, "CD cover");
                                c.repaint();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    retrievalThread.start();
                }
            }
        }
        
    }

    然后是显示图片的ImageComponent类

    import java.awt.*;  
    import javax.swing.*;  
      
    class ImageComponent extends JComponent {  
        private Icon icon;  
      
        public ImageComponent(Icon icon) {  
            this.icon = icon;
        }  
      
        public void setIcon(Icon icon) {  
            this.icon = icon;
        }  
      
        public void paintComponent(Graphics g) {  
            super.paintComponent(g);  
            int w = icon.getIconWidth();  
            int h = icon.getIconHeight();  
            int x = (800 - w)/2;  
            int y = (600 - h)/2;  
            icon.paintIcon(this, g, x, y);  
        }  
    } 

    测试类:

    import java.net.*;  
    import java.awt.event.*;  
    import javax.swing.*;  
    import java.util.*;
    import java.util.Map.Entry;  
      
    public class ImageProxyTestDrive {  
          
        ImageComponent imageComponent;  
        JFrame frame = new JFrame("CD封面加载器");
        JMenuBar menuBar;  // 菜单栏  
        JMenu menu;        // 菜单  
        Map<String, String> cds = new HashMap<>();
       
        public static void main (String[] args) throws Exception {  
            new ImageProxyTestDrive();
        }  
       
        public ImageProxyTestDrive() throws Exception{  
              
            // 构造菜单项用的, key=CD名,  value=URL  
            cds.put("轨迹","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E8%BD%A8%E8%BF%B9.png");  
            cds.put("分我一半的眼泪","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E5%88%86%E6%88%91%E4%B8%80%E5%8D%8A%E7%9A%84%E7%9C%BC%E6%B3%AA.jpg");  
            cds.put("光荣","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E5%85%89%E8%8D%A3.png");  
            cds.put("画中仙","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E7%94%BB%E4%B8%AD%E4%BB%99.jpg");  
      
            // 设置初始的CD封面  
            URL initialURL = new URL(cds.get("轨迹"));
            // 建立菜单栏  
            menuBar = new JMenuBar();
            menu = new JMenu("Favorite CDs");
            menuBar.add(menu);
            frame.setJMenuBar(menuBar);
      
            for(Entry<String, String> e : cds.entrySet()) {  
                String name = e.getKey();
                String url = e.getValue();
                JMenuItem menuItem = new JMenuItem(name);
                menu.add(menuItem);   
                menuItem.addActionListener(new ActionListener() {
                      public void actionPerformed(ActionEvent event) {
                        try {
                            imageComponent.setIcon(new ImageProxy(new URL(url)));
                        } catch (MalformedURLException e) {
                            e.printStackTrace();
                        }
                        frame.repaint();
                      }  
                });  
            }  
              
            // set up frame and menus  
            Icon icon = new ImageProxy(initialURL);
            imageComponent = new ImageComponent(icon);
            frame.getContentPane().add(imageComponent);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(800,600);
            frame.setVisible(true);
        }
    }

    一个虚拟代理就完成了。。。

    加载前:加载后:

    在上面的例子中,每一次都是创建新的ImageProxy来取得对象,即使图像已经被取出来过,可以把已加载的图片放入缓存中,这就是缓存代理。

    缓存代理(Caching Proxy)会维护之前创建的对象,当收到请求时,在可能的情况下返回缓存对象。

      

    动态代理:Java在java.lang.reflect包中有自己的代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时被创建的,我们称这个技术为:动态代理。

    动态代理类图:

    动态代理的实现

    有一个系统,存有每个人的信息,和每个人的评分。现在希望每个人不能给自己评分,而更改个人信息只有自己能做到。

    于是设计两个代理,一个控制访问自己的类,一个控制访问其他人。

    首先是个人信息的接口:

    public interface PersonBean {
        String getName();
        String getGender();
        String getInterests();
        int getHotOrNotRating();
        
        void setName(String name);
        void setGender(String gender);
        void setInterests(String interests);
        void setHotOrNotRating(int rating);
    }

    具体实现类

    public class PersonBeanImpl implements PersonBean {
        String name;
        String gender;
        String interests;
        int rating;
        int ratingCount = 0;    
        public PersonBeanImpl(String name, String gender, String interests) {
            super();
            this.name = name;
            this.gender = gender;
            this.interests = interests;
        }
        public String getName() {
            return name;
        }
        public String getGender() {
            return gender;
        }
        public String getInterests() {
            return interests;
        }
        
        public int getHotOrNotRating() {
            if (ratingCount == 0) return 0;
            return (rating / ratingCount);
        }
        
        public void setName(String name) {
            this.name = name;
        }
        public void setGender(String gender) {
            this.gender = gender;
        }
        public void setInterests(String interests) {
            this.interests = interests;
        }
        public void setHotOrNotRating(int rating) {
            this.rating += rating;
            ratingCount++;
        }
        
    }

    创建两个InvocationHandler,一个给拥有者使用

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class OwnerInvocationHandler implements InvocationHandler {
        PersonBean person;
        
        public OwnerInvocationHandler(PersonBean person) {
            this.person = person;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            
            try {
                if (method.getName().startsWith("get")) {
                    return method.invoke(person, args);
                } else if (method.getName().equals("setHotOrNotRating")) {
                    throw new IllegalAccessException();
                } else if (method.getName().startsWith("set")) {
                    return method.invoke(person, args);
                }
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            
            return null;
        }
    
    }

    一个给非拥有者使用

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class NotOwnerInvocationHandler implements InvocationHandler {
        PersonBean person;
        
        public NotOwnerInvocationHandler(PersonBean person) {
            this.person = person;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
            
            try {
                if (method.getName().startsWith("get")) {
                    return method.invoke(person, args);
                } else if (method.getName().equals("setHotOrNotRating")) {
                    return method.invoke(person, args);
                } else if (method.getName().startsWith("set")) {
                    throw new IllegalAccessException();
                }
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            
            return null;
        }
    }

    测试类:

    import java.lang.reflect.Proxy;
    import java.util.ArrayList;
    
    public class MatchMakingTestDrive {
        public static void main(String[] args) {
            MatchMakingTestDrive test = new MatchMakingTestDrive();
            test.drive();
        }
        
        public MatchMakingTestDrive() {
            initializeDatabsae();
        }
        
        public void drive() {
            PersonBean joe = getPersonFromDatabase("Joe JavaBean");
            PersonBean ownerProxy = getOwnerProxy(joe);
            System.out.println("Name is " + ownerProxy.getName());
            ownerProxy.setInterests("bowling, Go");
            System.out.println("Interests set from owner proxy");
            try {
                ownerProxy.setHotOrNotRating(10);//不能给自己平分
            } catch (Exception e) {
                System.out.println("Can't set rating from owner proxy");
            }
            
            System.out.println("Rating is " + ownerProxy.getHotOrNotRating());
            
            PersonBean nonOwnerProxy = getNonOwnerProxy(joe);
            System.out.println("Name is " + nonOwnerProxy.getName());
            try {
                nonOwnerProxy.setInterests("bowling, Go");
            } catch (Exception e) {
                System.out.println("Can't set interests from non owner proxy");
            }
            nonOwnerProxy.setHotOrNotRating(3);
            System.out.println("Rating set from non owner proxy");
            System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());
        }
        
        PersonBean getOwnerProxy(PersonBean person) {
            return (PersonBean) Proxy.newProxyInstance(
                                    person.getClass().getClassLoader(), 
                                    person.getClass().getInterfaces(), 
                                    new OwnerInvocationHandler(person));
        }
        
        PersonBean getNonOwnerProxy(PersonBean person) {
            return (PersonBean) Proxy.newProxyInstance(
                                    person.getClass().getClassLoader(), 
                                    person.getClass().getInterfaces(), 
                                    new NotOwnerInvocationHandler(person));
        }
        
        /************假装这是数据库实现**************/
        
        ArrayList<PersonBean> beans;
        
        void initializeDatabsae() {
            beans = new ArrayList<>();
            beans.add(new PersonBeanImpl("Joe JavaBean", "男", "学习"));
        }
        
        PersonBean getPersonFromDatabase(String name) {
            for (int i = 0; i < beans.size(); ++i) {
                PersonBean person = beans.get(i);
                if (person.getName().equals(name)) return person;
            }
            return null;
        }
    }

    一些其他的代理模式:

    防火墙代理:控制网络资源的访问,保护主题免于“坏客户”的侵害。

    智能引用代理:当主题被引用时,进行额外的动作,例如计算一个对象呗引用的次数。

    缓存代理:为开销大的运算结果提供暂时的存储,它也允许多个客户共享结果,以减少计算或网络延迟。

    同步代理:在多线程的情况下为主题提供安全的访问。

    复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度,并进行访问控制。

    写入时复制代理:用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。这是虚拟代理的变体。

  • 相关阅读:
    我所理解的执行力
    iOS移动开发周报-第20期
    iOS移动开发周报-第19期
    iOS开发如何提高
    iOS移动开发周报-第18期
    iOS移动开发周报-第17期
    一起入门python3之元组和数列
    提权笔记本
    sqlmap笔记本
    SQL注入自学[第一学:一个简单的注入环境的编写]
  • 原文地址:https://www.cnblogs.com/wenruo/p/6556283.html
Copyright © 2011-2022 走看看