三、设计模式——代理模式

 代理模式:为一个对象提供一个替身,以控制这个对象的访问。被代理对象可以使远程对象、创建开销大的对象或需要安全控制的对象。

接下来介绍一个远程监控程序,通过代理模式如何实现监控功能。

这个例子是在本地监控多台远端的糖果机,要求获取他们的位置以及状态。通过RMI,建立本地与远程机器的连接作为代理。

通过这个代理,我们能够查看他们的状态,实现监控。

对RMI不熟悉的朋友,可以出门左转,我有一篇关于RMI简单介绍的博文。

163039_3Y32_258106.png

远程接口:

import java.rmi.Remote;
import java.rmi.RemoteException;

import com.java.jikexueyuan.agentmode.candymachine.State;

public interface CandyMachineRemote extends Remote{
	public String  getLocation() throws RemoteException;
	public int getCount() throws RemoteException;
	public State getstate() throws RemoteException;
}

在目标机器上运行的远程接口实现:

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

import com.java.jikexueyuan.agentmode.candymachinermi.CandyMachineRemote;

public class CandyMachine extends UnicastRemoteObject implements CandyMachineRemote{

	State mSoldOutState;
	State mOnReadyState;
	State mHasCoin;
	State mSoldState;
	State mWinnerState;
	private String location="";
	private State state;
	private int count = 0;

	public CandyMachine(String location,int count) throws RemoteException{
		this.location=location;
		this.count = count;
		mSoldOutState = new SoldOutState(this);
		mOnReadyState = new OnReadyState(this);
		mHasCoin = new HasCoin(this);
		mSoldState = new SoldState(this);
		mWinnerState = new WinnerState(this);
		if (count > 0) {
			state = mOnReadyState;
		} else {
			state = mSoldOutState;
		}
	}
	public String getLocation()
	{
		return location;
	}
	public void setState(State state) {
		this.state = state;
	}

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

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

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

	void releaseCandy() {

		// TODO Auto-generated method stub
		if (count > 0) {
			count = count - 1;
			System.out.println("a candy rolling out!");
		}

	}

	public int getCount() {
		return count;
	}

	public void printstate() {
		state.printstate();
	}
	public State getstate() {
		return state;
	}
}

在远端机器上注册RMI服务,并且启动:

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

import com.java.jikexueyuan.agentmode.candymachine.CandyMachine;
import com.java.jikexueyuan.agentmode.rmi.MyRemote;
import com.java.jikexueyuan.agentmode.rmi.MyRemoteImpl;

public class RemoteMainTest {
	public static void main(String[] args) {

		try {
			CandyMachine service = new CandyMachine("test1", 7);
			// LocateRegistry.createRegistry(6602);
			Naming.rebind("rmi://127.0.0.1:6602/test1", service);
			service.insertCoin();
			service = new CandyMachine("test2", 5);
			Naming.rebind("rmi://127.0.0.1:6602/test2", service);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(e.toString());
		}

	}
}

监控类:我们可以看到,监控类通过数组维持了对所有目标机器的引用,通过循环获取所有机器的状态

import com.java.jikexueyuan.agentmode.candymachine.CandyMachine;

public class Monitor {

	private ArrayList<CandyMachineRemote> candyMachinelst;

	public Monitor() {
		candyMachinelst = new ArrayList<CandyMachineRemote>();
	}

	public void addMachine(CandyMachineRemote mCandyMachine) {
		candyMachinelst.add(mCandyMachine);
	}

