(delphi11最新学习资料) Object Pascal 学习笔记---第8章第3节(保护字段和封装)

news/2024/10/23 22:28:19/

8.3 保护字段和封装:

TNewDate 类中 GetText 方法的代码只有在与 TDate 类相同的单元中编写时才能编译。事实上,这个方法访问的是父类的 FDate 的私有字段。如果我们想将子类放在一个新的单元中,就必须将 FDate 字段声明为protected(或者strict protected),或者在父类中添加一个简单的protected方法来读取私有字段的值。

​ 一些开发人员认为,第一种解决方案是最好的,因为将大部分字段声明为protected会使类的可扩展性更强,也更容易编写子类。然而,这违反了封装的理念。在一个庞大的类层次结构中,更改基类中某些protected字段的定义就像改变某些全局数据结构一样困难。如果有十个派生类访问这些数据,那么改变其定义就意味着可能要修改这十个类中的代码。

​ 换句话说,灵活性、扩展性和封装性往往成为相互冲突的目标。当这种情况发生时,你应该尽量倾向于封装。如果能在不牺牲灵活性的前提下做到这一点,那就更好了。通常,这种中间解决方案可以通过使用虚方法来实现,我将在下文 "后期绑定和多态性 "一节中详细讨论这个话题。如果为了加快子类的编码速度而不使用封装,那么你的设计可能就没有遵循面向对象的原则。

​ 请记住,protected字段与private字段具有相同的访问规则,因此同一单元中的任何其他类都可以访问其他类的protected成员。如前一章所述,通过使用strict protected访问指定符,可以实现更强的封装。

8.3.1 使用"Protected"黑客技术

​ 如果你是 Object Pascal 和 OOP 的新手,这将是一个相当高级的章节。第一次阅读这本书时,你可能想跳过这部分内容,因为它可能会让你感到相当困惑。

​ 鉴于单元保护的工作原理,除非使用strict protected 关键字,否则即使是当前单元中声明的类的基类的受保护成员也可以被直接访问。这就是通常所说的 "protected hack "背后的原理,即定义一个与其基类完全相同的派生类的唯一目的就是访问基类的受保护成员。下面是它的工作原理。

​ 我们已经看到,一个类的私有数据和受保护数据可以被与该类出现在同一单元中的任何函数或方法访问。例如,请看这个简单的类(保护示例的一部分):

typeTTest = classprotectedFProtectedData: Integer;publicPublicData: Integer;function GetValue: string;end;

​ GetValue方法只是返回包含两个整数值的字符串:

function TTest.GetValue: string;
beginResult := Format('Public: %d, Protected: %d', [PublicData, FProtectedData]);
end;

​ 一旦将这个类放在自己的单元中,您将无法直接从其他单元访问其受保护的部分。因此,如果您编写以下代码,

procedure TForm1.Button1Click(Sender: TObject);
varObj: TTest;
beginObj := TTest.Create;Obj.PublicData := 10;Obj.FProtectedData := 20; // 编译不通过Show(Obj.GetValue);Obj.Free;
end;

​ 编译器将报出错误消息“Undeclared identifier: ‘FProtectedData’”。此时,您可能认为没有办法直接访问在不同单元中定义的类的protected数据。然而,还是有办法的。

​ 考虑一下如果创建一个明显无用的派生类会发生什么,例如:

typeTTestAccess = class(TTest);

​ 现在,在声明它的同一单元中,您可以调用 TTestAccess 类的任何protected方法。事实上,您可以调用在同一单元中声明的类的protected方法。

​ 这对使用 TTest 类对象有什么帮助呢?考虑到这两个类共享完全相同的内存布局(因为没有任何区别),您可以强制编译器将一个类的对象当作另一个类的对象来处理,这通常是一种不安全的类型转换:

