我是一位相当新的Ada程序员。我读过Barnes的书(我可能会补充两次)甚至设法在Ada写一个公平的终端程序。我的主要语言是C ++。
我目前想知道是否有办法保护" Ada中的子程序调用,也许是在Ada 2012中(我基本上什么都不知道)。让我解释一下我的意思(尽管用C ++术语)。
假设您有一个类Secret
,如下所示:
class Secret
{
private:
int secret_int;
public:
Set_Secret_Value( int i );
}
现在这是常见的东西,不要暴露secret_int,只能通过访问函数来操作它。但是,问题是任何有权访问Secret类型对象的人都可以操纵该值,无论该特定代码段是否应该这样做。因此,恶意改变secret_int的危险已经减少到任何人通过允许的函数改变secret_int,即使它发生在不应该操纵它的代码部分。
为了解决这个问题,我想出了以下构造
class Secret
{
friend class Secret_Interface;
private:
int secret_int;
Set_Secret_Value( int i );
Super_Secret_Function();
};
class Secret_Interface
{
friend class Client;
private:
static Set_Secret_Value( Secret &rc_secret_object, int i )
{
rc_secret_object.Set_Secret( i );
}
};
class Client
{
Some_Function()
{
...
Secret_Interface::Set_Secret_Value( c_object, some-value );
...
}
}
现在,类Secret_Interface
可以确定哪些其他类可以使用它的私有函数,并间接地通过这样做来实现暴露给Secret_Interface
的类Secret的功能。这样,类Secret仍然具有私有函数,这些函数不能被类外的任何人调用,例如函数Super_Secret_Function()
。
好吧,我想知道在Ada中是否有可能出现这种情况。基本上我的愿望是能够说:
Code A may only be executed by code B but not by anybody else
感谢您的帮助。
编辑: 我在这里添加了一个图表,其中包含一个程序结构,就像我想到的那样,我在这里表示的是数据结构在软件的广泛区域中的传输,记录的定义,创建和使用应该发生在代码部分中否则是不合适的
答案 0 :(得分:6)
我认为关键是要意识到,与C ++和其他语言不同,Ada的主要顶级单位是package
,并且可见性控制(即公共与私人)在每个 - 包装基础,而不是每种类型(或每类)。我不确定我是否说得对,但希望下面会解释一下。
C ++中friend
的主要目的之一是,您可以编写两个(或更多)密切相关的class
es,它们都参与实现一个概念。在这种情况下,有意义的是,一个类中的代码能够更直接地访问另一个类中的代码,因为它们一起工作。我假设在您的C ++示例中,Secret
和Client
具有这种密切关系。如果我正确理解C ++,它们都必须在同一个源文件中定义;如果你说friend class Client
,那么Client
类必须稍后在相同的源文件中定义(并且它不能在之前定义,因为此时{{1}中的方法}}或Secret
尚未宣布)。
在Ada中,您只需在同一个包中定义类型即可。
Secret_Interface
现在,package P is
type Secret is tagged private;
type Client is tagged private;
-- define public operations for both types
private
type Secret is tagged record ... end record;
type Client is tagged record ... end record;
-- define private operations for either or both types
end P;
的正文将包含两种类型的公共和私有操作的实际代码。 P
package body
中的所有代码都可以访问P
P
部分中定义的内容,无论其运行的是哪种类型。事实上,所有代码都可以访问这两种类型的完整定义。这意味着在private
上运行的过程可以调用在Client
上运行的私有操作,实际上它可以读取和写入Secret
的记录组件直。 (反之亦然。)对于习惯于大多数其他OOP语言使用的Secret
范例的程序员来说,这可能看起来很奇怪,但它在Ada中运行良好。 (事实上,除了class
的实现之外,如果您不需要Secret
可以访问其他任何内容,则可以在Client
部分中定义类型及其操作。 private
或P
。)这种安排不违反OOP背后的原则(封装,信息隐藏),只要这两种类型真正实现一个连贯的两个部分概念
如果那不是你想要的,即如果package body
和Secret
没有密切相关,那么我需要看一个更大的例子来找出什么您尝试实施的一种用例。
更多想法:在查看图表后,我认为您尝试解决问题的方式是劣质设计 - 如果您愿意的话,这是一种反模式。当你写一个"模块" (无论这意味着什么 - 类或包,或者在某些情况下,两个或更多密切相关的类或包彼此协作),该模块定义了其他模块如何使用它 - 它在其对象上提供的公共操作,以及这些行动是做什么的。
但是根据合同,模块(让它称之为M1)应该以相同的方式工作,无论其他模块是什么叫它,以及如何。 M1将获得一系列"消息"指示它执行某些任务或返回某些信息; M1不应该关心这些消息的来源。特别是,M1不应该对使用它的客户端的结构做出决定。通过让M1判断"程序XYZ只能从包ABC"中调用,M1对使用它的客户端强加了结构要求。我相信这会导致M1与程序的其他部分紧密耦合。这不是好设计。
但是,使用 M1的模块在内部执行某种类型的控制可能是有意义的。假设我们有一个"模块" M2实际上使用了许多软件包作为其实现的一部分。 "主要" M2中的包(M2的客户端用来让M2执行其任务)使用M1创建一个新对象,然后将该对象传递给其他几个完成工作的包。似乎一个合理的设计目标是找到一种方法,M2可以将该对象传递给某些包或子程序,而不会让它们更新对象,但是将其传递给将>的其他包或子程序em>有这种能力。
有一些解决方案可以防止大多数事故。例如:
Client
现在,可以采取"秘密"对象但不应该具有更新它的能力将具有使用package M1 is
type Secret is tagged private;
procedure Harmless_Operation (X : in out Secret);
type Secret_With_Updater is new Secret with null record;
procedure Dangerous_Operation (X : in out Secret_With_Updater);
end M1;
参数定义的过程。 M2会创建一个Secret'Class
对象;由于此对象类型位于Secret_With_Updater
中,因此可以将其作为参数传递给具有Secret'Class
参数的过程。但是,这些程序无法就其参数调用Secret'Class
;那不会编译。
具有Dangerous_Operation
参数的包仍然可以通过类型转换调用危险操作:
Secret'Class
语言无法阻止这种情况,因为它无法使procedure P (X : in out Secret'Class) is
begin
-- ...
M1.Secret_With_Updater(X).Dangerous_Operation;
-- ...
end P;
对某些软件包可见而对其他软件包不可见(不使用子软件包层次结构)。但是偶然做这件事会更难。如果你真的希望更进一步,甚至阻止这一点(如果你认为会有一个程序员对优秀的设计原则的理解太差,以至于他们愿意写这样的代码),那么你可以去一点进一步:
Secret_With_Updater
然后,为了创建一个秘密,M2会调用一些东西来创建一个Secret_With_Updater,它返回一个可以访问Secret的记录。然后它会将package M1 is
type Secret is tagged private;
procedure Harmless_Operation (X : in out Secret);
type Secret_Acc is access all Secret;
type Secret_With_Updater is tagged private;
function Get_Secret (X : Secret_With_Updater) return Secret_Acc;
-- this will be "return X.S"
procedure Dangerous_Operation (X : in out Secret_With_Updater);
private
-- ...
type Secret_With_Updater is tagged record
S : Secret_Acc;
end record;
-- ...
end M1;
传递给那些不允许调用X.Get_Secret
的程序,而Dangerous_Operation
本身则传递给那些允许的程序。 (您也可以声明X
,声明S : aliased Secret
返回Get_Secret
,并使用access Secret
实现。这可以避免潜在的内存泄漏,但也可能遇到无障碍检查问题。我没试过这个。)
无论如何,也许这些想法中的一些可能有助于实现您想要完成的任务,而不会通过强制M1了解使用它的应用程序的结构来引入不必要的耦合。这很难说,因为你对这个问题的描述,即使是图表,对于我来说仍然处于一个过于抽象的层面,看不到你真正想做的事情。
答案 1 :(得分:2)
您可以使用子包来执行此操作:
package Hidden is
private
A : Integer;
B : Integer;
end Hidden;
然后
package Hidden.Client_A_View is
function Get_A return Integer;
procedure Set_A (To : Integer);
end Hidden.Client_A_View;
然后,Client_A
可以写
with Hidden.Client_A_View;
procedure Client_A is
Tmp : Integer;
begin
Tmp := Hidden.Client_A_View.Get_A;
Hidden.Client_A_View.Set_A (Tmp + 1);
end Client_A;
答案 2 :(得分:2)
您的问题非常不清楚(并且所有C ++代码都没有帮助解释您的需求),但如果您的观点是您希望某个类型具有一些可公开访问的操作和一些私有操作,那么很容易完成:
package Example is
type Instance is private;
procedure Public_Operation (Item : in out Instance);
private
procedure Private_Operation (Item : in out Instance);
type Instance is ... -- whatever you need it to be
end Example;
Example.Private_Operation
的孩子可以访问Example
这个程序。如果您希望操作纯粹是内部操作,则只在包体中声明它:
package body Example is
procedure Internal_Operation (Item : in out Instance);
...
end Example;
答案 3 :(得分:2)
好吧,我想知道在Ada中是否有可能出现这种情况。基本上我的愿望是能够说:
Code A may only be executed by code B but not by anybody else
如果仅限于语言功能,请
如果必须向提供商提供允许执行其服务的经批准的“密钥”,并且仅向授权客户提供此类密钥,则可以通过编程方式保护代码执行。
设计此类密钥的性质,生成和安全性是留给读者的练习。