代理模式:指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
一.代理模式的步骤:
-
在功能类也就是想要做事情的类中(中介)中创建一个接口:明确接口中的方法,即我们想要代理的对象做什么事情,例如让中介找房子给我们;
-
在功能类(中介)创建一个代理者变量(接口类型的变量),用于保存实际执行对象,例如房产公司;
-
在实现类也就是具体做事情的类中中实现该接口,即真正做事情的,例如让房产公司找到符合的房子;
-
设置代理者是我们的实现类的对象,因为实现类实现了接口,所以代理者可以接受实现类对象,例如中介保存了相关的房产公司;
-
在功能中添加一个方法用于调用代理者(也就是我们的实现类对象)实现代理方法,例如中介找房产公司找房子
-
租房子的人去调用中介,让中介帮它找房子。
//中介类
package swu.xl.day7.AgentHouse;
public class Agent {
String name;
public Agent(String name){
this.name = name;
}
//保存代理者
GetHouseMessage delegate;
//让代理者返回信息给我
public String letDelegateDo(){
return delegate.messageOfHuse();
}
//协议
interface GetHouseMessage{
//返回房子信息
String messageOfHuse();
}
}
//房产公司类
package swu.xl.day7.AgentHouse;
public class HouseManager implements Agent.GetHouseMessage {
String name;
public HouseManager(String name){
this.name = name;
}
@Override
public String messageOfHuse() {
//房产公司做一系列事情
//***
return "西南大学D栋412";
}
}
//人类
package swu.xl.day7.AgentHouse;
public class Person {
String name;
public Person(String name){
this.name = name;
}
public void wantHouse(String houseMessage){
//得到房子了
System.out.println(name+"的房子是:"+houseMessage);
}
}
//模拟过程
package swu.xl.day7.AgentHouse;
public class Process {
public static void main(String[] args){
//有一个人小王
Person xw = new Person("小王");
//有一个中介
Agent agent = new Agent("租房子中介");
//有一个房子管理者
HouseManager houseManager = new HouseManager("文一地产");
//中介找到真正做事的
agent.delegate = houseManager;
//人发起租房子的行为
xw.wantHouse(agent.letDelegateDo());
}
}
这里面还可以有一层代理:人和中介的代理,人有各种联系方式。中介提供规定的联系方式,告诉人找到房子的信息。就需要人统一接受信息的方式,怎么做到的呢;中介提供一个接口用于统一联系方式,人只需要服从这个接口就可以接收到回调的信息。
package swu.xl.day11;
//Person类
//线程中:Agent类
public class AllLineInterfaceToBlockTest {
public static void main(String[] args) {
Person person = new Person();
person.name = "小王";
person.needHouse();
}
}
class Person implements Agent.AgentInterface {
String name;
public void needHouse(){
System.out.println(Thread.currentThread().getName());
Agent agent = new Agent();
agent.start();
agent.target = this;
}
@Override
public void callback(String desc) {
System.out.println(name+"的房子:"+desc);
}
}
class Agent extends Thread{
AgentInterface target;
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println("开始找房子");
System.out.println("---------");
System.out.println("房子找到了,返回数据");
target.callback("西南大学");
super.run();
}
public interface AgentInterface{
//统一通过打电话的方式告诉人房子的信息
void callback(String desc);
}
}
二.为什么需要代理模式
-
客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
-
例如:人想去租房子,人并没有租房子的功能,人只是想要租房子;所以人需要找一个中介做代理帮它找房子。
-
个人理解:某A类想要完成一个功能,但是该功能在B类中可以十分简单的实现;所以我们将该功能交给B类代理。
-
下面的例子:按钮添加到界面上,按钮点击想要界面做出相应改变;界面最知道自己该怎么改变,所以将该按钮的功能交给界面来做。
三.优/缺点
优点:
- 代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度。
- 代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。
- 代理对象也可以对目标对象调用之前进行其他操作。
缺点:
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
- 增加了系统的复杂度。
四.代理模式使用实例
- 模拟在主界面中添加一个按钮,点击按钮触发相应事件;
- 主界面类继承于界面类(抽象类),Button继承于View;
- 模拟Button添加到主界面上,点击按钮,界面做出相应改变;
- 模拟ImageView添加到主界面上,点击视图,界面做出相应改变。
1.构建一个抽象类作为所有界面的模板,该模板具有两个抽象方法,创建界面和销毁界面;定义一个ArrayList
保存添加到该界面的视图;定义一个方法用于添加视图到该界面。
package swu.xl.day6.Teacher;
import java.util.ArrayList;
/**
* 管理界面的抽象类 定义一个创建界面到结束的模板
*/
public abstract class Activity {
//保存这个界面的所有子视图
public ArrayList<View> children = new ArrayList<>();
//界面创建
public abstract void onCreate();
//界面销毁
public abstract void onDestroy();
//将子控件添加到界面上
//接受的时候用父类接受子类
//当需要访问子类内容的时候,就需要强制转化为对应的类
public void addChild(View view){
children.add(view);
if (view instanceof Button){
Button button = (Button)view;
System.out.println(button.title+"按钮被添加到界面上");
}else {
ImageView imageView = (ImageView)view;
System.out.println(imageView.picture+"图片被添加到界面上");
}
}
}
2.构建一个View
类,拥有一些控件共有的一些属性和方法。该View
类想要完成onClick()功能,但是View
类很难完成onClick()
功能。因为该功能需要主界面来做,所以在View
类中写一个Interface
,定义好我们想要实现的功能,定义好一个Interface
类型的变量用于保存实现类对象。最后定义一个触发事件用于调用实现类对象调用代理方法
package swu.xl.day6.Teacher;
/**
* 管理视图的显示和事件监听
* 什么情况下需要讲一个父类做成抽象类(模板)
* 1.不能直接创建这个类的对象
* 这个类里面的某些方法还不确定如何实现
*/
public class View {
//所有的视图都共有的属性
String backgroundColor;
String borderColor;
//记录谁先想监听我这个事件
//暂时不确定是谁要监听
//但是要监听的人肯定实现了这个接口
OnClickeListener listener;
//所有的视图都需要监听事件
//定义内部接口,封装
public interface OnClickeListener{
//定义一套规范/方法,约束外部使用这些方法来监听事件
void onClick(View view);
}
//用于控件接受触摸事件
public void getTouchEvent(){
//调用监听者里面的onClick方法
listener.onClick(this);
}
}
3.定义一个继承于View
的Button
和ImageView
package swu.xl.day6.Teacher;
/**
* 创建按钮
*/
public class Button extends View {
String title;
String titleColor;
public Button(String title,String titleColor,String backgroundColor,String borderColor){
this.title = title;
this.titleColor = titleColor;
this.backgroundColor = backgroundColor;
this.borderColor = borderColor;
}
}
package swu.xl.day6.Teacher;
/**
* 创建图片
*/
public class ImageView extends View {
String picture;
public ImageView(String picture,String backgroundColor,String borderColor){
this.picture = picture;
this.backgroundColor = backgroundColor;
this.borderColor = borderColor;
}
}
4.定义一个继承于Activity
的,实现View.OnClickeListener
接口的MainActivity
类,在其中实现了接口的方法;在构造方法里面调用onCreate
方法用于添加Button
和ImageView
;Button
将其代理者设置为this-MainActivity对象;而ImageView
是通过匿名类作为代理者的方式;MainActivity
类里面也提供了模拟屏幕触摸的方法。
package swu.xl.day6.Teacher;
/**
* 主界面(程序运行起来的第一个界面)
*/
public class MainActivity extends Activity implements View.OnClickeListener {
Button button;
ImageView imageView;
//当界面被创建,就自动调用onCreate();
public MainActivity(){
onCreate();
}
@Override
public void onCreate() {
//主界面如何布局
//添加一个按钮
button = new Button("分享","红色","","");
//添加一张图片
imageView = new ImageView("动漫","","");
//将创建的控价添加到界面上
addChild(button);
addChild(imageView);
//如果一个控件需要监听事件,那么就必须实现监听事件的接口
//告诉按钮谁在监听这个事件
button.listener = this;
//给imageView添加一个事件监听,匿名对象
imageView.listener = new View.OnClickeListener() {
@Override
public void onClick(View view) {
System.out.println("图片被点击了");
}
};
}
//模拟触摸
public void touch(){
//将屏幕触摸的事件传递给按钮
button.getTouchEvent();
imageView.getTouchEvent();
}
@Override
public void onDestroy() {
//销毁之前需要做点什么事情
}
//当事件触发了,就会调用这个onClick方法
@Override
public void onClick(View view) {
System.out.println("按钮被点击了");
}
}
5.在main函数里面模拟
package swu.xl.day6.Teacher;
public class MyClass {
public static void main(String[] args){
//启动程序 创建界面
MainActivity main = new MainActivity();
//模拟触摸
main.touch();
}
}
//运行结果
分享按钮被添加到界面上
动漫图片被添加到界面上
按钮被点击了
图片被点击了
五.解析不使用上面的实例的原因
- 模拟浏览界面和聊天界面,两者都需要设置字体颜色和大小
- 设置界面改变两者字体颜色和大小,则需要保存两者的对象,通过相应的对象来设置对应页面的字体颜色和大小
//阅读界面
package swu.xl.day7.NoInterface;
/**
* 阅读界面,展示显示的文本
*/
public class ReadView {
private String text;
private String color;
private int font;
public ReadView(String text){
this.text = text;
}
//提供给外部一个方法 可以通过这个方法给我传值
public void change(String color,int font){
System.out.println("改变前的颜色:"+this.color+" "+"改变前的颜色:"+this.font);
this.color = color;
this.font = font;
System.out.println("改变后的颜色:"+this.color+" "+"改变后的颜色:"+this.font);
}
}
//聊天界面
package swu.xl.day7.NoInterface;
public class ChatView {
private String message;
private String color;
private int font;
public ChatView(String message){
this.message = message;
}
//提供给外部一个方法 可以通过这个方法给我传值
public void change(String color,int font){
System.out.println("改变前的颜色:"+this.color+" "+"改变前的颜色:"+this.font);
this.color = color;
this.font = font;
System.out.println("改变后的颜色:"+this.color+" "+"改变后的颜色:"+this.font);
}
}
//设置界面
package swu.xl.day7.NoInterface;
/**
* 设置页面 设置字体大小和颜色
*/
public class SetView {
//记录为谁设置颜色和大小
//记录下我做完事情之后 将数据返回给谁
ReadView readView;
ChatView chatView;
//开始设置
public void startSetRead(){
System.out.println("开始设置");
System.out.println("........");
System.out.println("设置完毕 即将返回结果");
//如果有可以访问属性,直接通过属性给值
//比较少用 对方没办法第一时间知道自己需要的值
//通过方法来回调
readView.change("黑色",30);
}
public void startSetChat(){
System.out.println("开始设置");
System.out.println("........");
System.out.println("设置完毕 即将返回结果");
//通过方法来回调
chatView.change("白色",50);
}
}
//模拟过程
package swu.xl.day7.NoInterface;
public class Main {
public static void main(String[] args){
//浏览界面
ReadView readView = new ReadView("test");
//进入到设置页面
SetView setView = new SetView();
setView.readView = readView;
setView.startSetRead();
//聊天界面
ChatView chatView = new ChatView("message");
//进入到设置页面
setView.chatView = chatView;
setView.startSetChat();
}
}
- 在上面的过程中我们可以看到,需要更改多少个界面就需要在设置界面里面添加多少个对象来保存对应界面对象,就需要添加多少个对应的方法来实现具体的步骤。
- 关键的问题在于每个界面无法用统一的变量去接受,但是我们一旦在设置界面添加了一套接口,界面实现相应接口,这些界面就可以用相同的接口对象来接受。
//阅读界面
package swu.xl.day7.Interface;
public class ReadView implements SetView.SetInterface {
private String text;
private String color;
private int font;
public ReadView(String text){
this.text = text;
}
@Override
public void change(String color,int font) {
System.out.println("改变前的颜色:"+this.color+" "+"改变前的颜色:"+this.font);
this.color = color;
this.font = font;
System.out.println("改变后的颜色:"+this.color+" "+"改变后的颜色:"+this.font);
}
}
//聊天界面
package swu.xl.day7.Interface;
public class ChatView implements SetView.SetInterface {
private String message;
private String color;
private int font;
public ChatView(String message){
this.message = message;
}
@Override
public void change(String color,int font) {
System.out.println("改变前的颜色:"+this.color+" "+"改变前的颜色:"+this.font);
this.color = color;
this.font = font;
System.out.println("改变后的颜色:"+this.color+" "+"改变后的颜色:"+this.font);
}
}
//设置界面
public class SetView {
//保存实现者
SetInterface setView;
//开始设置
public void startSet(){
System.out.println("开始设置");
System.out.println("........");
System.out.println("设置完毕 即将返回结果");
//通过方法来回调
setView.change("黑色",30);
}
//协议
interface SetInterface{
//开始设置界面
void change(String color,int font);
}
}
//模拟过程
package swu.xl.day7.Interface;
public class Main {
public static void main(String[] args){
//设置页面
SetView setView = new SetView();
//浏览界面
ReadView readView = new ReadView("test");
//进入到设置页面设置
setView.setView = readView;
setView.startSet();
//浏览界面
ChatView chatView = new ChatView("message");
//进入到设置页面设置
setView.setView = chatView;
setView.startSet();
}
}
延伸问题:普通类,抽象类,接口如何选择:
-
是否需要添加成员变量,需要:普通类,抽象类,不需要:接口
-
添加的方法是否必须要实现的,必须:抽象类,接口, 不需要:普通类,抽象类
-
需要提供模板还是提供通信(数据传递)方式,模板:抽象类,通信方式:接口
网友评论