	public void report() {
		CandyMachineRemote mCandyMachine;
		for (int i = 0, len = candyMachinelst.size(); i < len; i++) {
			mCandyMachine = candyMachinelst.get(i);
			try {
				System.out
						.println("Machine Loc:" + mCandyMachine.getLocation());

				System.out.println("Machine Candy count:"
						+ mCandyMachine.getCount());
				System.out.println("Machine State:"
						+ mCandyMachine.getstate().getstatename());
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}

测试类:建立和所有机器的连接,传给监控类,调用report函数获取监控数据

import java.rmi.Naming;

import com.java.jikexueyuan.agentmode.candymachine.CandyMachine;
import com.java.jikexueyuan.agentmode.rmi.MyRemote;

public class MainTest {

	public static void main(String[] args) {
		Monitor mMonitor = new Monitor();

		try {
			CandyMachineRemote mCandyMachine = (CandyMachineRemote) Naming
					.lookup("rmi://127.0.0.1:6602/test1");
			mMonitor.addMachine(mCandyMachine);
			mCandyMachine = (CandyMachineRemote) Naming
					.lookup("rmi://127.0.0.1:6602/test2");
			mMonitor.addMachine(mCandyMachine);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		mMonitor.report();
	}

}

华丽的分割线


上面的介绍太过简单,怎么对得起正在看本文的客官呢,下面介绍更多干货。

一、虚拟代理 

为创建开销大的对象提供代理服务。举个栗子,用过hibernate的童鞋应该都知道,session.load方法提供的懒加载功能就是这种。当真正使用的时候,该方法返回的对象只不过是一个代理而没有数据。

还有,Android异步加载网络图片的时候的image,其实也是虚拟代理。

二、动态代理

关于动态代理,其实我们并不陌生,Spring的重要概念AOP就是通过Java的动态代理来实现的。

这里有两个重要的组件必须介绍,分别是Proxy类和InnovacationHandler接口。


举个栗子,加入我们要开发一个类似facebook一样给别人评分的功能。规则是自己能够设置性别、名字、爱好等信息,但是不能给自己打分;而其他人能够给自己打分,但是不能篡改自己性别、姓名等信息。

个人信息类如下:

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;
	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return name;
	}

	@Override
	public String getGender() {
		// TODO Auto-generated method stub
		return gender;
	}

	@Override
	public String getInterests() {
		// TODO Auto-generated method stub
		return interests;
	}

	@Override
	public int getHotOrNotRating() {
		// TODO Auto-generated method stub
		if(ratingcount==0) 		return 0;
		return (rating/ratingcount);
	}

	@Override
	public void setName(String name) {
		// TODO Auto-generated method stub
		this.name=name;
	}

	@Override
	public void setGender(String gender) {
		// TODO Auto-generated method stub
		this.gender=gender;
	}

	@Override
	public void setInterests(String interests) {
		// TODO Auto-generated method stub
		this.interests=interests;
	}

	@Override
	public void setHotOrNotRating(int rating) {
		// TODO Auto-generated method stub
		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 {
		// TODO Auto-generated method stub

		if(method.getName().startsWith("get"))
		{
			return method.invoke(person,args);
		}else if(method.getName().equals("setHotOrNotRating"))
		{
			return new IllegalAccessException();
		}else if(method.getName().startsWith("set"))
		{
			return method.invoke(person,args);
		}
		
		return null;
	}

}

对于其他人

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class NonOwnerInvocationHandler implements InvocationHandler{
	PersonBean person;
	public NonOwnerInvocationHandler(PersonBean person)
	{
		this.person=person;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub

		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"))
		{
			return new IllegalAccessException();
		}
		
		return null;
	}

}


我们仔细分析下面代码,通过getOwnerProxy方法和getNonOwnerProxy两个方法,返回不同的代理。

前者能够给自己设置性别、名字、兴趣,但是不能修改评分。

而后者相反。通过代理,使不同身份的用户访问得到控制,达到信息安全的目的。是不是很神奇~~~

InvocationHandler用来定义哪些功能能访问,哪些不能访问。而Proxy跟InvocationHandler合作,返回代理。

import java.lang.reflect.Proxy;

public class MatchService {
	public MatchService() {

		PersonBean joe = getPersonInfo("joe", "male", "running");

		PersonBean OwnerProxy = getOwnerProxy(joe);

		System.out.println("Name is " + OwnerProxy.getName());
		System.out.println("Interests is " + OwnerProxy.getInterests());

		OwnerProxy.setInterests("Bowling");
		System.out.println("Interests are " + OwnerProxy.getInterests());
		OwnerProxy.setHotOrNotRating(50);
		System.out.println("Rating is " + OwnerProxy.getHotOrNotRating());
		OwnerProxy.setHotOrNotRating(40);
		System.out.println("Rating is " + OwnerProxy.getHotOrNotRating());

		System.out.println("**************");

		PersonBean nonOwnerProxy = getNonOwnerProxy(joe);
		System.out.println("Name is " + nonOwnerProxy.getName());
		System.out.println("Interests are " + nonOwnerProxy.getInterests());
		nonOwnerProxy.setInterests("haha");
		System.out.println("Interests are " + nonOwnerProxy.getInterests());
		nonOwnerProxy.setHotOrNotRating(60);
		System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());

	}

	PersonBean getPersonInfo(String name, String gender, String interests) {
		PersonBean person = new PersonBeanImpl();
		person.setName(name);
		person.setGender(gender);
		person.setInterests(interests);
		return person;
	}

	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));
	}
}


三、缓存代理

通俗来讲,比如手机第一次访问网络上的图片后,缓存起来,下次访问的时候先从本地读取。


四、防火墙代理

在局域网的机器通过访问防火墙的形式,间接访问互联网。



不怎么华丽的分割线


代理模式和装饰模式的区别

代理模式不增加新功能,是对已有功能的访问控制

装饰模式增加新功能,对已有对象的扩张。

转载于:https://my.oschina.net/gaohongtian/blog/491927

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值