procedure TForm1.Button2Click(Sender: TObject);
varObj: TTest;
beginObj := TTest.Create;Obj.PublicData := 10;TTestAccess(Obj).FProtectedData := 20; // 编译通过!Show(Obj.GetValue);Obj.Free;

​ 这段代码编译并正常工作,正如您通过运行Protection示例所看到的。同样,原因是 TTestAccess 类自动继承了 TTest 基类的受保护字段,而且由于 TTestAccess 类与试图访问继承字段中数据的代码位于同一单元,因此受保护的数据是可访问的。

​ 既然我已经向你演示了如何做到这一点,我必须警告你,以这种方式违反类保护机制很可能会导致程序出错(因为你访问了确实不应该访问的数据),而且它与良好的 OOP 方法论背道而驰。不过,在极少数情况下,使用这种技术是最好的解决方案,这一点你可以通过查看库源代码和许多组件的代码来了解。

​ 总的来说,这种技术是一种 "黑客 "行为,应尽可能避免使用,尽管它可以被视为语言规范的一部分,并适用于所有平台以及所有现在和过去的 Object Pascal 版本。


http://www.ppmy.cn/news/1400576.html

相关文章

实时渲染是什么意思?实时渲染和离线渲染的区别

一、实时渲染是什么意思? 实时渲染是指在计算机程序运行时即时地生成图像和动画的过程,这种渲染技术通常用于网络游戏、虚拟现实和增强现实等需要实时交互的XR应用中。实时渲染需要在每秒内渲染数百万到数十亿个像素,以呈现出平滑的动画和交…

第6周 Python函数刷题(一)

单击题目,直接跳转到页面刷题,一周后公布答案。 229:函数参数打印信息185:判断质数(使用函数)188:前N个自然数之和69:特定两数之和235:递归求一个数的阶乘26&#xff1a…

wireshark创建显示过滤器实验简述

伯克利包过滤是一种在计算机网络中进行数据包过滤的技术,通过在内核中插入过滤器程序来实现对网络流量的控制和分析。 在数据包细节面板中创建显示过滤器,显示过滤器可以在wireshark捕获数据之后使用。 实验拓扑图: 实验基础配置&#xff1…

农村分散式生活污水分质处理及循环利用技术指南

标准已完成意见征集: 本文件给出了农村分散式生活污水分质处理及循环利用的总则、污水收集、污水分质处理、资源化利用、利用模式、运维管理等的指导。 本文件适用于农村分散式生活污水分质处理及循环利用的设施新建、扩建和改建工程的设计、施工与运维。 注:本文件…

springcloud基本使用(搭建eureka服务端)

创建springbootmaven项目 next next finish创建成功 删除项目下所有文件目录&#xff0c;只保留pox.xml文件 父项目中的依赖&#xff1a; springboot依赖&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-s…

2024年中国医疗领域AI Models Overview

1. 神农(ShenNong-TCM) (2023-06-25) Code: ShenNong-TCM-LM基座模型: Chinese-Alpaca-Plus-7B数据: 以开源的中医药知识图谱为基础调用 ChatGPT 得到 1Iw的围绕中医药的指令数据 Model: Chinese-Alpaca-Plus-7BDataset: ShenNong_TCM Dataset, 中医药指令数据集 ShenNong_TCM…

SpringMVC注解及使用规则

文章目录 前言一、SpringMVC注解是什么&#xff1f;二、使用步骤1.注解使用2创建JSP3 SpringMVC视图1. 逻辑视图&#xff08;Logical View&#xff09;2. 物理视图&#xff08;Physical View&#xff09;区别和关系 4 SpringMVC注解总结 总结 前言 提示&#xff1a;这里可以添…

FastAPI+React全栈开发04 FastAPI概述

Chapter01 Web Development and the FARM Stack 04 Introducing FastAPI FastAPIReact全栈开发04 FastAPI概述 Now we will look at a brief introducion to the Python REST-API framework of choice - FastAPI. Additionally, we will go over a high-level overview of t…