设计模式之代理模式

还是那个糖果机,CEO想要获得对糖果机的远程监控,获取机器状态,库存等。(果断想到了RMI)

我们会把GumballMachine变成远程服务,提供一些可以被调用的方法,然后创建一个能和GumballMachine沟通的代理,这需要用到RMI。最后再结合监视系统,CEO就可以监视任何数量的远程糖果机了。

步骤:0、制作远程接口1、制作远程的实现2、利用rmic产生的stub和skeleton3、启动RMIregistry4、开始远程服务


服务器端的代码:

远程接口:

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


public interface GumballMachineRemote extends Remote{
	public int getCount() throws RemoteException;
	public String getLocation() throws RemoteException;
	public State getState() throws RemoteException;

}
State不是可序列的 所以要修改下:

import java.io.Serializable;


public interface State extends Serializable{
	public void insertQuarter();
	public void ejectQuarter();
	public void turnCrank();
	public void dispense();

}

这样子类中的State就可以在网上传播了。
但是我们不希望整个糖果机都被序列化,需要在GumballMachine实例变量前面加上transient关键字。

GumballMachine类实现GumballMachineRemote接口:

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



public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote{
	 State soldOutState;
	 State noQuarterState;
	 State hasQuarterState;
	 State soldState;
	 State winnerState;
	
	 String location;
	 
	State state=soldOutState;//跟踪当前状态,初始为 售罄
	int count=0;//追踪机器内的糖果数量
	
	public GumballMachine(String location,int count)throws RemoteException{
		soldOutState=new SoldOutState(this);
		noQuarterState=new NoQuarterState(this);
		hasQuarterState=new HasQuarterState(this);
		soldState=new SoldState(this);
		winnerState=new WinnerState(this);
		this.location=location;
		this.count=count;
		if(count>0)
			state=noQuarterState;//如果库存不为零,机器进入 没有25分钱状态
	}
	
	public String getLocation(){
		return 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;
	}
	
	public State getState(){
		return this.state;
	}
	
	void releaseBall(){
		System.out.println("A gumball comes rolling out the slot...");
		if(count!=0)
			count=count-1;
	}
	
	public State getHasQuarterState(){
		return hasQuarterState;
	}
	
	public int getCount(){
		return count;
	}
	
	public State getNoQuarterState(){
		return noQuarterState;
	}
	
	public State getSoldState(){
		return soldState;
	}
	
	public State getSoldOutState(){
		return soldOutState;
	}
	
	public State getWinnerState(){
		return winnerState;
	}
	


}

注册启动服务:

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


public class GumballMachineTestDrive {
	public static void main(String[]args){
		GumballMachineRemote gumballMachine=null;
		int count;
		Scanner in=new Scanner(System.in);
		int oo=in.nextInt();
		
		try{
			count=oo;
			gumballMachine=new GumballMachine("santafe.mightygumball.com", count);
			LocateRegistry.createRegistry(1099);
			Naming.rebind("gumballmachine", gumballMachine);
		}catch(Exception e){
			e.printStackTrace();
		}
	}

}

然后是GumballMonitor客户端:

import java.rmi.RemoteException;


public class GumballMonitor {
	GumballMachineRemote machine;
	
	public GumballMonitor(GumballMachineRemote machine){
		this.machine=machine;
	}
	
	public void report(){
		try{
			System.out.println("Gumball Machine: "+machine.getLocation());
			System.out.println("Current inventory: "+machine.getCount()+" gumballs");
			System.out.println("Current State: "+machine.getState());
		}catch(RemoteException e){
			e.printStackTrace();
		}
	}

}

监视器测试程序:

import java.rmi.Naming;


public class GumballMonitorTestDrive {
	public static void main(String[]args){
		String location="rmi://127.0.0.1/gumballmachine";
		GumballMonitor monitor=null;
		
		
			try{
				GumballMachineRemote machine=(GumballMachineRemote)Naming.lookup(location);
				monitor=new GumballMonitor(machine);
				//System.out.println(monitor);
			}catch(Exception e){
				e.printStackTrace();
			}

		
		
			monitor.report();

	}

}

输入数量是555的话

Gumball Machine: santafe.mightygumball.com
Current inventory: 555 gumballs
Current State: NoQuarterState

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

