Fork me on GitHub

设计模式之代理模式

设计模式之代理模式

1. 什么是代理模式

Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问。

所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。

代理模式为一个对象提供一个替身,以控制对这个对象的访问被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象代理模式有很多变体,都是为了控制与管理对象访问

代理模式的一般结构:

1

subject(抽象主题角色):真实主题与代理主题的共同接口。

RealSubject(真实主题角色):定义了代理角色所代表的真实对象。

Proxy(代理主题角色):含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真是主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象。

动态代理:

  1. InvocationHandler 接口
  2. invoke方法
  3. Proxy.newProxyInstance();

代理模式和装饰者模式差异:装饰者模式,装饰以后会添加新功能,而代理模式目的是对目标对象访问的控制和管理。

2. 具体实例

对于在状态模式中介绍的糖果机项目,我们现在要添加一个监控糖果机:监控糖果机的地点、糖果库存和当前状态。这时候我们使用远程代理的模式来设计这个项目是合适的。通过代理模式获取远程对象(糖果机)的本地代表,通过它可以让远程对象当本地对象来调用。这样就相当于在本地查看糖果机的状态了。

可以使用JAVA的RMI来实现远程代理:

RMI的结构图:

2

RMI远程方法调用是计算机之间通过网络实现对象调用的一种通讯机制。使用这种机制,一台计算机上的对象可以调用另外 一台计算机上的对象来获取远程数据。

在过去,TCP/IP通讯是远程通讯的主要手段,面向过程的开发。而RPC使程序员更容易地调用远程程序,但在面对复杂的信息传讯时,RPC依然未能很好的支持。

RMI被设计成一种面向对象开发方式,允许程序员使用远程对象来实现通信。

RMI的实现步骤:

  1. 制作远程接口:接口文件
  2. 远程接口的实现:Service文件
  3. RMI服务端注册,开启服务
  4. RMI代理端通过RMI查询到服务端,建立联系,通过接口调用远程方法

通过上面的步骤,实现一个简单的JavaRMI 的实例:

远程接口:

1
2
3
4
5
public interface MyRemote extends Remote{

public String sayHello() throws RemoteException;

}

接口的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{

protected MyRemoteImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}

@Override
public String sayHello() throws RemoteException {
// TODO Auto-generated method stub
return "Hello World!";
}
public static void main(String[] args) {

try {
MyRemote service=new MyRemoteImpl();
// LocateRegistry.createRegistry(6600);
Naming.rebind("rmi://127.0.0.1:6600/RemoteHello", service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println( e.toString());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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:6600/RemoteHello");
String s=service.sayHello();
System.out.println(s);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

然后通过使用RMI怎么来实现上面所讲的糖果机的项目呢?具体代码如下:

接口:

1
2
3
4
5
public interface CandyMachineRemote extends Remote{
public String getLocation() throws RemoteException;
public int getCount() throws RemoteException;
public State getstate() throws RemoteException;
}

接口的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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;
}
}

注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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();
}
}

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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());
}

}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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();
}

}

3. 几种其他常见的代理模式

  • 虚拟代理: 虚拟代理为创建开销大的对象提供代理服务。真正的对象在创建前和创建中时,由虚拟代理来扮演替身。例如手机加载图片,一开始是个模糊的,加载完成才显示清楚,开始就就是本地代理的显示。
  • 动态代理:运行时动态的创建代理类,并将方法调用转发到指定类
  • 保护代理:这种代理用于对真实对象的功能做一些访问限制, 在代理层做身份验证. 通过了验证, 才调用真实的主体对象的相应方法.
  • 几种变体:、防火墙代理、缓存代理、智能引用代理、同步代理、写入时复制代理

关于保护代理和动态代理的使用举一个简单的例子:

一个相亲网的项目,每个人都有自己的信息和评分,评分别人可以评价自己不能评价,个人信息自己可以修改别人只能查看。

具体的代码:

接口:

1
2
3
4
5
6
7
8
9
10
11
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);
}

接口的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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++;
}
}

非本人代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;
}

}

本人代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
}

}

调用代理方法的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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));
}
}

测试类:

1
2
3
4
5
6
public class MainTest {

public static void main(String[] args) {
MatchService mMatchService=new MatchService();
}
}

本文标题:设计模式之代理模式

文章作者:WilsonSong

发布时间:2018年10月18日 - 14:10

最后更新:2018年10月18日 - 14:10

原始链接:https://songwell1024.github.io/2018/10/18/ProxyMode/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------