From 2664c9ed73a73b79553a1291de7dbd3c90f9fede Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 24 Nov 2024 04:11:17 +0100 Subject: [PATCH] Editorial change: reverting to previous format Improving comparison with the original version. --- .../chapters/abstract_data_machines.rst | 105 +++++++++--------- ...ctor_functions_for_abstract_data_types.rst | 9 +- ...ontrolling_obj_initialization_creation.rst | 4 +- .../essential_idioms_for_packages.rst | 10 +- .../chapters/inheritance_idioms.rst | 44 ++++---- .../chapters/programming_by_extension.rst | 2 +- 6 files changed, 86 insertions(+), 88 deletions(-) diff --git a/content/courses/ada-idioms/chapters/abstract_data_machines.rst b/content/courses/ada-idioms/chapters/abstract_data_machines.rst index 1dfa13fea..5d86fbf21 100644 --- a/content/courses/ada-idioms/chapters/abstract_data_machines.rst +++ b/content/courses/ada-idioms/chapters/abstract_data_machines.rst @@ -16,25 +16,27 @@ Motivation Solution -------- -The Abstract Data Machine (ADM) idiom is similar to the :ref:`Abstract Data -Type ` idiom in that it presents an -abstraction that doesn't already exist in the programming language. Furthermore, -like the ADT, operations are provided to manipulate the abstraction -state, which is not otherwise compile-time visible to client -code. These operations are thus enforced as the only manipulation possible, -as per the designer's intent. (The Abstract Data Machine was introduced by -Grady Booch [1]_ as the Abstract State Machine, but that name, though -appropriate, encompasses more in computer science than we intend to evoke.) - -Unlike the ADT, however, the ADM does not define the abstraction as a -type. To understand this point, recall that type declarations are -descriptions for objects that will contain data (the state). For example, our earlier -:ada:`Stack` type was defined as a record containing two components: an -array to hold the values logically contained by the :ada:`Stack` and an -integer indicating the logical top of that array. No data actually exists, -i.e., is allocated storage, until objects are declared. Clients can -declare as many objects of type :ada:`Stack` as they require and each -object has a distinct, separate copy of those two components. +The Abstract Data Machine (ADM) idiom is similar to the +:ref:`Abstract Data Type ` idiom in that it +presents an abstraction that doesn't already exist in the +programming language. Furthermore, like the ADT, operations are provided to +manipulate the abstraction state, which is not otherwise compile-time +visible to client code. These operations are thus enforced as the only +manipulation possible, as per the designer's intent. (The Abstract Data +Machine was introduced by Grady Booch [1]_ as the Abstract State Machine, but that +name, though appropriate, encompasses more in computer science than we intend +to evoke.) + +Unlike the ADT, however, the ADM does not define the abstraction as a type. To +understand this point, recall that type declarations are descriptions for +objects that will contain data (the state). For example, +our earlier :ada:`Stack` type was +defined as a record containing two components: an array to hold the values +logically contained by the :ada:`Stack` and an integer indicating the logical +top of that array. No data actually exists, i.e., is allocated storage, until +objects are declared. Clients can declare as many objects of type :ada:`Stack` +as they require and each object has a distinct, separate copy of those two +components. Clients can, of course, choose to declare only one object of a given type, in which case only one instance of the data described by the type will exist. But @@ -62,13 +64,12 @@ or even this, using an anonymously-typed array: If there is only one *stack*, these two objects will suffice. That's what the ADM does. The package, usually a library package, declares -the necessary state for a single abstraction instance. But, as an -abstraction, those data declarations must not be compile-time visible to -clients. Therefore, the state is declared in either the package private -part or the package body. Doing so requires that visible operations be -made available to clients, like any other abstraction. Hence the package is -the one instance of the abstraction, as opposed to defining one or more objects -of a type. +the necessary state for a single abstraction instance. But, as an abstraction, +those data declarations must not be compile-time visible to clients. Therefore, +the state is declared in either the package private part or the package body. +Doing so requires that visible operations be made available to clients, like any +other abstraction. Hence the package is the one instance of the abstraction, as +opposed to defining one or more objects of a type. Therefore, the package declaration's visible section contains only the following: @@ -113,11 +114,11 @@ version we declare the state in the package body. function Empty return Boolean is (Top = 0); end Integer_Stack; -Now there is no type presenting a :ada:`Stack` abstraction and the -operations do not take a stack parameter because the package and its data -is the instance of the abstraction. When using this idiom, there is only -one stack of integers. That's why we changed the name of the package from -:ada:`Integer_Stacks`, i.e., from the plural form to the singular. +Now there is no type presenting a :ada:`Stack` abstraction and the operations +do not take a stack parameter because the package and its data is the instance +of the abstraction. When using this idiom, there is only one stack of integers. +That's why we changed the name of the package from :ada:`Integer_Stacks`, i.e., +from the plural form to the singular. As with the ADT idiom, clients of an ADM can only manipulate the encapsulated state indirectly, via the visible operations. The difference is that the state @@ -137,11 +138,11 @@ type :ada:`Stack` are manipulated: -- ... Push (Answers, 42); -That call places the value 42 in the array :ada:`Answers.Values`, i.e., -within the :ada:`Answers` variable. Clients can declare as many -:ada:`Stack` objects as they require, each containing a distinct copy of -the state defined by the type. In the ADM version, there is only one stack -and therefore only one instance of the state. +That call places the value 42 in the array :ada:`Answers.Values`, i.e., within +the :ada:`Answers` variable. Clients can declare as many :ada:`Stack` objects +as they require, each containing a distinct copy of the state defined by the +type. In the ADM version, there is only one stack and therefore only one instance +of the state. Rather than declare the abstraction state in the package body, we could just as easily declare it in the package's private section: @@ -170,17 +171,15 @@ the package body. The ADM idiom applies information hiding to the internal state, like the ADT idiom, except that the state is not in objects. Also, like the -:ref:`Groups of Related Program Units -`, the implementations of the -visible subprograms are hidden in the package body, along with any -non-visible entities required for their implementation. +:ref:`Groups of Related Program Units `, +the implementations of the visible subprograms are hidden in the package body, +along with any non-visible entities required for their implementation. There are no constructor functions returning a value of the abstraction -type because there is no such type with the ADM. However, there could be -one or more initialization procedures, operating directly on the hidden -state in the package private part or package body. In the :ada:`Stack` ADM -there is no need because of the reasonable initial state, as is true with -the ADT version. +type because there is no such type with the ADM. However, there could be one or +more initialization procedures, operating directly on the hidden state in the +package private part or package body. In the :ada:`Stack` ADM there is no need +because of the reasonable initial state, as is true with the ADT version. The considerations regarding selectors/accessors are the same for the ADM as for the ADT idiom, so they are not provided by default. Also like the ADT, @@ -190,14 +189,14 @@ idiom by default. Pros ---- -In terms of abstraction and information hiding, the ADM idiom provides the -same advantages as the ADT idiom: clients have no visibility to -representation details and must use the operations declared in the package -to manipulate the state. The compiler enforces this abstract view. The ADM -also has the ADT benefit of knowing where any bugs could possibly be -located. If there is a bug in the manipulation, it must be in the one -package defining the abstraction itself. No other code would have the -compile-time visibility necessary. +In terms of abstraction and information hiding, the ADM idiom provides the same +advantages as the ADT idiom: clients have no visibility to +representation details and +must use the operations declared in the package to manipulate the state. The +compiler enforces this abstract view. The ADM also has the ADT benefit of +knowing where any bugs could possibly be located. If there is a bug in the +manipulation, it must be in the one package defining the abstraction itself. No +other code would have the compile-time visibility necessary. This idiom can be applied to any situation requiring abstraction, including hardware. For example, consider a microprocessor that has an on-board rotary diff --git a/content/courses/ada-idioms/chapters/constructor_functions_for_abstract_data_types.rst b/content/courses/ada-idioms/chapters/constructor_functions_for_abstract_data_types.rst index 0aafbf787..d0dff1523 100644 --- a/content/courses/ada-idioms/chapters/constructor_functions_for_abstract_data_types.rst +++ b/content/courses/ada-idioms/chapters/constructor_functions_for_abstract_data_types.rst @@ -65,11 +65,10 @@ functions that return an object of the type. Like any function there may be formal parameters specified, but not necessarily. Functions and procedures that manipulate objects of the private type are -*primitive operations* for the type if they are declared in the same -package as the type declaration itself. For procedures, that means they -have formal parameters of the type. For functions, that means they -either have formal parameters of the type, or return a value of the -type, or both. +*primitive operations* for the type if they are declared in the same package as +the type declaration itself. For procedures, that means they have formal +parameters of the type. For functions, that means they either have formal +parameters of the type, or return a value of the type, or both. Declaration with the same package as the type itself provides the compile-time visibility to the type's representation required to diff --git a/content/courses/ada-idioms/chapters/controlling_obj_initialization_creation.rst b/content/courses/ada-idioms/chapters/controlling_obj_initialization_creation.rst index c55927b70..fee8fef68 100644 --- a/content/courses/ada-idioms/chapters/controlling_obj_initialization_creation.rst +++ b/content/courses/ada-idioms/chapters/controlling_obj_initialization_creation.rst @@ -57,8 +57,8 @@ is an access type, the automatic default value :ada:`null` initializes ... end Binary_Trees; -In both cases, simply declaring an object in the client code is -sufficient to ensure it is initially empty. +In both cases, simply declaring an object in the client code is sufficient to +ensure it is initially empty. However, not all abstractions have a meaningful default initial state. Default initialization will not suffice to fully initialize objects in these cases, so diff --git a/content/courses/ada-idioms/chapters/essential_idioms_for_packages.rst b/content/courses/ada-idioms/chapters/essential_idioms_for_packages.rst index 818c14a09..ddd6314da 100644 --- a/content/courses/ada-idioms/chapters/essential_idioms_for_packages.rst +++ b/content/courses/ada-idioms/chapters/essential_idioms_for_packages.rst @@ -19,11 +19,11 @@ Specifically, packages should exhibit high cohesion and loose coupling related to one another, in the context of the problem being solved. Unrelated entities should not be declared in the same module. This allows the reader to focus on one primary concept, which should be the subject of -the package. Coupling is the degree to which a module depends upon other -modules. Loose coupling enhances comprehension and maintenance because it -allows readers and future developers to examine and modify the module in -relative isolation. Coupling and cohesion are interrelated: higher -cohesion tends to result in less coupling. +the package. Coupling is the degree to which a module +depends upon other modules. Loose coupling enhances comprehension and +maintenance because it allows readers and future developers to examine and modify the +module in relative isolation. Coupling and cohesion are interrelated: +higher cohesion tends to result in less coupling. Solution diff --git a/content/courses/ada-idioms/chapters/inheritance_idioms.rst b/content/courses/ada-idioms/chapters/inheritance_idioms.rst index 7ee508803..c6b869aa7 100644 --- a/content/courses/ada-idioms/chapters/inheritance_idioms.rst +++ b/content/courses/ada-idioms/chapters/inheritance_idioms.rst @@ -48,11 +48,11 @@ we express that inheritance via derived types representing the categories and subcategories. Ada's strong typing ensures they are treated as disjoint entities. -Although the derived child type is distinct from the parent type, the -child is the same *kind* as the parent type. Some authors use *kind of* -as the name for the relationship between the child and parent. Meyer -uses the term *is-a* [1]_, a popular term that we will use too. For -example, a cat *is a* mammal, and also is an animal. +Although the derived child type is distinct from the parent type, the child is +the same *kind* as the parent type. Some authors use *kind of* as the name for the +relationship between the child and parent. Meyer uses the term *is-a* [1]_, a +popular term that we will use too. For example, a cat *is a* mammal, and also is +an animal. The fundamental difference between :ref:`Subtype Inheritance ` and @@ -87,11 +87,10 @@ declarations for derived types, providing considerable flexibility and expressive power for controlling the client's view of the child and parent types. -For example, in Ada, full dynamic OOP capabilities require type -declarations to be decorated with the reserved word :ada:`tagged`. However, -from its earliest days, Ada has also supported a static form of -inheritance, using types that are not tagged. The solution we describe -below works with both forms of inheritance. +For example, in Ada, full dynamic OOP capabilities require type declarations to +be decorated with the reserved word :ada:`tagged`. However, from its earliest days, +Ada has also supported a static form of inheritance, using types that are not +tagged. The solution we describe below works with both forms of inheritance. The developer also has a choice of whether the parent type and/or the child type is a private type. Using private types is the default design choice, for the @@ -208,11 +207,12 @@ then show the two idiom expressions in separate subsections. - Whether the child type is visibly derived will vary with the :ref:`inheritance idiom ` solution. -To avoid unnecessary code duplication, we use the same parent type, -declared as a simple tagged private type, in the examples for the two idiom -solutions. The parent type could itself be derived from some other tagged -type, but that changes nothing conceptually significant. We declare parent -type in package :ada:`P` as follows: +To avoid unnecessary code duplication, we use +the same parent type, declared as a simple tagged private type, in the examples +for the two idiom solutions. The parent type +could itself be derived from some other tagged type, but that changes nothing +conceptually significant. We declare parent type in package :ada:`P` as +follows: .. code-block:: ada @@ -248,12 +248,12 @@ part of the package: type Child is new Parent with record ... end record; end Q; -The primitive operations from the parent type are implicitly declared -immediately after the private extension declaration. That means those +The primitive operations from the parent type are implicitly +declared immediately after the private extension declaration. That means those operations are in the visible part of the package, hence clients can invoke them. Any additional operations for the client interface will be explicitly -declared in the visible part as well, as will any overriding declarations -for those inherited operations that are to be changed. +declared in the visible part as well, as will any overriding declarations for +those inherited operations that are to be changed. For example, here is a basic bank account :ref:`ADT ` that we will use as the parent type @@ -466,9 +466,9 @@ For example, we might use a *controlled type* in the implementation of a tagged private type. These types have procedures :ada:`Initialize` and :ada:`Finalize` defined as primitive operations. Both are called automatically by the compiler. Clients generally don't have any business directly calling -them so we usually use implementation inheritance with controlled types. -But if clients did have the need to call them we would use Subtype Inheritance -instead, to make them visible to clients. +them so we usually use implementation inheritance with controlled types. But if +clients did have the need to call them we +would use Subtype Inheritance instead, to make them visible to clients. For example, the following is a generic package providing an abstract data type for unbounded queues. As such, the :ada:`Queue` type uses dynamic allocation diff --git a/content/courses/ada-idioms/chapters/programming_by_extension.rst b/content/courses/ada-idioms/chapters/programming_by_extension.rst index ecb393426..22346c73f 100644 --- a/content/courses/ada-idioms/chapters/programming_by_extension.rst +++ b/content/courses/ada-idioms/chapters/programming_by_extension.rst @@ -87,7 +87,7 @@ with the stack state declared in the package body: We could add the private part to the package declaration and move the state of the :ref:`ADM ` |mdash| the two variables in -this case |mdash| up there without any other changes. The subprogram bodies +this case |mdash| up there without any other changes. The subprogram bodies have the same visibility to the two variables either way. (There is no requirement for the :ada:`Content` type because :ada:`Values` is not a record component; anonymously-typed array objects are legal.) From the viewpoint of