7分钟3个例子理解代理模式,面试多加29分

本文通过三个生动的例子,深入浅出地介绍了代理模式的概念及其在实际编程中的应用,包括代购、房屋中介和论坛发帖权限检查。

代理模式 (Proxy Pattern) 是一种结构性设计模式。通过代理模式,我们可以不免直接访问目标对象,转而通过代理对象来访问目标对象。同时,我们可以通过代理对象来控制访问权限以及增强功能

让我们用 3 个例子来解释清楚什么是代理模式,以及代理模式所带来的好处。

例子1:代购

代购是讲解代理模式中最常见的例子,那么我们也不能免俗地要来讲讲这个例子。

假设我想要购买 A 品牌的洋奶粉,需要通过代购才能够买到这款羊奶粉,那么我们可以这样说:

  • 抽象主题角色 (Subject)
    买家

  • 真实主题角色 (Real Subject)

  • 代理主题角色 (Proxy)
    代购

通过代码来表示:

// 买家
abstract class Buyer {
	abstract void buy(String item);
}

// 我
public class Me extends Buyer {
	public void buy(String item) {
		System.out.println("购买商品: " + item);
	}
}

// 代购
class BuyingAgent extends Buyer {
	// 代替我买
	private Buyer me = new Me();

	public void buy(String item) {
		System.out.println("我是代购,我正在加拿大温哥华商场");
		me.buy(item);
		System.out.println("购买完毕,准备快递寄回委托人手里");
	}
}

运行程序

public static void main(String[] args) {
	Buyer buyingAgent = new BuyingAgent();
	buyingAgent.buy("A品牌奶粉");
}

运行之后得到的输出结果是

我是代购,我正在加拿大温哥华商场
购买商品: A品牌奶粉
购买完毕,准备快递寄回委托人手里

这个例子体现出了代理模式的基本用法。我们会在代理类中包含一个真实对象,并在调用真实对象方法之前或之后,我们还可以调用其他函数或者是运行其他逻辑代码。

例子2:房屋中介

假设Kevin有一套房子需要出售,然而他并不懂得市场行情,也没有时间与买家周旋,于是Kevin请了一位房屋中介帮忙销售这套房子。

房屋中介除了可以帮Kevin卖出房屋之外,还可以直接拒绝心理价位以下的出价,以及帮助Kevin处理后续的交易文件以及手续,我们可以将这理解为功能增强

在这里,房屋中介的职责就是一个代理人,阻止了买家直接与卖家联系,任何事务都必须通过房屋中介处理。

我们可以这样定义:

  • 抽象主题角色 (Subject)
    卖家

  • 真实主题角色 (Real Subject)
    卖家 Kevin

  • 代理主题角色 (Proxy)
    房屋中介

通过代码来表示:

abstract class Seller {
	abstract void sell(String house, int price);
}

class Kevin extends Seller {
	public void sell(String house, int price) {
		System.out.println("售出房产: " + house + ", 售价: " + price + "万");
	}
}

class Agent extends Seller {
	private Seller seller = new Kevin();
	
	// 卖家可接受最低价格
	private int acceptablePrice = 100;

	public void sell(String house, int price) {
		// 控制访问权限,出价过低不出售
		if (!isPriceAcceptable(price)) {
			System.out.println("抱歉," + price + "万出价过低。");
			return;
		}
		System.out.println("接受出价" + price + "万");
		seller.sell(house, price);
		processDocuments();
	}

	private boolean isPriceAcceptable(int price) {
		return price >= acceptablePrice;
	}

	// 功能增强:帮卖家处理交易手续
	private void processDocuments() {
		System.out.println("处理交易手续");
	}
}

运行程序

public static void main(String[] args) {
	Seller agent = new Agent();
	agent.sell("A套房产", 90);
	agent.sell("A套房产", 100);
}

得到输出结果

抱歉,90万出价过低。
接受出价100万
售出房产: A套房产, 售价: 100万
处理交易手续