上面是远程代理,接下来是另一种代理:虚拟代理。

我们打算建立一个应用程序,用来展现你最喜欢的CD封面。你可以建立一个CD标题菜单,然后从网站的在线服务中取得CD封面的图。如果使用swing,可以创建一个Icon接口从网络上加载图像。唯一的问题是,下载可能需要一些时间,所以在等待图像加载的时候,应该显示sth...

我们设计一个ImageProxy,首先创建一个ImageIcon,然后开始从网络URL上加载图像,在加载的过程中,ImageProxy显示“CD封面加载中,请稍后。。。”当图像加载完成,ImageProxy把所有方法调用委托给真正的ImageIcon,这些方法包括paintIcon()、getWidth()、getHeight()。如果用户请求新的图像,我们就创建新的代理,重复这样的过程。

编写ImageProxy:

public class ImageProxy implements Icon{
	ImageIcon imageIcon;//真正的图像
	URL imageUrl;
	Thread retrievalThread;
	boolean retrieving=false;
	
	public ImageProxy(URL url){
		imageUrl=url;
	}
	//在图像加载完毕前,返回默认的宽和高,图像加载完毕后,转给imageIcon处理
	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);//如果有Icon,就画出自己
		}else {//否则显示加载中的消息
			g.drawString("Loading CD cover. please wait...", x+300, y+190);
			if(!retrieving){//如果还没有试着取出图像
				retrieving=true;//那么就开始取出图像
				
				retrievalThread=new Thread(new Runnable() {
					//我们不希望挂起整个用户界面,所以用另一个线程取出图像
					@Override
					public void run() {
						// TODO Auto-generated method stub
						try{
							imageIcon=new ImageIcon(imageUrl,"CD cover");
							//在线程中,我们实例化此icon对象,其构造器会在图像加载完成后才返回
							c.repaint();
						}catch(Exception e){
							e.printStackTrace();
						}
					}
				});
				retrievalThread.start();
			}
		}
	}
//可以用状态模式清理这些if语句
}

测试CD封面浏览器:

public class ImageProxyTestDrive {
	ImageComponent imageComponent;
	JFrame frame=new JFrame("CD cover viewer");
	JMenuBar menuBar;
	JMenu menu;
	Hashtable cds= new Hashtable<>();
	
	public static void main(String[]args)throws Exception{
		ImageProxyTestDrive testDrive=new ImageProxyTestDrive();
	}
	
	public ImageProxyTestDrive()throws Exception{
		cds.put("meinv1", "http://game.online.cq.cn/uploadfile/2012/0113/20120113104548732.jpg");
		cds.put("meinv2", "http://pic10.nipic.com/20101023/1304280_092014003671_2.jpg");
		cds.put("meinv3", "http://pic4.nipic.com/20091014/1295091_150512051562_2.jpg");
		cds.put("meinv4", "http://pic6.nipic.com/20100417/1304280_085257009323_2.jpg");
		cds.put("meinv5", "http://pic6.nipic.com/20100417/4713361_100503544816_2.jpg");
		cds.put("meinv6", "http://pic.4j4j.cn/upload/pic/20140331/e6b50c91eb.jpg");
		cds.put("meinv7", "http://iphone.images.paojiao.cn/iphone/paper/20116/30/74970434/paojiao_3a066a72.jpg");
		
		URL initialUrl=new URL((String)cds.get("meinv5"));
		menuBar=new JMenuBar();
		menu=new JMenu("Favorite CDs");
		menuBar.add(menu);
		frame.setJMenuBar(menuBar);
		
		for(Enumeration e=cds.keys();e.hasMoreElements();){
			String name=(String)e.nextElement();
			JMenuItem menuItem=new JMenuItem(name);
			menu.add(menuItem);
			menuItem.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent e) {
					// TODO Auto-generated method stub
					imageComponent.setIcon(new ImageProxy(getCDUrl(e.getActionCommand())));
					frame.repaint();
				}
			});
		}
		
		Icon icon=new ImageProxy(initialUrl);
		imageComponent=new ImageComponent(icon);
		frame.getContentPane().add(imageComponent);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		
	}
	
	URL getCDUrl(String name){
		try{
			return new URL((String)cds.get(name));
		}catch(MalformedURLException e){
			e.printStackTrace();
			return null;
		}
	}
	

}

