Head First 之 代理模式(一)

读<Head First>的时候,觉得这个书给人的感觉太好了,每一个设计模式都栩栩如生,这么好的书,光看不练习就浪费了,但是思来想去,似乎整理成博客,这样更好!就代理模式而言,书整整用了70页来书名代理模式,例子简直生动的不得了!希望我能用几篇博客,来将此思想整理出来,供大家,我自己来学习,当然还是希望大家能够买这个正版的书来看,O'REILLY的书一般都不会让人失望!

此处的代理模式包括,远程过程调用(RPC),动态代理等,所以这个系列的文章,有点儿长,故事的背景是一个糖果公司的CEO,一直不断的提需求,不断的找茬,看看聪明的程序员是怎样一点点收拾他的吧...


CEO的需求一 :希望我的糖果机能够获得更好的监控,你能找到一个办法给我一份库存以及机器的状态的报告么?


首先,定义一台水果机:

/**
 * 糖果机
 * Created by gzd on 2016/12/27.
 */
public class GumballMachine {

    //位置
    private String location;

    //库存
    private Integer count;

    //状态
    private String state;

    public GumballMachine(String location,Integer count) {
        this.location = location;
        this.count = count;
    }

    public String getLocation() {
        return location;
    }

    public GumballMachine setLocation(String location) {
        this.location = location;
        return this;
    }

    public Integer getInventory() {
        return count;
    }

    public GumballMachine setInventory(Integer inventory) {
        this.count = inventory;
        return this;
    }

    public String getState() {
        return state;
    }

    public GumballMachine setState(String state) {
        this.state = state;
        return this;
    }
}
然后在定义一台糖果监视器,并提供report(方法)

/**
 * 糖果监视器
 * Created by gzd on 2016/12/27.
 */
public class GumballMonitor {

    private GumballMachine gumballMachine;

    public GumballMonitor(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    public void report(){
        System.out.println("糖果机位置: " + gumballMachine.getLocation());
        System.out.println("目前库存 : " + gumballMachine.getInventory());
        System.out.println("当前状态 : " + gumballMachine.getState());
    }

}
最后,CEO来验收,给他一个操作器,,他可以随时查看结果

public class GumballMachineTestDrive {
    /**
     * 输出结果:
     * 糖果机位置: 东直门
     * 目前库存 : 500
     * 当前状态 : 售卖中...
     * @param args
     */
    public static void main(String[] args) {
        GumballMachine machine = new GumballMachine("东直门", 500)
                .setState("售卖中...");
        GumballMonitor gumballMonitor = new GumballMonitor(machine);

        gumballMonitor.report();
    }

}



CEO看了这个说:这个机器的输出看起来不错,但是可能是我之前说的不清楚,我需要的是远程监控糖果机!其实我们已经把网络准备好了,但是你们在瞎忙什么?

这样,也就是CEO的需求二: 可以远程查看结果!

程序员开始讨论:

A:你说远程什么?

B:远程代理.你想想:我们已经写好监视器的代码,对吧?我们给GumballMonitor一个糖果机的引用,他给我们一份报告,问题在于监视器和糖果机在一起呢,也就是在一个jvm上执行,但是CEO希望在他的办公室就可以远程监控这些机器!所以,咱们不要变化GumballMonitor不要将糖果机教给GumballMonitor,而是将一个远程代理对象交给他!

A:我不太懂

B:其实所谓的代理(proxy),就是代表某个真实的对象,这个案例中,代理其实就是一个糖果机对象,但其实幕后是它利用网络和一个远程的糖果机进行沟通!

A:你是说,不该我们的代码,只是将GumballMachine代理版本教给监视器就可以了...然后这个代理假装他是真正的对象,但是其实一切的动作是它利用网络和真正的对象沟通!

B:差不多是这样的!

A:这好像说的比做的容易哦!

B:或许吧!但是我不认为有这么难,我们必须确定糖果机能够通过网络接收请求并提供服务;我们也需要让监视器有办法获得代理对象的引用,这方面,幸好java已经有了一些很棒的内置工具(RMI)可以帮我们,先看看远程代理....


远程代理就好比"远程对象的本地代表",何谓远程对象?这是一种对象,活在不同的java虚拟机堆中,何谓本地代表?这是一种可以由本地方法调用的对象,其行为会转发到远程对象中!

做之前,你需要了解,1,java的RMI;2如果把糖果机变成远程服务,提供一些可以被远程调用的方法;3创建一个能和远程糖果机沟通的代理,最后结合监视系统,CEO就可以看结果了,


用RMI远程服务,第一步,提供一个远程接口:

/**
 * 必须继承Remote接口,提供一组可以远程调用的方法
 * 确定所有方法的返回值,都是可序列化的
 * Created by gzd on 2016/12/27.
 */
public interface GumballMachineRomote extends Remote {

