数据隐藏的实现
本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 33 课,共 70 课。
数据隐藏不仅仅是使用 private 字段。它还意味着设计类时,让内部实现细节对外部代码完全不可见,只通过简洁的公共接口公开必要的内容。
想想一个用于跟踪用户密码的类。你不希望任何人访问原始密码,甚至不希望他们读取它:
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}注意,你无法取回密码,甚至无法取回哈希值。外部代码只能设置密码或验证密码,内部存储机制完全隐藏。HashPassword 方法也是私有的,因为它属于实现细节,即使发生变化,也不会影响类的使用方式。
这种做法可以保护你的类不被误用,也让你以后能自由更改内部实现。如果你决定使用不同的哈希算法,也不会导致外部代码出错,因为没有任何代码依赖密码的存储方式。
挑战
简单让我们构建一个安全的钱包系统,来展示真正的数据隐藏。你将创建一个数字钱包,其中的余额受到完全保护,无法直接访问。外部代码只能存款、取款,以及检查交易是否可行,但绝不能直接查看或操作实际余额。
你将把代码分到两个文件中:
Wallet.cs:在Finance命名空间中创建一个Wallet类,让余额完全隐藏。钱包应包含:- 一个用于存储余额的私有字段(decimal)
- 一个私有方法
FormatBalance(),用于将余额格式化为货币字符串,例如"$123.45"。这是一个不应公开的内部辅助方法 - 一个公共方法
Deposit(decimal amount),仅当金额为正数时才增加余额。成功时返回true,否则返回false - 一个公共方法
Withdraw(decimal amount),仅当金额为正数且不超过当前余额时才扣减余额。成功时返回true,否则返回false - 一个公共方法
CanAfford(decimal amount),如果余额大于或等于金额,则返回true,否则返回false - 一个公共方法
GetStatement(),在内部使用私有方法FormatBalance(),并返回"Current balance: {formatted balance}"
Program.cs:在主文件中创建一个Wallet,并使用输入值执行一系列交易。展示钱包如何保护其内部状态,同时通过公共接口提供实用功能。
你将收到三个输入:
- 初始存款金额(decimal)
- 尝试取款的金额(decimal)
- 要检查是否有足够余额支付的金额(decimal)
按以下格式打印输出:
Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}例如,如果输入为 100.50、30.25 和 80.00,输出应为:
Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: False这里的关键在于,外部代码不会直接接触余额字段。即使是格式化后的余额字符串,也要通过一个在内部使用私有辅助方法的公共方法获取。这就是数据隐藏:实现细节完全不可见,你可以自由更改余额的存储或格式化方式,而不会破坏使用钱包的代码!
亲手试试
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// 读取输入值
decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
decimal checkAmount = Convert.ToDecimal(Console.ReadLine());
// TODO:创建一个 Wallet 实例
// TODO:执行存款操作,并按以下格式打印结果:"Deposit {amount}: {True/False}"
// TODO:使用 GetStatement() 打印对账单
// TODO:执行取款操作,并按以下格式打印结果:"Withdraw {amount}: {True/False}"
// TODO:使用 GetStatement() 打印对账单
// TODO:检查余额是否足够,并按以下格式打印结果:"Can afford {amount}: {True/False}"
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器