En Java o C #, a menudo tendría miembros de clase que son final o readonly: se configuran una vez y luego nunca se tocan nuevamente. Pueden contener diferentes valores para diferentes instancias de la clase.

¿Hay algo similar en Ada? He intentado crear algo similar en Ada de esta manera:

package MyPackage is

   type MyObject is limited new OtherPackage.Object with private;

....

private

   type MyObject (...) is limited new OtherPackage.Object with
      record
         M_MyField : Integer := 10;
         M_MyConstantFactory : constant Factory.Object'Class := new Factory.Object;
      end record;

end MyPackage;

Esto falla en la declaración de M_MyConstantFactory que dice constant components are not permitted. ¿Hay alguna forma de evitar esto? Un colega sugirió declararlo en otro lugar del paquete, pero eso significaría un solo M_MyConstantFactory compartido en todas las instancias, que no es lo que quiero.

¿Debo aceptar que es posible modificar el valor una vez establecido y protegerme manualmente de que eso suceda?

ada
6
Avi Chapman 10 sep. 2018 a las 08:56

3 respuestas

La mejor respuesta

Antes de responder la pregunta, probablemente sería útil distinguir entre el modelado de objetos de Ada y Java / C #. En Java, todo es un objeto, por lo que todas las constantes deben ser final; en Ada, las cosas son un poco diferentes, el sistema de objetos de Ada ("tipos etiquetados" en el lenguaje de Ada) se basa en dos elementos: registros y derivación de tipo. Esto significa que, al enseñar OOP, podríamos llegar introduciendo gradualmente la primera derivación de tipo (por ejemplo, Type Degree is new Integer;), luego los registros (es decir, la encapsulación), luego private - tipos (es decir, ocultación de información) y finalmente unificando todo junto con los tipos etiquetados ... todo lo cual supongo que conoce.

En Ada, un constant es solo eso: algún objeto que se puede leer pero [en general] no se puede escribir. (Las cosas pueden ponerse divertidas con, por ejemplo, IO mapeado en memoria). Entonces podríamos decir:

Package Ex1 is
  Type Stub1 is private; -- Some type, with almost nothing public.
  C1 : Constant Stub1;   -- A constant of that type.
Private
  Type Stub1 is tagged record
    Data_1 : Integer;
    Data_2 : Float;
  end;

  -- And now we can tell the compiler what C1 _is_.
  C1: Constant Stub1 := (Data_1 => 3, Data_2 => 1.2);
End Ex1;

Así es como haríamos una constante para un tipo etiquetado mientras mantenemos ocultos los detalles de su implementación; sin embargo, es cierto que podríamos haber expuesto todo y deshacernos de toda la sección private.

Ahora llegamos a una característica interesante de los registros [y tipos etiquetados] llamados discriminantes: son como constantes similares y como tipos genéricos similares en otros idiomas. Con discriminantes podríamos hacer un tipo de mensaje que varía en tamaño de acuerdo con la longitud del mensaje:

Package Ex2 is
  Type Message(Length : Natural) is private; -- A message.
  Function Create( Text : String ) return Message;
Private
  Type Message(Length : Natural) is record
    Data : String(1..Length) := (Others => ' '); -- Defaults to space-filled string.
  end;

  Function Create( Text : String ) return Message is
  ( Data => Text, Length => Text'Length );
End Ex2;

Ahora, en este caso, cuando realiza una asignación como X : Message := Create("Steve");, el tipo de la variable [sin restricciones, en este ejemplo, se limita en este caso a Message(5) (porque "Steve" tiene 5 caracteres) y así lo intenta reasignar con una cadena de mensaje de diferente tamaño no funcionaría. (Entonces, aunque no pudiste decir X:= Create("Why"), puedes puedes decir X:= Create("Hello") porque el discriminante [Length] aquí es 5.) - Entonces, en eso Los discriminantes pueden actuar como campos constantes en algunos casos.

La palabra clave limited significa que el tipo no tiene una asignación [pero tiene inicialización], por lo que podría hacer que todo el tipo se comporte como una constante; sin embargo, esto es diferente de tener un componente que sea constant, ciertamente no tan sutil como la distinción entre T y T'Class (T'Class es el Tipo T y todos los tipos derivados de ellos, donde T es solo ese tipo).

1
Shark8 29 sep. 2018 a las 02:49

No. No del todo.

Si su componente es de tipo discreto o de acceso, puede convertirlo en un elemento discriminante y, por lo tanto, hacerlo inmutable.

with Ada.Integer_Text_IO;

procedure Immutable_Components is

   type Instance (Immutable : Positive) is null record;

   A : Instance := (Immutable => 1);

begin
   Ada.Integer_Text_IO.Put (A.Immutable);

   --  A.Immutable := 2; --  assignment to discriminant not allowed:
end Immutable_Components;
7
TamaMcGlinn 13 sep. 2018 a las 15:27

Ya casi tiene la solución general (para los casos en que no puede ser discriminante). El tipo es privado limitado. Los clientes del paquete solo pueden modificarlo a través de las operaciones que proporciona el paquete. Mientras las operaciones no modifiquen el campo en cuestión, usted tiene lo que desea (a menos que lo haya entendido mal y la pregunta es cómo evitar modificar el campo en el cuerpo del paquete).

0
Jeffrey R. Carter 15 sep. 2018 a las 12:52