设计模式
代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以使远程的对象(远程代理)、创建开销大的对象(虚拟代理),或需要安全控制的对象(保护代理)。
远程代理:可以作为另一个JVM上对象的本地代表。调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果返回给客户。
虚拟代理:作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后代理就会将请求直接委托给对象。
动态代理:java在java.lang.reflect包中有自己的代理支持,利用这个包你可以在运行时动态的创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。实际的代理类是在运行时创建的,在Java中我们称这种技术为动态代理。利用Java的动态代理,可以实现保护代理。
防火墙代理:控制网络资源的访问,保护主题免于“坏客户”的侵害。
智能引用代理:当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数。
缓存代理:为开销大的运算结果提供暂时存储,它也运行多个客户共享结果,以减少计算或网络延迟。
同步代理:多线程的情况下为主题提供安全的访问。
复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也成为外观代理。复杂隐藏代理和外观模式不一样,因为代理控制访问,而外观模式只是提供另一组接口。
写入时复制代理:用来控制对象的复制,方法是延迟对象的复制,知道客户真正需要为止,这是虚拟代理的变体。
制作远程服务
(1)制作远程接口
(2)制作远程实现
(3)利用rmic产生stub和skeleton
(4)启动RMI registry
(5)开始远程服务
设计原则
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
为交互对象之间的送耦合设计而努力
类应该对扩展开发,对修改关闭
依赖抽象,而不依赖具体类
只和朋友交谈
别找我,我会找你
类应该只有一个改变的理由
要点
代理在结构上类似装饰者,但是目的不一样。装饰者模式为对象加上行为,而代理则是控制行为。
和其他包装者一样,代理会造成你的设计中类的数目增加。
远程代理
import java.rmi.Remote;//用来做rmiregistry lookup的naming类在java.rmi包中
import java.rmi.RemoteException;
//extends Remote这表示此接口要用来支持远程调用
public interface GumballMachineRemote extends Remote {
//准备支持的方法,每个都要抛出RemoteException
//因为每次远程方法调用都必须考虑成“有风险的”
public int getCount() throws RemoteException;
public String getLocation() throws RemoteException;
//返回值将从服务器经过网络运回客户,所以必须是原语类型或可序列化类型
public State getState() throws RemoteException;
}
//扩展Serializable接口,使得State可序列化
public interface State extends Serializable {
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
public class HasQuarterState implements State {
private static final long serialVersionUID = 768887299984514010L;
Random randomWinner = new Random(System.currentTimeMillis());
//对于State的每个市县,我们都在GumballMachine实例变量前面加上transient关键字,这样就可以高考JVM不要序列化这个字段
transient GumballMachine gumballMachine;
//其他方法
}
//GumballMachine 要继承UnicastRemoteObject成为一个远程服务
//GumballMachine 也需要实现GumballMachineRemote这个远程接口
public class GumballMachine extends UnicastRemoteObject implements
GumballMachineRemote {
/**
*
*/
private static final long serialVersionUID = -2838970117227273571L;
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState;
int count = 0;
String location;
//构造器需要抛出RemoteException,因为超类是这样做的
public GumballMachine(String location, int numberGumballs)
throws RemoteException {
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;
}
//其他方法
}
//在RMI register中注册
public class GumballMachineTestDrive {
public static void main(String[] args) {
GumballMachineRemote gumballMachine = null;
int count;
if (args.length < 2) {
System.out.println("GumballMachine <name> <inventory>");
System.exit(1);
}
try {//需要在实例化代码前加上try/catch,因为我们的构造器可能会抛出异常
count = Integer.parseInt(args[1]);
gumballMachine = new GumballMachine(args[0], count);
//我们也添加上对Naming.rebind的调用,用gumballmachine的名字发布GumballMachine的stub
Naming.rebind("//" + args[0] + "/gumballmachine", gumballMachine);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//GumballMonitor客户端
public class GumballMonitor {
//依赖此GumballMachineRemote远程接口,而不是具体的类
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();
}
}
}
//监视测试程序
public class GumballMonitorTestDrive {
public static void main(String[] args) {
String[] location = { "rmi://santafe.mightygumball.com/gumballmachine",
"rmi://boulder.mightygumball.com/gumballmachine",
"rmi://seattle.mightygumball.com/gumballmachine" };
GumballMonitor[] monitor = new GumballMonitor[location.length];
for (int i = 0; i < location.length; i++) {
try {
//为每个远程机器创建一个代理,客户从Register中寻找代理,也就是stub对象
GumballMachineRemote machine = (GumballMachineRemote) Naming
.lookup(location[i]);
monitor[i] = new GumballMonitor(machine);
System.out.println(monitor[i]);
} catch (Exception e) {
e.printStackTrace();
}
}
//遍历每台机器,打印报告
for (int i = 0; i < monitor.length; i++) {
monitor[i].report();
}
}
}
虚拟代理
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 = new ImageIcon(imageURL, "CD Cover");
//当图像准备好后,我们告诉Swing需要重绘
c.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});
retrievalThread.start();
}
}
}
}
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 {
//建立框架和菜单
//创建一个图像代理,并指定初始URL
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;
}
}
}