ATOOD - Project
Patternwizard
Developer-Guide
v0.2
2.3 Functionality
of the application
5.1 Adding
a new design-pattern
5.1.1 Code generator
specific tasks
This guide describes the Pattern Wizard Tool from the developer viewpoint. The overall design, as well as the design of the single clusters are depicted, containing the description of the representative classes. Extendibility of the Pattern Wizard was one of the main goals in the design of the tool. Genericity and decentralization were also aimed at, as described in Chapter 2 of this document.
The single clusters are documented in Chapter 3. The functionality of the classes is shown in detail for representative classes of the clusters, in order to facilitate the extension of the tool to further patterns. Extendibility is discussed in Chapter 5.
Additionally, testing of the tool on sample classes is documented.
For the development and implementation the EiffelStudio development environment, with EiffelStudio 5.3 as the new compiler of the ISE Eiffel environment and the new graphical builder EiffelBuild, were used.
The user is expected to be familiar with design patterns. For an overview of the tool’s functionality please also refer to the user guide.
The
specification of the Pattern Wizard Tool covers the following:
-
Design
and implementation of a design pattern wizard (GUI and “business model”) in
Eiffel
-
5
design patterns
(Abstract Factory, Factory Method, Builder, Prototype, Singleton)
-
Should
be extendible to all design patterns described by Gamma et al.
-
Ability
to select design pattern and classes to be generated
-
Once
the user has pressed the button “Create” (whatever you call it), the wizard
should generate the corresponding code and be closed;
-
If
the user presses “Cancel”, the wizard should be closed without generating any
code.
The Tool was not aimed to have software correction facilities and inconsistent input classes likely produce an exception during the execution of the Pattern Wizard Tool. Consistency checking is limited to feature name duplication. If features with the same name exist in the code, a warning appears in the result report of the tool and a comment is added in the correspondent class text.
The tool consists of one GUI window whose content is updated for each design pattern. Generally three kinds of views can be identified:
The design of SW was done with respect to small interfaces between the different clusters.
Following graphic illustrates an overall view of the software architecture :
Figure 1 : Overall view
of the SW-architecture
The application is started in the main part. Afterwards the remaining clusters take the lead of the application.
First the initial window is drawn. The user interacts through the GUI with the STATES cluster (layer) and activates the next state in the transition list. As soon as a user activity (usually clicking the ‘create’ button) occurs the CODEGENERATOR is activated, which will occasionally use the PARSER to validate already existing classes or generate new source files
The pattern application proceeds in two steps. First strings containing the pattern specific code are produced. In case the pattern is applied to an existing class, both the code and the class are parsed, and merged clause by clause into one class.
The application is divided into five parts (also called clusters)
- Main or root cluster
- States
- Gui
- Code Generator
- Parser
The data flow between these clusters and the file system and the result report (singleton instance) is shown in the following picture.
Figure 2: Data flow
diagram
The main part is responsible to start the application and to pop up the first window (initial window). After that, the actions on the user interface determine the process of the application and the agents behind them keep the whole application alive. Agents are "operation wrapper" objects that are mainly associated in this application with certain events of the user interface, such as a mouse click on a button.
Implements the functionality of the windows and the transition between them.
It is implemented with the ‘state’ design pattern. Every window created by the application is the client of a state. The user interacts with the GUI and launches new states by pushing a button ‘do’, ‘cancel’, etc.
Cluster GUI contains the implementation of the graphical user interface windows;
The builder cluster can be divided into 3 main parts:
- The classes IMPLEMENTATION_DESCRIPTOR, CODE_ENGINE and CLASS_BINDING: the first is the interface between the GUI and the code generating classes and hands over the user input data. CODE_ENGINE implements the features to create the pattern-specific code, and starts the parser in the case that classes are present in the assigned cluster. CLASS_BINDING is a helper class to the Code_engine.
- The subcluster VARIABLES: Contains the user input information, specific to the pattern.
- The subcluster STRING_CLASSES: Predefined pattern code, deposited in Eiffel classes, with the functionality of variable-replacement and code repetition.
A parser for eiffel classes, with the capability to merge two classes with the same class name into one final class.
As it is
mentioned above, the state design pattern is used for this application. The
code of the pattern resides in the cluster STATES. The State pattern is useful when you want to
have an object represent the state of an application, and you want to change
the state by changing that object. The benefit of the State pattern is that
state-specific logic is localized in objects that represent that state.
This cluster contents most of functionality of the application. A state has a window and can react to user inputs as mouse clicks on buttons, dropdown menus etc.
Figure 2: State pattern
used in the application
All existing states are stored in the array ‘states’ in the STATE_LIST. The state transformation (from one state to another one) depending on the user’s input is defined in the array ‘transition’. When the application is started, both arrays (states and transition) are initialized. The application starts with the state STATE_INTIAL (started with feature execute_first_session). There exists no central loop cycle which deals with all user activities and step further to the next state. But there are agents, which handles the user events and activates the next state by calling ‘execute_next_session’ from STATE_LIST.
(So feature ‘execute_next_session is a callback function)
The transition from one state to the other is defined in a matrix. Following table illustrates the transition:
Note: target and source are type of states
Table 1: Transitions choice dependency
Target |
choice = label |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
state source |
|
|
|
|
|
|
|
|
|
|
1 initial |
|
1 |
2 |
4 |
6 |
7 |
8 |
|
|
10 |
2 abstract factory I |
|
1 |
2 |
4 |
6 |
7 |
8 |
3 |
1 |
10 |
3 abstract factory II |
|
1 |
2 |
4 |
6 |
7 |
8 |
9 |
1 |
10 |
4 factory method I |
|
1 |
2 |
4 |
6 |
7 |
8 |
5 |
1 |
10 |
5 factory method II |
|
1 |
2 |
4 |
6 |
7 |
8 |
9 |
1 |
10 |
6 builder |
|
1 |
2 |
4 |
6 |
7 |
8 |
9 |
1 |
10 |
7 prototyp |
|
1 |
2 |
4 |
6 |
7 |
8 |
9 |
1 |
10 |
8 singleton |
|
1 |
2 |
4 |
6 |
7 |
8 |
9 |
1 |
10 |
9 result |
|
1 |
2 |
4 |
6 |
7 |
8 |
1 |
1 |
10 |
10 final |
|
|
|
|
|
|
|
|
|
|
Table 2 : User choices or labels
Label |
Type of
GUI component |
number |
Please Choose |
1. list
item in drop down menu |
1 |
abstract factory |
2. list
item |
2 |
factory method |
3. list
item |
3 |
Builder |
4. list
item |
4 |
prototyp |
5. list
item |
5 |
singleton |
6. list
item |
6 |
Create,
Proceed |
Button |
7 |
Cancel,
New |
Button |
8 |
Quit |
Button |
9 |
For example:
The User sees the first window of the state STATE_FACTORY_METHOD_INITIAL. This is the source state 4 (factory method I). The User clicks now the button ‘Proceed’ at the bottom of the window (Label Create, Proceed). This label activity has the number 7. So according the matrix list the next state is number 5 which is the state factory II .
Following graphic shows a regular window with all possible user choices (labels):
7 8 9 1 2 3 4 5 6
Figure 3: User Choices
During the lifetime of the application there exists only one window reference. In order to change the content of this window the memory block to which the reference refers to has to be overwritten. The reference for this window is kept in a singleton called GUI_OBJECTS. Other user data that has to live over different states (like the cluster name) are also stored in this object.
Following picture shows the dependencies of the application with the GUI cluster.
Figure 4: class diagram
of the Gui cluster
Each state has an own window type and because all windows need similar functionalities a deferred class WINDOW is introduced
At startup the state STATE_INITIAL is the state that defines the startup view of the application. The startup (or initial) view is created with the object of type WINDOW_INITIAL.
Following picture shows the dependencies between some state-classes and the according window-classes:
Figure 5: class diagram
of the State cluster
The purpose of this cluster is to provide functionality to parse existing class files. It differentiates between clusters, classes and features. A cluster is mainly a list of classes and provides functionality like opening a cluster from a directory, save a cluster (i.e. all its class files). A class object is a representation of a class file. Class representation is divided into indexing, class name, inheritance, creation, list of feature objects and invariants. All of these are individually accessible and settable. Furthermore a class can be set expanded, separate or deferred. As a special feature, an instance of class_obj can be merged with another instance of class_obj. Thereby indexing, inheritance, creation and invariants are joined. Features are joined only if they have unique names, otherwise both features are added to the class. This requires the user to manually change one of the two names or merge them before compiling the generated code. An added comment to one of the features indicates the duplicated feature.
A feature object finally represents a single feature. It is divided into name, export status, clause (e.g. -- Access), arguments, return type, comment, preconditions, local variables, postconditions and do clause. All of them can be set or read. Furthermore a feature object can be set deferred or once.
The dependencies among the classes are shown in Figure 6. Please refer to the Appendix A for the listings of the functions of each class.
Figure 6: class diagram
of cluster ‘parser’
CLASS_OBJ: inherits from class OBJ. It represents a class object. On creation a file or string that represent a class text is parsed and divided into its key parts.
SCAN: is a deferred class. Is ancestor of CLASS_SCAN and FEATURE_SCAN. This class inherits from SCANNING and ARGUMENTS, two classes from the lex library cluster. On creation of an instance of this class, my_Eiffel_lex is – if not already existing – created. Attributes indicate whether a file or a string should be parsed. A few features help to move to a desired position within the text.
The VARIABLES classes are pattern-specific and contain the default class-names for a pattern, which may be exchanged by the Gui application. This class serves as interface between the Gui and the code-generating classes. The purpose is to provide the default class-names to the Gui, and to provide the user input to the code-generating classes.
Figure 6: class diagram of cluster ‘variables’
VARIABLE_LIST: The name of the classes, which shall be extended with the same features of the pattern are stored in a list of strings. VARIABLE_LIST indeed inherits from the library class LINKED_LIST[G], with the actual generic parameter STRING. The functionality added to this class is to allow only class-name valid entries, and no repeated entries (case un-sensitive); empty strings are also not allowed. The query for checking for class-name validity is inherited from VALIDATION, used for the user input validation in the GUI. All strings are stored in uppercase. The list cannot be completely emptied.
The ADT of this class depicts its main functionalities. The implementation of the axioms, which are not inherited from the library class, is referenced in the source code by the axiom’s numeric identifier.
Table 3 :
ADT of VARIABLE_LIST
Type: |
VARIABLE_LIST (queue, entries with only one occurrence
are allowed ) |
Functions: |
Creation new -> VARIABLE_LIST Commands extend: VARIABLE_LIST * STRING
|-> VARIABLE_LIST reset_to VARIABLE_LIST * STRING
|-> VARIABLE_LIST Queries item : VARIABLE_LIST |->
STRING count : VARIABLE_LIST ->
INTEGER empty : VARIABLE_LIST ->
BOOLEAN has : VARIABLE_LIST *
STRING -> BOOLEAN occurrence: VARIABLE_LIST * STRING ->
BOOLEAN |
Preconditions: |
item( vlist ) requires not
empty( vlist ) put( vlist, s ) requires s
/= void and is_valid_classname(s) and not has( s ) reset_to( vlist, s )
requires s /= void and is_valid_classname(s) |
Axioms: |
1) empty( new ) 2) not empty( put( vlist,
s ) ) 3) not empty(reset_to(
vlist, s ) ) and count( reset_to(
vlist, s ) ) = 1 4) item( put(vlist,
s) ) = s if empty(vlist) , = item(
vlist ) otherwise 5) remove( extend( vlist ,
s ) = vlist if empty( vlist ), = extend ( remove(
vlist ), s ) otherwise 6) reset_to( put( vlist, s
), s’ ) = reset_to( vlist, s’ ) 7) occurrence( item( vlist
) ) = 1 8) has( put( vlist, s), s
) |
VARIABLES: is a deferred class. The container for objects of type VARIABLE_LIST is a hash table with pattern-specific keys, implemented in the heirs of this class.
The variable lists of these classes are used in the string classes for the code generation and may therefore not be empty, in order to guarantee the correct text output of the string classes.
For the list of functionalities please refer the following ADT – description
Table 4 : ADT of VARIABLES
Type: |
VARIABLES |
Functions: |
Creation new : -> VARIABLES Command item : VARIABLES * KEY |->
VARIABLE_LIST reset_to : VARIABLES
* STRING * KEY |-> VARIABLES replace : VARIABLES * VARIABLE_LIST *
KEY |-> VARIABLES new_binding VARIABLES * VARIABLE_LIST *
STRING -> VARIABLES Query empty : VARIABLES
-> BOOLEAN has : VARIABLES
* KEY -> BOOLEAN |
Preconditions: |
new_binding(var,list, k)
requires k /= void and not empty(k) replace(var, vlist, k)
requires has( var, k ) and vlist /= void and not empty(vlist) item(var, k) requires
has(var, k) reset_to(var, s, k)
requires has(var, k) |
Axioms: |
1) not
empty(new) -- class inv 2) item(put(var,
vlist, k), k) = vlist 3) reset_to(replace(var,
vlist, k), s, k) = replace( var,
reset_to( vlist, s ), k ) 5) |
In the descendants, the keys are created during instantiation of the object, and also default class-names are given for each key. The access to the hash table is allowed only to features of the class, whose name explicitly shows the kind of variable that is retrieved or is set by the call of the feature. The keys are unchangeable for most of the patterns implemented (FACTORY_METHOD_VARIABLES, PROTOTYPE_VARIABLES, SINGLETON_VARIABLES, BUILDER_VARIABLES). In the case of the Abstract Factory Pattern, discussed next, the keys had to be adapted to the user input. The class invariant for proper keys was implemented by the hidden feature ‘keys_set’, adusted to each pattern.
ABSTRACT_FACTORY_VARIABLES: these variables have the keys for the ‘Abstract Factory Pattern’ and may serve as an example for the implementation of ‘extendible’ keys: they may be extended during the lifetime of the object. Indeed the number of product classes is 2 after creation and may be changed afterwards. If the number of product classes is reduced during the object’s lifetime, keys, which are not needed, are simply not used in the code generation. Otherwise keys and their default content are dynamically added.
The class-hierarchies were reflected in the dynamic adaptation of the names of the concrete classes to the class names of the abstract classes, implemented in the setter methods of the class-names of the deferred classes. This was also reasonable from the viewpoint of the user interface (GUI), as entering of the names of the concrete class-names was possible only after the abstract class-names were set.
The purpose of this cluster is to provide the pattern specific code to the parser. The code for the Patterns ‘Abstract Factory’, ‘Factory Method’, ‘Builder’, ‘Prototype’, ‘Singleton’ is made retrievable.
STRING_CLASS : is a deferred class. It contains the class text, needed by the code generating classes.
Each class of the pattern corresponds to an heir of string class. The code is deposited in strings, which contain the ‘pattern variables’ in entities. The variables are, as predictable, contained in instances of the class VARIABLE_LIST. They serve to adapt the content to the user input. Furthermore code repetition is implemented by the iteration over the list.
For the main functionalities please refer also to the ADT of this type
Table 5 : ADT of VARIABLE_LIST
Type: |
STRING_CLASS |
Functions: |
creation new : VARIABLES |->
STRING CLASS command generate_all_strings :
STRING CLASS -> STRING CLASS text : STRING CLASS |->
STRING all_generated: STRING CLASS -> BOOLEAN set_classname : STRING CLASS * STRING |->
STRING_CLASS classname : STRING CLASS |-> STRING set_inheritance : STRING CLASS * STRING |->
STRING_CLASS inherit_from : STRING CLASS |-> SET[ STRING
] variables : STRING CLASS -> VARIABLES query equal STRING CLASS * STRING CLASS ->
BOOLEAN |
Preconditions: |
new( var ) requires var /=
void and not var.is_empty text ( sc ) requires
all_generated ( sc ) set_classname( sc, s )
requires s /= void and not empty(s) |
This cluster has the control over the code generation.
First, IMPLEMENTATION_DESCRIPTOR hands over the user input information, which are:
- the kind of pattern to generate (‘pattern_no’),
- the cluster where to save the resulting classes (‘cluster_name’) and
- the pattern-specific class-names, contained in the ‘variables’ of the class.
The report of the code generating actions is saved in string ‘result-report’ in this class, and is written by the CODE_ENGINE and by the parser classes CLUSTER_OBJ and CLASS_OBJ. The IMPLEMENTATION_DESCRIPTOR was implemented as a singleton to be easily accessed from the GUI implementation and the parser classes.
CODE_ENGINE executes the generation of the code. The procedure ‘create pattern’ chooses the right feature to execute. First, the code implemented in the string-classes is processed, and the items of the ‘variables’ are replaced by the actual values. If classes exist in the target cluster, they are parsed and deposited in CLASS_OBJ and FEATURE_OBJ objects. In the case of identical class-names, the generated code is also parsed, and the contents of the two classes are merged, as described in the Parser section.
A helper class CLASS_BINDING facilitates the writing of the procedure for the code generation. Cloning of the objects of type STRING_CLASS ensures avoidance of erroneous overwriting of entities of the class. The procedure ‘create_subclass’ helps to correctly implement the class hierarchies in the pattern.
Figure 7: class diagram of cluster ‘code_generator’
The test suite is organized in the following way: In a directory called test_suite there exists a subfolder for each design pattern implemented. For each design pattern there again exist two subfolders: new_cluster (not included unfortunately in the zip file delivered) and existing_cluster. The subfolder new_cluster is empty. To test the ability of the tool to generate code from scratch use this folder as cluster name of the appropriate design pattern in the tool. Results are then stored in this folder. Check the results against your manually validated code.
To test the ability of the tool to work on existing code, use the example classes in the directory existing_cluster. These example classes will be edited. A copy of the original classes will automatically be stored. Make sure you delete the edited files and remove the .bak extension of the backup files before you test again.
For the Pattern the following classes are prepared
- Abstract Factory : a toy_factory.e and a military_factory.e may get concrete factories, and use the Pattern Wizard to implement the abstract factory and the product hierarchies.
- Factory Method: my_producer.e is can be used to produce an object of a type of your choice.
- Builder : the classes new_house.e shall be linked to windows.e and walls.e by the product – parts client relation of the Builder pattern, adding also the classes to implement the builder itself.
- For Prototype a ferrari.e expects you to make e prototype out of it and for the Singleton Pattern feel free to use the my_car.e or keys.e classes.
The results are shown in the appendix B of this guide.
- Implement a new class heir of VARIABLES, containing the features to obtain and set the class names of this pattern.
- Extend the Pattern Constants by the default class-names, used to implement the initialization feature ‘init_bindings’ in the new Variables-class.
- Implement a string class for each class of the pattern.
o Copy the pattern-class text to the STRING_CLASS text, and assign the clauses to the features indexing_string, create_string, feature_string, etc. ( use the string delimiters “[ and ]” for multiline strings ).
o Implement a local entity of type VARIABLE_LIST for every exchangeable class name, which appears in the text of this class. Use these entitites for string replacement and code repetition.
o Implement a ‘make_default’ creation procedure of the STRING_CLASS, where default values are set for the class-name, and the inherited class.
- Implement the create-procedure in the CODE_ENGINE class, by creating objects for all type of string-classes in the pattern ( ‘make_default’). Instantiate an object of class CLASS_BINDING from a STRING_CLASS by assigning the class name, and use the ‘create_subclass’ of procedure for implementing class-hierarchies
- Extend the Application Constant
- Extend the set_pattern feature of the IMPLEMENATION_DESCRIPTOR with the creation procedure for the new VARIABLES – class and the assignment of the pattern_no.
- Each state has an own window. So before another design pattern is added, it should be clear how many windows with pattern specific property fields are used.
- Each of these state classes has to be inherited from STATE_PARENT.
- Adapt feature ‘display’ to display the start values in the GUI
- Adapt feature ‘do_action’ to your needs
- Add a new window class and inherit it from class WINDOW, which is the deferred class with general functionality for all used windows
- Add setter and getter functions for all texts (usually used in input boxes) which could be changed by the user
The application can easily be adapted to any need of Eiffel source refactoring.
We tested the application repeatedly and found no bugs. All sources could be generated or adapted properly.
-
The interaction between STATES and
CODE_GENERATOR could be improved. At the moment a state object has the lead
about parser and code_generator., especially it has to know everything about
the exported features of the IMPLEMENTATION_DESCRIPTOR. More suitable would
have been to use a generic array for parameter delivery. A solution could also
have been to send directly the GUI-components to the CODE_GENERATOR cluster.
Another improvement would be to combine all the GUI components which can be
changed by the user and all variables types of the CODE_GENERATOR.
- more decentralized code creation procedure
Appendix A
ADT Specifications
(incomplete)
Type: |
OBJ |
Functions: |
name: OBJ ® STRING set_name: OBJ ´ STRING ® OBJ merge: OBJ ´ OBJ ® OBJ |
Type: |
CLUSTER_OBJ |
Functions: |
name: CLUSTER_OBJ ® STRING merge: CLUSTER_OBJ ´ CLUSTER_OBJ ® CLUSTER_OBJ make_new: CLUSTER_OBJ make_from_directory: STRING ® CLUSTER_OBJ last_found: CLUSTER_OBJ ® CLASS_OBJ has_class: CLUSTER_OBJ ´ STRING ® BOOLEAN save: CLUSTER_OBJ ® CLUSTER_OBJ save_as: CLUSTER_OBJ ® CLUSTER_OBJ |
Type: |
CLASS_OBJ |
Functions: |
make_new: CLASS_OBJ make_from_file: STRING ® CLASS_OBJ make_from_string: STRING ® CLASS_OBJ text: CLASS_OBJ ® STRING name: CLASS_OBJ ® STRING name_clause: CLASS_OBJ ® STRING inheritance_clause: CLASS_OBJ /® STRING creation_clause: CLASS_OBJ /® STRING feature_clause: CLASS_OBJ /® STRING feature_by_name: CLASS_OBJ ´ STRING /® FEATURE_OBJ invariant_clause: CLASS_OBJ /® STRING last_found: CLASS_OBJ /® FEATURE_OBJ has_feature: CLASS_OBJ ´ STRING ® BOOLEAN has_exported_feature: CLASS_OBJ ´ STRING ® BOOLEAN has_createion_feature: CLASS_OBJ ´ STRING ® BOOLEAN is_deferred: CLASS_OBJ ® BOOLEAN is_expanded: CLASS_OBJ ® BOOLEAN is_separate: CLASS_OBJ ® BOOLEAN add_indexing: CLASS_OBJ ´ STRING ® CLASS_OBJ add_default_indexing: CLASS_OBJ ® CLASS_OBJ add_inheritance: CLASS_OBJ ´ STRING ® CLASS_OBJ add_creation: CLASS_OBJ ´ STRING ® CLASS_OBJ add_creation_list: CLASS_OBJ ´ LIST[STRING] ® CLASS_OBJ add_feature: CLASS_OBJ ´ FEATURE_OBJ ® CLASS_OBJ add_feature_list: CLASS_OBJ ´ LIST[FEATURE_OBJ] ® CLASS_OBJ add_invariant: CLASS_OBJ ´ FEATURE_OBJ ® CLASS_OBJ set_deferred: CLASS_OBJ ® CLASS_OBJ set_expanded: CLASS_OBJ ® CLASS_OBJ set_separate: CLASS_OBJ ® CLASS_OBJ save: CLASS_OBJ ® CLASS_OBJ merge: CLASS_OBJ ´ CLASS_OBJ ® CLASS_OBJ sort_features_by_type: CLASS_OBJ ® CLASS_OBJ |
Axioms: |
NOT is_deferred AND is_expanded NOT is_deferred AND is_separate NOT is_expanded AND is_separate |
Type: |
FEATURE_OBJ |
Functions: |
make: FEATURE _OBJ text: FEATURE _OBJ ® STRING name: FEATURE _OBJ ® STRING export_status: FEATURE_OBJ
® STRING feature_clause: FEATURE _OBJ ® STRING argument: FEATURE _OBJ /® STRING return_type: FEATURE _OBJ /® STRING comment: FEATURE _OBJ /® STRING require_clause: FEATURE _OBJ /® STRING local_clause: FEATURE _OBJ /® STRING do_clause: FEATURE _OBJ /® STRING ensure_clause: FEATURE _OBJ /® STRING is_deferred: FEATURE _OBJ ® BOOLEAN is_once: FEATURE _OBJ ® BOOLEAN add_export_status: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_export_status_list: FEATURE _OBJ ´ LIST[STRING] ® FEATURE _OBJ set_feature_clause: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_argument: FEATURE _OBJ ´ STRING ® FEATURE _OBJ set_return_type: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_require: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_local: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_comment: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_do: FEATURE _OBJ ´ STRING ® FEATURE _OBJ add_ensure: FEATURE _OBJ ´ STRING ® FEATURE _OBJ set_once: FEATURE _OBJ ® FEATURE _OBJ set_deferred: FEATURE _OBJ ® FEATURE _OBJ merge: FEATURE _OBJ ´ FEATURE _OBJ ® FEATURE _OBJ |
Type: |
SCAN |
Functions: |
make: SCAN from_file: SCAN ® BOOLEAN from_string: SCAN ® BOOLEAN set_from_file: SCAN ® SCAN set_from_string: SCAN ® SCAN goto_keyword: SCAN ´ INTEGER ® SCAN goto_next_keyword: SCAN ® SCAN goto_next_token_of_type: SCAN ´ ARRAY[INTEGER] ® SCAN goto_next_token_not_of_type: SCAN ´ ARRAY[INTEGER] ® SCAN |
Appendix B
Test Result
Here the results of the application of the Builder Pattern to the prepared classes new_house.e, walls.e and windows.e, as described in the Test Suite Section, are shown:
Builder Pattern
Original
classes
indexing
description: "[
My new house
]"
class
NEW_HOUSE
create
make
feature -- Initialization
make is
do
is_new := true
ensure
is_new_house: is_new
end
feature -- Access
walls : WALLS
feature -- Access
windows : WINDOWS
feature -- Status report
is_new :BOOLEAN
invariant
is_a_new_house : is_new
end
-- class NEW_HOUSE
-----------------------------------------------------------------------------------------
indexing
description: "[
Windows of a new house.
]"
class
WINDOWS
feature
-- Measurement
cost : INTEGER is
-- cost of the windows
do
Result := number * 10
if bright then
Result := Result * 2
end
end
feature
-- Status report
bright : BOOLEAN
number : INTEGER
feature
-- Status setting
set_bright is
-- are the windows bright
do
bright := true
ensure
bright : bright
end
set_number ( n : INTEGER ) is
-- how many windows
do
number := n
ensure
number_set : number = n
end
end
-----------------------------------------------------------------------------------------
indexing
description: "[
Walls of a new house.
]"
class
WALLS
feature
-- Measurement
cost : INTEGER is
-- the cost of these walls
do
Result := height *
thickness
end
feature
-- Status report
height : INTEGER
thickness : INTEGER
feature
-- Status setting
set_height ( h : INTEGER ) is
-- how high shall the walls
be
do
height := h
ensure
height_set : height = h
end
set_thickness ( t : INTEGER ) is
-- how thick shall the
walls be
do
thickness := t
ensure
thickness_set : thickness =
t
end
end
Result
report
+++++ REPORT OF THE PATTERN CREATION
PROCESS +++++
Starting
the creation of the Builder Pattern code.
Creating
class WINDOWS ... done.
Creating
class WALLS ... done.
Creating
class NEW_HOUSE ... done.
Creating
class HOUSE_BUILDER ... done.
Creating
class MY_HOUSE_BUILDER ... done.
Creating
class CLIENT ... done.
Parsing
object:
T:\fabrizipgeordnet\eiffel\test_suite\builder\existing_cluster\WINDOWS.e...done.
Parsing
object: T:\fabrizipgeordnet\eiffel\test_suite\builder\existing_cluster\WALLS.e...done.
Parsing
object:
T:\fabrizipgeordnet\eiffel\test_suite\builder\existing_cluster\NEW_HOUSE.e...done.
Backing
up 'WINDOWS' ... done.
Merging
'WINDOWS' and 'WINDOWS' ... done.
Saving
and closing T:\fabrizipgeordnet\eiffel\test_suite\builder\existing_cluster\WINDOWS.e ... done.
Backing
up 'WALLS' ... done.
Merging
'WALLS' and 'WALLS' ... done.
Saving
and closing
T:\fabrizipgeordnet\eiffel\test_suite\builder\existing_cluster\WALLS.e ... done.
Backing
up 'NEW_HOUSE' ... done.
Merging
'NEW_HOUSE' and 'NEW_HOUSE' ... WARNING: Feature 'windows' is duplicated.
Manual merge required
WARNING:
Feature 'walls' is duplicated. Manual merge required
done.
Saving
and closing
T:\fabrizipgeordnet\eiffel\test_suite\builder\existing_cluster\NEW_HOUSE.e ... done.
Task
fulfilled !
Press
'New' to start again or 'Quit' to leave.
Resulting
classes
indexing
description: "[
Notify the builder when a
new product should be built.
]"
note: "Implementation of the Builder
design pattern "
class
CLIENT
create
make
feature
{NONE} -- Initialization
make
(a_builder: like my_house_builder ) is
-- Set 'my_house_builder to
'a_builder'.
require
builder_not_void
: a_builder /= Void
do
my_house_builder
:= a_builder
ensure
my_house_builder_set:
my_house_builder = a_builder
end
feature
-- Access
my_house_builder
: HOUSE_BUILDER
-- Product builder
feature
-- Basic Operations
build
is
-- Call 'my_house_builder'
to construct a new new_house
do
my_house_builder.build
ensure
new_house_not_void :
my_house_builder.last_new_house /= Void
windows_not_void :
my_house_builder.last_new_house.windows /= Void
walls_not_void :
my_house_builder.last_new_house.walls /= Void
end
invariant
my_house_builder_not_void :
my_house_builder /= Void
end
-----------------------------------------------------------------------------------------
indexing
description: "[
Build product part by
part.
]"
note: "Implementation of the Builder
design pattern "
deferred
class HOUSE_BUILDER
feature
-- Access
last_new_house:
NEW_HOUSE is
-- new_house under
construction
deferred
end
feature
-- Basic operations
build
is
-- Create and build `last_new_house'.
do
build_new_house
build_windows
build_walls
ensure
last_new_house_not_void
: last_new_house /= Void
windows_not_void
: last_new_house.windows /= Void
walls_not_void
: last_new_house.walls /= Void
end
-----------------------------------------------------------------------------------------
indexing
description: "[
Build product part by
part.
]"
note: "Implementation of the Builder
design pattern "
class
MY_HOUSE_BUILDER
inherit
HOUSE_BUILDER
feature
-- Access
last_new_house:
NEW_HOUSE
-- new_house under
construction
feature {NONE} -- Implementation
build_new_house
is
-- Create and build
`last_new_house'.
do
debug
io.put_string ("
Create new new_house.%N")
end
create last_new_house
end
build_windows
is
-- Build part 1 of the
product.
do
last_new_house.set_windows
( create {WINDOWS})
debug
io.put_string ("
Set new_house part 1.%N")
end
end
build_walls
is
--
Build part 2 of the product.
do
last_new_house.set_walls (
create {WALLS})
debug
io.put_string ("
Set new_house part 2.%N")
end
end
end
-----------------------------------------------------------------------------------------
indexing
description: "[
My new house
]"
description: "[
Product to be created by
the concrete builder
]"
note: "Implementation of the Builder
design pattern "
class
NEW_HOUSE
create
make
feature -- Initialization
make is
do
is_new := true
ensure
is_new_house: is_new
end
feature -- Access
walls :WALLS
feature -- Access
windows :WINDOWS
feature -- Access
windows :WINDOWS -- WARNING:
duplicated feature! Remove one version or merge the two features manually
feature -- Access
walls :WALLS -- WARNING:
duplicated feature! Remove one version or merge the two features manually
feature -- Status report
is_new :BOOLEAN
feature
{HOUSE_BUILDER} -- Status setting
set_windows( part_1: like windows) is
-- set 'windows' to
'part_1'.
require
part_1_not_void: part_1 /=
Void
do
windows:= part_1
ensure
windows_set: windows =
part_1
end
feature
{HOUSE_BUILDER} -- Status setting
set_walls( part_2: like walls) is
-- set 'walls' to 'part_2'.
require
part_2_not_void: part_2 /=
Void
do
walls:= part_2
ensure
walls_set: walls = part_2
end
invariant
is_a_new_house : is_new
end
-- class NEW_HOUSE
-----------------------------------------------------------------------------------------
indexing
description: "[
Walls of a new house.
]"
description: "[
Part of product to be
created
by the concrete
builder
]"
note: "Implementation of the Builder
design pattern "
class
WALLS
feature -- Measurement
cost :INTEGER is
-- the cost of these walls
do
Result := height *
thickness
end
feature -- Status report
height :INTEGER
feature -- Status report
thickness :INTEGER
feature -- Status setting
set_height( h : INTEGER ) is
-- how high shall the walls
be
do
height := h
ensure
height_set : height = h
end
feature -- Status setting
set_thickness( t : INTEGER ) is
-- how thick shall the
walls be
do
thickness := t
ensure
thickness_set : thickness =
t
end
end
-- class WALLS
-----------------------------------------------------------------------------------------
indexing
description: "[
Windows of a new house.
]"
description: "[
Part of product to be
created
by the concrete
builder
]"
note: "Implementation of the Builder
design pattern "
class
WINDOWS
feature -- Measurement
cost :INTEGER is
-- cost of the windows
do
Result := number * 10
if bright then
Result := Result * 2
end
end
feature -- Status report
bright :BOOLEAN
feature -- Status report
number :INTEGER
feature -- Status setting
set_bright is
-- are the windows bright
do
bright := true
ensure
bright : bright
end
feature -- Status setting
set_number( n : INTEGER ) is
-- how many windows
do
number := n
ensure
number_set : number = n
end
end
-- class WINDOWS