Java 设计模式(代理模式)

代理模式

为另一个对象提供一个替身或占位符以控制对这个对象的访问。
需使用 RMI (Remote Method Invocation) 远程方法调用,需简单了解。

RMI 注意:

  1. 启动远程服务前先启动 rmiregistry(使用 Naming.rebind() 注册服务,rmiregister 必须运行)。
  2. 变量和返回值的泪水称为可序列化的类型(编译期无法发现)。
  3. 给客户提供 stub 类
RMI 测试
import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * @author NNroc
 * @date 2020/8/18 12:36
 */
public interface MyRemote extends Remote {
    public String sayHello() throws RemoteException;
}
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

/**
 * @author NNroc
 * @date 2020/8/18 12:37
 */
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
    public String sayHello() {
        return "Server says, 'Hey'";
    }

    public MyRemoteImpl() throws RemoteException {
    }

    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099);
            MyRemote service = new MyRemoteImpl();
            Naming.rebind("rmi://localhost:1099/RemoteHello", service);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
import java.rmi.Naming;

/**
 * @author NNroc
 * @date 2020/8/18 12:42
 */
public class MyRemoteClient {
    public static void main(String[] args) {
        new MyRemoteClient().go();
    }

    public void go() {
        try {
            MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1:1099/RemoteHello");
            String s = service.sayHello();
            System.out.println(s);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
远程代理

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

卖糖果机器服务端示例

CEO 想要获取远端每个糖果机的情况。

public interface State extends Serializable {
    public void insertQuarter();

    public void ejectQuarter();

    public void turnCrank();

    public void dispense();
}
public interface GumballMachineRemote extends Remote {
    public int getCount() throws RemoteException;

    public String getLocation() throws RemoteException;

    public State getState() throws RemoteException;
}

其中 5 种方式(soldOutState、noQuarterState、hasQuarterState、soldState、winnerState)请参考https://blog.csdn.net/weixin_43820352/article/details/108053317
需将其中类前两行中GumballMachine gumballMachine;改为transient GumballMachine gumballMachine;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * @author NNroc
 * @date 2020/8/18 11:51
 */
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote {
    State soldOutState;
    State noQuarterState;
    State hasQuarterState;
    State soldState;
    State winnerState;
    State state = soldOutState;
    int count = 0;
    String location;

    public GumballMachine(String location, int numberGumballs) throws RemoteException {//设计一个构造方法来抛出异常,这是因为UnicastRemoteObject 就抛出异常,超类抛出异常时,子类也只得抛出同样的异常。
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        winnerState = new WinnerState(this);

        this.count = numberGumballs;
        if (numberGumballs > 0) {
            state = noQuarterState;
        }
        this.location = location;
    }

    public void insertQuarter() {
        state.insertQuarter();
    }

    public void ejectQuarter() {
        state.ejectQuarter();
    }

    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

    void setState(State state) {
        this.state = state;
    }

    void releaseBall() {
        System.out.println("A gumball comes rolling out the slot...");
        if (count != 0) {
            count = count - 1;
        }
    }

    public void refill(int count) {
        this.count = count;
        state = noQuarterState;
    }

    public int getCount() {
        return count;
    }

    public State getState() {
        return state;
    }

    public String getLocation() {
        return location;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getWinnerState() {
        return winnerState;
    }

    public String toString() {
        StringBuffer result = new StringBuffer();
        result.append("/nMighty Gumball, Inc.");
        result.append("/nJava-enabled Standing Gumball Model #2004");
        result.append("/nInventory: " + count + " gumball");
        if (count != 1) {
            result.append("s");
        }
        result.append("/n");
        result.append("Machine is " + state + "/n");
        return result.toString();
    }
}

测试

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

/**
 * @author NNroc
 * @date 2020/8/18 14:49
 */
public class GumballMachineTestDrive {
    public static void main(String[] args) {
        GumballMachineRemote gumballMachine = null;
        int count;
//        if (args.length < 2) {
//            System.out.println("GumballMachine ");
//            System.exit(1);
//        }

        try {
            // 本地注册
            LocateRegistry.createRegistry(1099);
            count = 100;
            gumballMachine = new GumballMachine("1099", count);
//            count = Integer.parseInt(args[1]);
//            gumballMachine = new GumballMachine(args[0], count);
            Naming.rebind("rmi://localhost:" + "1099" + "/gumballmachine", gumballMachine);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
虚拟代理

虚拟代理控制访问创建开销大的资源。
作为创建开销大的对象的代表。当我们真正需要创建一个对象时才创建它,当对象在创建前和创建中时,虚拟代理来扮演对象的替身,对象创建后,代理会将请求直接委托给对象。

加载图片示例

ImageProxy 工作过程:

  1. ImageProxy 首先创建一个 ImageIcon,然后开始从网络 URL 上加载图片。
  2. 在加载过程中,ImageProxy 显示“CD封面加载中,请稍候······”
  3. 当图像加载完毕,ImageProxy 把所有方法调用委托给真正的 ImageIcon,这些方法包括了 paintIcon()、getWidth() 和 getHeight()。
  4. 如果用户请求新的图像,我们就创建新的代理,重复过程。
import java.awt.*;
import javax.swing.*;

/**
 * @author NNroc
 * @date 2020/8/18 15:38
 */
public class ImageComponent extends JComponent {
    private static final long serialVersionUID = 1L;
    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.awt.*;
import java.net.URL;
import javax.swing.*;

/**
 * @author NNroc
 * @date 2020/8/18 15:34
 */
public class ImageProxy implements Icon {
    // imageIcon是我们希望加载后显示出来的真正图象
    volatile ImageIcon imageIcon;
    final 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) {
            // 如果图象加载完毕,则通知图象绘制自己
            imageIcon.paintIcon(c, g, x, y);
        } else {
            // 无图像则显示Loading
            g.drawString("Loading CD cover, please wait...", x + 300, y + 190);
            if (!retrieving) {
                retrieving = true;

                retrievalThread = new Thread(new Runnable() {
                    public void run() {
                        try {
                            setImageIcon(new ImageIcon(imageURL, "CD Cover"));
                            c.repaint();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                retrievalThread.start();
            }
        }
    }

    synchronized void setImageIcon(ImageIcon imageIcon) {
        this.imageIcon = imageIcon;
    }
}
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.swing.*;

/**
 * @author NNroc
 * @date 2020/8/18 15:39
 */
public class ImageProxyTestDrive {
    ImageComponent imageComponent;
    JFrame frame = new JFrame("CD Cover Viewer");
    JMenuBar menuBar;
    JMenu menu;
    Hashtable<String, String> cds = new Hashtable<String, String>();

    public static void main(String[] args) throws Exception {
        ImageProxyTestDrive testDrive = new ImageProxyTestDrive();
    }

    public ImageProxyTestDrive() throws Exception {
        cds.put("Buddha Bar", "http://images.amazon.com/images/P/B00009XBYK.01.LZZZZZZZ.jpg");
        cds.put("Ima", "http://images.amazon.com/images/P/B000005IRM.01.LZZZZZZZ.jpg");
        cds.put("Karma", "http://images.amazon.com/images/P/B000005DCB.01.LZZZZZZZ.gif");
        cds.put("MCMXC A.D.", "http://images.amazon.com/images/P/B000002URV.01.LZZZZZZZ.jpg");
        cds.put("Northern Exposure", "http://images.amazon.com/images/P/B000003SFN.01.LZZZZZZZ.jpg");
        cds.put("Selected Ambient Works, Vol. 2", "http://images.amazon.com/images/P/B000002MNZ.01.LZZZZZZZ.jpg");

        URL initialURL = new URL((String) cds.get("Selected Ambient Works, Vol. 2"));
        menuBar = new JMenuBar();
        menu = new JMenu("Favorite CDs");
        menuBar.add(menu);
        frame.setJMenuBar(menuBar);

        for (Enumeration<String> e = cds.keys(); e.hasMoreElements(); ) {
            String name = (String) e.nextElement();
            JMenuItem menuItem = new JMenuItem(name);
            menu.add(menuItem);
            menuItem.addActionListener(event -> {
                imageComponent.setIcon(new ImageProxy(getCDUrl(event.getActionCommand())));
                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);

    }

    URL getCDUrl(String name) {
        try {
            return new URL((String) cds.get(name));
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }
    }
}
保护代理

保护代理基于权限控制对资源的访问。

个人信息示例

要防止随意修改别人的信息

/**
 * 用户信息接口
 *
 * @author NNroc
 * @date 2020/8/18 15:46
 */
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);
}
/**
 * @author NNroc
 * @date 2020/8/18 15:47
 */
public class PersonBeanImpl implements PersonBean {
    String name;
    String gender;
    String interests;
    //总评分
    int rating;
    //被评价次数
    int ratingCount = 0;

    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++;
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 本类和OwnerInvocationHandler同理
 *
 * @author NNroc
 * @date 2020/8/18 17:48
 */
public class NonOwnerInvocationHandler implements InvocationHandler {
    PersonBean person;

    public NonOwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }

    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.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author NNroc
 * @date 2020/8/18 15:55
 */
public class OwnerInvocationHandler implements InvocationHandler {
    //持有RealSubject的引用(这样底层就可以调用RealSubject的方法)
    PersonBean person;

    public OwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }

    //proxy的任何代码被调用,就会导致invoke方法被调用
    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")) {
                //不允许被评价者调用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.Proxy;
import java.util.HashMap;

/**
 * @author NNroc
 * @date 2020/8/18 17:50
 */
public class MatchMakingTestDrive {
    // 装载PersonBean的容器
    HashMap<String, PersonBean> datingDB = new HashMap<String, PersonBean>();

    public static void main(String[] args) {
        MatchMakingTestDrive test = new MatchMakingTestDrive();
        test.drive();
    }

    public MatchMakingTestDrive() {
        //初始化datingDB
        initializeDatabase();
    }

    public void drive() {
        //取得PersonBean
        PersonBean joe = getPersonFromDatabase("Joe Javabean");
        //创建出PersonBean的"被评分者代理"对象
        PersonBean ownerProxy = getOwnerProxy(joe);
        System.out.println("Name is " + ownerProxy.getName());
        //被评分者可以设置“名称”、“性别”、“爱好”三个信息
        ownerProxy.setInterests("bowling, Go");
        System.out.println("Interests set from owner proxy");
        try {
            //被评分者不能设置HotOrNotRating值
            ownerProxy.setHotOrNotRating(10);
        } catch (Exception e) {
            System.out.println("Can't set rating from owner proxy");
        }
        System.out.println("Rating is " + ownerProxy.getHotOrNotRating());

        //创建出PersonBean的"评分者代理"对象
        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");
        }
        //评分者可以设置HotOrNotRating值
        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 NonOwnerInvocationHandler(person));
    }

    //根据名称从容器中获得PersonBean
    PersonBean getPersonFromDatabase(String name) {
        return (PersonBean)datingDB.get(name);
    }

    void initializeDatabase() {
        PersonBean joe = new PersonBeanImpl();
        joe.setName("Joe Javabean");
        joe.setInterests("cars, computers, music");
        joe.setHotOrNotRating(7);
        datingDB.put(joe.getName(), joe);

        PersonBean kelly = new PersonBeanImpl();
        kelly.setName("Kelly Klosure");
        kelly.setInterests("ebay, movies, music");
        kelly.setHotOrNotRating(6);
        datingDB.put(kelly.getName(), kelly);
    }
}
代理模式与装饰者模式

代理模式目的:控制被代理者的访问。
装饰者模式目的:为被装饰者加上新的行为。
代表对象而不是装饰对象,作为真正主题的替身,可以保护对象避免不想要的访问,也可以避免在加载大对象的过程中GUI会挂起,或者隐藏主题在远程运行的事实。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值