阿达语:包装概念

3
这是我之前发布的帖子的跟进内容:

Ada: Understanding private types and understanding packaging

使用一种实现方式即Rectangular_Method_1Rectangular类型进行了实现,需要一个规范文件和一个主体文件来完成这个实现。
如果我们想要将另一种实现Rectangular_Method_2提供给用户,则可以更改主文件rectangular_Form.ads
-- with Rectangular_Method_1;
-- package Rectangular_Form renames Rectangular_Method_1;
with Rectangular_Method_2;
package Rectangular_Form renames Rectangular_Method_2;

问题

  1. 在软件工程中,这是否是允许另一种实现的正确方法,使得测试文件test_rectangular_form.adb对于不同的实现保持不变?

  2. 如果我们创建了第二个实现Rectangular_Method_2,除了为这个新实现创建一个强制性的新主体之外,是否需要创建一个单独的规范文件?然而,在新实现中需要为Vector_Basis_rSet_HorzGet_Horz等提供相同的过程/函数,以便我们可以在test_rectangular_form.adb中使用它们。

谢谢...


Q2:如果您同意@oenone的建议使用不同的文件和GPRs,只要整个规范相同,您就不需要不同的规范;但是,如果您以任何方式更改它,例如在私有记录定义中使用极坐标而不是笛卡尔坐标,则还必须使用不同的规范。 - Simon Wright
2个回答

4
如果您使用GNAT,您可以使用GPR文件作为项目。在其中,您可以更改特定包的文件名,例如:
for Specification (Rectangular_Form) use "Rectangular_Method_1.ads";
for Implementation (Rectangular_Form) use "Rectangular_Method_1.adb";

你甚至可以根据环境变量来设置这个。

如果你的规范文件都应该看起来一样,那么你可以使用一个 Rectangular_Form.ads 文件,并仅使用上面的实现行。

一个示例 GPR 文件可能是这样的:

project Example is

   type Methods is ("normal", "something_else");
   Method : Methods := external ("METHOD", "normal");

   package Naming is
      case Method is
         when "normal" =>
            for Implementation ("Example") use "example_normal.adb";
         when "something_else" =>
            for Implementation ("Example") use "example_something.adb";
      end case;
   end Naming;

end Example;

然后,您可以使用 gnatmake -P example.gpr 编译它,具体取决于您的 METHOD 变量,或者使用 -XMETHOD=... 参数进行编译,或者只使用提供的默认值。

example_*.adb 应该都包含 Example 包的主体,而不是 Example_Normal 等。


@ oenone 谢谢。您能否详细说明最后一行是什么意思?1 票投票。 - yCalleecharan
相比于文件名建议的 package body Example_Normal,你需要写成 package body Example,就像文件名是 example.adb 一样。 - Rommudoh
@oenone 你能否请尽量使用我的方法和文件名,重写你答案中的GPR示例?你可以将 example_normal.adb 替换为 Rectangular_Method_1.adb,将 example_something.adb 替换为 Rectangular_Method_1.adb。进行任何必要的调整。这将更容易让我理解。谢谢。 - yCalleecharan
不,我不会喂你。如果你有理解上的问题,请提出具体的问题。 - Rommudoh

3

另一种实现这一目的的方法是使用标记类型。

package Rectangular is
   type Instance is abstract tagged private;

   procedure Vector_Basis_r (A : in Long_Float; D : out Instance);
   procedure Set_Horz (R : in out Instance; H : Long_Float);
   function Get_Horz (R : Instance) return Long_Float;
private
   type instance is tagged null record;
end Rectangular;

with Rectangular;
package Rectangular_Method_1 is
    type Instance is new Rectangular.Instance with private;
    ...
private
    type Instance is new Rectangular.Instance with 
    record
        Horz, Vert: Long_Float;
    end record;
end Rectangular_Method_1;

(类似于矩形法2的实现方法)。
然后我相信你可以这样编写使用它的代码:
with Rectangular_Method_1;
with Rectangular_Method_2;
...
--  My_Rectangle : Rectangular_Method_1.Instance;
My_Rectangle : Rectangular_Method_2.Instance;

My_Rectangle.Set_Horiz(Whatever_Value);
...

换句话说,当在两种类型之间切换时,您需要改变的只是类型名称。您的客户甚至可以通过在顶部使用单个子类型来消除这些更改。
subtype Rectangle_Instance is Rectangular_Method_2.Instance;

这还可以让你能够将常见的代码/字段移动到基类(包)中,这是我认为你所追求的。

1
@ T.E.D. 谢谢,确实是一个非常有趣的替代方案。如果我有问题,我会进行调查并回来。1个赞。 - yCalleecharan
@ T.E.D,你把Rectangular包放在rectangular.ads中,而将Rectangular_Method_1包单独放在Rectangular_Method_1.ads中了吗?如果是的话,那么当我编译Rectangular_Method_1.ads时,会生成错误消息:“>>>“Rectangular”未定义(更多引用跟随)”。谢谢。 - yCalleecharan
@yCalleecharan - Rectangular_Method_1将不得不与Rectangular一起使用。我会将其添加到上面的准代码中,以使其更清晰。但可能还会有其他一些编译器问题。 - T.E.D.
@ T.E.D. 谢谢。我在编译 test_rectangular_form 时遇到了错误:My_Rectangle : Rectangular_Method_2.Instance; | >>> 需要编译单元 - yCalleecharan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接