在这个例子中,我们看到了代理类可以带来访问权限的控制与其他增强功能。

  • 访问权限控制
    报价过低无法访问真实对象
  • 功能增强
    处理交易手续

例子3:实际编程工作中的应用场景

假设我们在一家软件公司里工作,当前正在开发一款论坛产品。A组的开发人员已经开发好了发布帖子的接口了,而我们的任务则是给这个发布帖子的接口加上发帖权限检查,以及发帖日志记录的功能。

然而这里有个限制,由于这个接口是由A组开发的,因此他们不希望我们动他们的源代码,这在软件公司里是很常见的事情,不同组之间尽量不去动对方的代码,那么我们需要怎么做,才能在不动到对方代码的情况下,实现我们的功能呢?

答案就是:代理模式

先让我们来看看A组开发的接口:

public interface PostService {
	void makePost(String content, int userId);
}

public class PostServiceImpl implements PostService{
	public void makePost(String content, int userId) {
		// 发布帖子的逻辑
		System.out.println("用户" + userId + "发布帖子成功");
	}
}

那么我们可以使用代理模式,创建一个代理类,通过代理类我们会在调用真实对象发布帖子的函数之前先进行权限检查,并在调用真实对象发布帖子的函数之后再调用打日志的函数。

public class PostServiceProxy implements PostService {
	private PostService postService = new PostServiceImpl();

	public void makePost(String content, int userId) {
	
		// 先进性权限检查
		validateAuthentication(userId);
		
		// 再实际调用发帖函数
		postService.makePost(content, userId);
		
		// 之后打上日志
		logActivity("发布帖子", userId);
	}
	
	private void validateAuthentication(int userId) {
		System.out.println("检查用户" + userId + "权限");
	}

	private void logActivity(String activity, int userId) {
		System.out.print("记录用户" + userId + "活动: " + activity);
	}
}

实际运行程序:

public static void main(String[] args) {
	PostService postService = new PostServiceProxy();
	int userId = 1;
	postService.makePost("新人报道帖!", userId);
}

运行结果:

检查用户1权限
用户1发布帖子成功
记录用户1活动: 发布帖子

可以看到,从调用者的角度来看,除了创建的对象是代理类之外,实际用法与真实对象无异,却又能够增加权限检查与打日志两项功能,这便是代理模式的厉害之处。