    int getCount() throws RemoteException;

    String getLocation() throws RemoteException;

    State getState() throws RemoteException;

}
State的代码如下:

/**
 * Created by gzd on 2016/12/27.
 */
public interface State extends Serializable{

    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
}
todo state实现类


在前面我们已经做好了GumballMachine类,但是需要让他变成服务用,并且处理来自网络的请求,为了做这个,我们得让她实现上面的GumballMachineRomote接口.

/**
 * 糖果机,可以处理远程任务
 * Created by gzd on 2016/12/27.
 */
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRomote{

    //位置
    private String location;

    //库存
    private Integer count;

    //状态
    private State state;

    public GumballMachine(String location,Integer count) throws RemoteException {
        this.location = location;
        this.count = count;
    }

    @Override
    public int getCount() throws RemoteException {
        return count;
    }

    public String getLocation() {
        return location;
    }

    public GumballMachine setLocation(String location) {
        this.location = location;
        return this;
    }

    public Integer getInventory() {
        return count;
    }

    public GumballMachine setInventory(Integer inventory) {
        this.count = inventory;
        return this;
    }

    public State getState() {
        return state;
    }

    public GumballMachine setState(State state) {
        this.state = state;
        return this;
    }
}
糖果机上面的服务已经OK,现在,我们要将他装上去接受请求,首先,我们要确保他注册到RMI registry中,好让客户端找到他.GumballMachineTestDrive的代码要修改一下:

public class GumballMachineTestDrive {
    
    public static void main(String[] args) throws AlreadyBoundException, MalformedURLException {
        GumballMachineRemote machine = null;
        try {
            int count = 500;
            String location = "192.168.31.149";
            machine = new GumballMachine(location, count);
            Naming.rebind("//" + location +"/gumballMachine",machine);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

}
下面开始测试,首先启动rmiregistry(去$JAVA_HOME/bin里找到他启动),然后启动我们的drive去注册!


至于GumballMonitor,也要做一下改动

/**
 * 糖果监视器
 * Created by gzd on 2016/12/27.
 */
public class GumballMonitor {

    private GumballMachineRemote gumballMachine;

    public GumballMonitor(GumballMachineRemote gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    public void report(){
        try {
            System.out.println("糖果机位置: " + gumballMachine.getLocation());
            System.out.println("目前库存 : " + gumballMachine.getCount());
            System.out.println("当前状态 : " + gumballMachine.getState());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
稍加改动drive,让CEO监视糖果机用:

public class GumballMachineTestDrive {

    public static void main(String[] args) throws AlreadyBoundException, MalformedURLException, NotBoundException {
        //创建一个机器的数组,条件有限,就写一个吧,假装他是多个,,,
        String[] location = {"rmi://192.168.31.149/gumballMachine"};
        //创建多个监视器
        GumballMonitor[] monitor = new GumballMonitor[location.length];
        for (int i = 0; i < location.length; i++) {
            try {
                GumballMachineRemote machine = (GumballMachineRemote) Naming.lookup(location[i]);
                monitor[i] = new GumballMonitor(machine);
                System.out.println(monitor[i]);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            for (int j = 0; j < monitor.length; j++) {
                monitor[j].report();
            }
        }
    }

}

好吧,,,就先到这里State类可以随便弄,,,嫌麻烦定义成String也可以,免去定义对象的麻烦,下一部分内容,将会介绍另一种代理,,,,,,动态代理,,,,



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值