ImageProxy有点像是装饰者,但是目的是不一样的,装饰者为对象增加行为,而代理是控制对象的访问。

上面我们是创建新的ImageProxy来取得图像,即使图像已经被取回来过。所以我们可以把加载过的图像放在缓存中,就是缓存代理。它会维护之前创建的对象,当收到请求时,在可能等情况下返回缓存对象。


下面使用JAVA API的代理,创建一个保护代理,其实就是动态代理。

约会服务系统中可以加入“hot”或者“not”的评鉴,涉及到一个PersonBean,允许设置或取得一个人的信息。

public interface PersonBean {
	void initialInfo(String name,String gender,String interests,int rating,int ratingCount);
	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 void initialInfo(String name,String gender,String interests,int rating,int ratingCount){
		this.name=name;
		this.gender=gender;
		this.interests=interests;
		this.rating=rating;
		this.ratingCount=ratingCount;
	}
	public String getName() {
		// TODO Auto-generated method stub
		return name;
	}
	public String getGender() {
		// TODO Auto-generated method stub
		return gender;
	}
	public String getInterests() {
		// TODO Auto-generated method stub
		return interests;
	}
	public int getHotOrNotRating() {
		// TODO Auto-generated method stub
		if(rating==0)
			return 0;
		return (rating/ratingCount);
	}
	public void setName(String name) {
		// TODO Auto-generated method stub
		this.name=name;
	}
	public void setGender(String gender) {
		// TODO Auto-generated method stub
		this.gender=gender;
	}
	public void setInterests(String interests) {
		// TODO Auto-generated method stub
		this.interests=interests;
	}
	public void setHotOrNotRating(int rating) {
		// TODO Auto-generated method stub
		this.rating+=rating;
		ratingCount++;
	}
}

为PersonBean创建动态代理:

首先创建两个InvocationHandler,创建动态代理,利用适当的代理包装任何PersonBean对象。

如果不是顾客自己(拥有者),就是另一个顾客正在检查的服务使用者(非拥有者)

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;
	}
	
	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().startsWith("in")) {
				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) {
				// TODO: handle exception
				e.printStackTrace();
			}
			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;
	}
	
	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().startsWith("in")) {
				throw new IllegalAccessException();
			}
			else if (method.getName().equals("setHotOrNotRating")) {
				return method.invoke(person, args);
			}else if (method.getName().startsWith("set")) {
				throw new IllegalAccessException();
			}
		}catch (InvocationTargetException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			return null;
		}

}

测试配对服务:

import java.lang.reflect.Proxy;

public class MatchMakingTestDrive {
	
	public static void main(String[]args){
		MatchMakingTestDrive test=new MatchMakingTestDrive();
		test.drive();
	}
	
	
	public void drive(){
		PersonBean joe=new PersonBeanImpl();
		PersonBean ownerProxy=(PersonBean)Proxy.newProxyInstance(joe.getClass().getClassLoader(), 
				joe.getClass().getInterfaces(),new OwnerInvocationHandler(joe));
		ownerProxy.initialInfo("joe", "male", "Reading book", 8, 1);
		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 nonPersonProxy=(PersonBean)Proxy.newProxyInstance(joe.getClass().getClassLoader(),
			joe.getClass().getInterfaces(),new NonOwnerInvocationHandler(joe));
	System.out.println("Name is "+nonPersonProxy.getName());
	try{
		nonPersonProxy.setInterests("bowling ,go");
	}catch(Exception e){
		System.out.println("can't see interests from non owner proxy");
	}
	nonPersonProxy.setHotOrNotRating(3);
	System.out.println("Rating set from non owner proxy");
	System.out.println("Rating is "+nonPersonProxy.getHotOrNotRating());
	
	}
}

执行结果:

Name is joe
Interests set From owner proxy
can't  set rating from owner proxy
Rating is 8
Name is joe
can't see interests from non owner proxy
Rating set from non owner proxy
Rating is 5

===================

还有其他代理:

防火墙代理

智能引用代理

缓存代理

同步代理

复杂隐藏代理

写入时复制代理

。。。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值