以下是在原有代码基础上添系统的实现。每次消除3个方块时1,每消除一个方块就额外1。 --- ### 修改后的代码 ```cpp #include <graphics.h> #include <conio.h> #include <vector> #include <time.h> #include <cstdlib> using namespace std; const int ROW = 8; // 行数 const int COL = 10; // 列数 const int BLOCK_SIZE = 40; // 方块大小 const int COLORS[] = {RED, GREEN, BLUE, YELLOW, MAGENTA}; // 颜色数组 const int NUM_COLORS = sizeof(COLORS) / sizeof(COLORS[0]); // 游戏网格和积 vector<vector<int>> grid(ROW, vector<int>(COL)); int score = 0; // 积系统 // 初始化网格 void initGrid() { for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { grid[i][j] = rand() % NUM_COLORS; // 随机配颜色 } } } // 绘制网格和积 void drawGrid() { cleardevice(); // 绘制方格 for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { setfillcolor(COLORS[grid[i][j]]); solidrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE); } } // 显示积 settextstyle(DEFAULT_FONT, HORIZ_DIR, 2); // 设置字体样式 outtextxy(10, ROW * BLOCK_SIZE + 10, ("Score: " + to_string(score)).c_str()); } // 检查并标记匹配的方块 bool checkAndMarkMatches(vector<vector<bool>>& toRemove) { bool matched = false; int matchCount = 0; // 记录匹配方块的数量 // 水平检查 for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL - 2; ++j) { if (grid[i][j] == grid[i][j + 1] && grid[i][j] == grid[i][j + 2]) { toRemove[i][j] = toRemove[i][j + 1] = toRemove[i][j + 2] = true; matched = true; matchCount += 3; // 至少匹配3个方块 // 如果有更连续匹配,增计数 int k = j + 3; while (k < COL && grid[i][k] == grid[i][j]) { toRemove[i][k] = true; matchCount++; k++; } } } } // 垂直检查 for (int j = 0; j < COL; ++j) { for (int i = 0; i < ROW - 2; ++i) { if (grid[i][j] == grid[i + 1][j] && grid[i][j] == grid[i + 2][j]) { toRemove[i][j] = toRemove[i + 1][j] = toRemove[i + 2][j] = true; matched = true; matchCount += 3; // 至少匹配3个方块 // 如果有更连续匹配,增计数 int k = i + 3; while (k < ROW && grid[k][j] == grid[i][j]) { toRemove[k][j] = true; matchCount++; k++; } } } } // 更新积 if (matched) { score += matchCount - 2; // 消除31,每消除1个1 } return matched; } // 移除标记的方块并下落 void removeAndDropBlocks() { vector<vector<bool>> toRemove(ROW, vector<bool>(COL, false)); bool matched = checkAndMarkMatches(toRemove); if (matched) { // 移除匹配的方块 for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { if (toRemove[i][j]) { grid[i][j] = -1; // 标记为需要删除 } } } // 下落方块 for (int j = 0; j < COL; ++j) { vector<int> temp; for (int i = 0; i < ROW; ++i) { if (grid[i][j] != -1) { temp.push_back(grid[i][j]); } } for (int i = 0; i < ROW; ++i) { if (i < ROW - temp.size()) { grid[i][j] = rand() % NUM_COLORS; // 生成新方块 } else { grid[i][j] = temp[i - (ROW - temp.size())]; } } } } } // 处理鼠标点击事件 void handleMouseClick() { static int lastX = -1, lastY = -1; // 记录上次点击的位置 if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) { // 使用 GetAsyncKeyState 检测鼠标左键状态 MOUSEMSG msg = GetMouseMsg(); // 获取鼠标消息 int x = msg.x / BLOCK_SIZE; int y = msg.y / BLOCK_SIZE; if (x >= 0 && x < COL && y >= 0 && y < ROW) { if (lastX == -1 && lastY == -1) { // 第一次点击,记录位置 lastX = x; lastY = y; } else { // 第二次点击,检查是否相邻 if (abs(lastX - x) + abs(lastY - y) == 1) { // 交换两个方块 swap(grid[lastY][lastX], grid[y][x]); drawGrid(); // 检查交换后是否有匹配 vector<vector<bool>> tempToCheck(ROW, vector<bool>(COL, false)); if (!checkAndMarkMatches(tempToCheck)) { // 如果没有匹配,则恢复原状 swap(grid[lastY][lastX], grid[y][x]); drawGrid(); } else { // 如果有匹配,则移除方块并下落 removeAndDropBlocks(); drawGrid(); } } // 重置上次点击的位置 lastX = -1; lastY = -1; } } } } // 主函数 int main() { srand(time(0)); initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE + 50); // 初始化图形窗口,预留空间显示积 initGrid(); // 初始化网格 drawGrid(); // 绘制网格 while (true) { handleMouseClick(); // 处理鼠标点击 } _getch(); // 等待用户按键 closegraph(); // 关闭图形窗口 return 0; } ``` --- ### 解释 1. **积系统**: - 添了一个全局变量 `score` 来记录玩家的积。 - 在 `checkAndMarkMatches` 函数中,计算每次匹配的方块数量 `matchCount`。 - 每次消除3个方块1,每消除一个方块额外1。 2. **积显示**: - 使用 `settextstyle` 和 `outtextxy` 函数在屏幕底部显示积信息。 - 图形窗口的高度增了 50 像素,用于显示积3. **连续匹配处理**: - 在水平和垂直检查中,如果匹配的方块超过3个,会继续检查后续的方块,并将它们也标记为需要移除。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值