Here we go now with your first pyswarm application. One approach, top-down, would be to sketch the structure of our system prior to modeling the fine details.
Before modeling the first pyswarm application, we need to create the XMI file in which the UML model will be stored. We will use the pyswarm master XMI file which is part of the downloaded pyswarm distribution. This master file is almost empty, except of some basic elements which are required for all pyswarm projects, and we will expand during this tutorial. If you are finished with this project it should be the same as the example PetStore project in the projects directory.
|
Note |
|---|---|
|
If you don't want to create the entire system yourself, you may want to use directly the PetStore.xml file which is already completed. |
|
UML: Implementation diagram when finished this PetStore model.
|
Tip |
|---|---|
|
See also the Reference Manual for XMI. |
|
Start your UML CASE tool
You can either use the empty master XMI file pyswarm-master.xml which you find in the directory projects/pyswarm-master. This is for building yourself an own project from the first to the last element.
Or alternatively open the projects/PetStore/PetStore.xml, if you want to load the already completed XMI file.
->
Save as new XMI file, so you have the pyswarm profile included:
->
Change to a path of your choice (recommended is your projects directory) and create a new sub-directory where you can store your individual PetStore example.
Enter a new file name, e.g. PetStore.xml
Select Extensible Markup Language (*.xml)
Select: XMI 2.1 (is default)
De-select Rich XMI
Click on
If not already visible, open the Containment Tree panel, we will need it many times. ->
The top-level element in the tree is Data. Here all elements of our model file will go as child elements and their child elements. The implementation diagrams and class diagrams we are going to create later will reside as direct child elements of Data.
|
Tip |
|---|---|
|
See also the Reference Manual for Model. |
|
You may also see the UML Standard profile. We can ignore it for now.
You may also see the pyswarmProfile. Now open the profile, so the tree expands and you can see all the elements in the profile. There are stereotypes, datatypes, and some tag-definitions. We will learn very soon, how they are used.
|
Tip |
|---|---|
|
See also the Reference Manual for pyswarm Profile, Datatype, Stereotype, and Tag-Definition. |
|
You may also see a package named site-packages (or probably another name) which is stereotyped as «pyswarm.external» -- here you can model all external Python packages and modules you like to import in order to access them from your pyswarm application. The libary pyswarm_shared may be one of these packages. This feature is described later in this tutorial. You can ignore this package for now.
|
Tip |
|---|---|
|
See also the Reference Manual for External. |
|
Procedure 2.1. Creating a new UML model
Abstract
We create a package element which shall represent our system and will contain all elements of the application.
Right-click on the Data element.
->
Enter the name of your pyswarm system, e.g. petstore
|
Important |
|---|---|
|
For all package and componented elements use names with a lower-cased first letter. |
|
Next we want to open the specification dialog of this element to provide some more information. This is done by either
right-clicking the element and selecting or
left-clicking the element and pressing the Enter key.
The specification dialog contains a lot of information, depending on the type of the element. The left panel allows you to browse to the other groups of information, which can be displayed in the right panel. For now let us stay on the general information. On the right top corner you see a select field Properties which allows you to switch the amount of information displayed in the right panel, which also can be customized.
Switch by clicking on
The right panel should at least shows the Name field and the empty Applied Stereotype field. The name is already given by you. Now let us define an appropriate stereotype on this element, so the pyswarm Generator will recognize this element as the system element. This is easily done.
Click on the button right hand of the Applied Stereotype field. A dialog pop ups containing all stereotypes available in this project. Let us look for the «pyswarm.system» element. You can simply browse through the list to find the desired stereotype. Or you can start typing into the empty text field on the top of the dialog to jump directly to stereotypes matching your input.
Now click on the checkbox of the «pyswarm.system» stereotype, so it is selected.
Click on the button and the dialog disappears. You will see the «pyswarm.system» stereotype now in the field Applied Stereotype .
Now we need to specify the system in more detail. This is done by using so called tag-values . Some of the supported pyswarm stereotypes has tag-definitions . If you apply a stereotype to an appropriate element you may add respective tag-values to provide additional information to the Generator, depending on the element. Therefore let us have a look on the left panel of the specification dialog of our system element. There you will see Tags in the tree.
Click Tags on the left panel, so the right panel will show a list of all tag-definitions of the stereotypes in the pyswarm profile.
Expand the tag-definitions of the stereotype «pyswarm.system» and you will see all of them with values unset.
Left-click one of the tags, let us begin with title .
Click on the button
On the right hand, depending on the type of the tag-definition you selected, you will see an input field (or sometimes a select field). Type the title of your system: My Funny Pet Store
At input fields, click on the button to add the tag value. At select fields this usually is not necessary.
Tag the system element also with the other values the same way. Some of the tags are used by the generator to create package information for the Python setup files, e.g.: version, author, description.
We are finished with our first element. The next elements are created almost on the same way. Click on the button.
Procedure 2.2. Create a System
|
Tip |
|---|---|
|
See also the Reference Manual for System. |
|
It's rather obvious that each pyswarm system, as any other, is hosted at least on one node (=host, computer). I have never built a real pet store, but let us picture that our system will consist of two parts, where one will be hosted on a public server and one server in the office of the company. Of course a system can consist of a single node only, in our tutorial example we will use two nodes, but implement just one of them.
|
Tip |
|---|---|
|
See also the Reference Manual for Node. |
|
On a node we host a single sub-package or multiple sub-packages of a system. Sub-packages are particularily helpful to structure bigger projects. Sub-packages are child elements of nodes, and each of them consist of one or more components with the business logic and their database components. We want to host two sub-packages on the internet node.
|
Tip |
|---|---|
|
See also the Reference Manual for Sub-Package. |
|
Now we are getting closer to the pulsing heart of our application: the business logic. The logic component contains all information focused on a specific business domain, e.g. addresses, identities, orders, or articles. In these components the structure of these business entities are defined, as well as the logic how these entities are related to each other and how they are processed. A logic component does not store the persistent entities, since it is using its corresponding database component for this purpose, which is decribed in the next chapter. In this tutorial we will focus on the (rudimentary) adress management of the store.
|
Tip |
|---|---|
|
See also the Reference Manual for Logic Component. |
|
Right-click on the sub-package element contacts
-> (NOT Package here!)
Enter the name of the logic-component: adm
Apply the stereotype «pyswarm.component.logic»
Procedure 2.5. Create a Component with Business Logic
Each sub-package can have multiple logic-components. For our example a single logic-component is sufficient.
As mentioned in the previous section each logic-component requires a corresponding database-component where it can store its business information as soon as these information become persistent. The database component serves as a persistency layer for the business logic layer.
|
Tip |
|---|---|
|
See also the Reference Manual for Database Component. |
|
Right-click on the sub-package element contacts
-> (NOT Package here!)
Enter the name of the database-component: admDB
Apply the stereotype «pyswarm.component.database»
Now we need to specify the database-component in more detail. We use the related tag-definitions user and password which should be used by the logic-component to log into its database. The tagging works as described for the system element.
Also assign the other available tags of the database. The generator will create an initdb.py script for this system that will help us to initialize this database and the other databases in the system.
Procedure 2.6. Create a Component for Persistency
You have also the option to specify Python scripts that will be part of your application. By doing so the scripts are properly handled by the generated setup.py file.
|
Tip |
|---|---|
|
See also the Reference Manual for Script Component. |
|
At this point we already have created a some elements in our system and you probably wonder when the neat model can be visualized in a diagram. Okay, let us make a diagram. Before proceeding to the next steps, please ensure that you have the Containment Tree panel still visible and no dialog is opened, so the main panel is empty.
Click in the menu on ->
A dialog opens with an empty list since no implementation diagrams has been created for our project yet.
Click on the button
Another dialog opens with a list of all project's containments. Type the name of the implementation diagram: Im_overview
Select our model's top-level node Data as parent element of the diagram.
Click on the button
You are back on the previous dialog and now you can see our recently created diagram
Select the created diagram and click on the button
Now the main panel shows you an empty diagram. The tab on top informs you which diagram is currently shown.
In order to see the elements in the diagram click on the system element in the Containment Tree to drag and drop them to the empty diagram in the main panel.
Voi la, there is our system package. All provided tags may be already shown depending on your tool configuration. Let us complete the diagram.
Click on the internet node element in the Containment Tree to drag and drop it directly into the frame which the system package shows in the diagram. A blue frame will signal you that the node element will be dropped inside the system package. Keep in mind that the diagram is a visualization of the Containment Tree, it is not necessarily a one-to-one representation. Repeat with the other node element intranet. You probably need to resize or reposition the diagram elements to get enough space.
Drag and drop the contacts, core, shop and the logistics sub-packages into the internet node.
Drag and drop the adm and the admDB components into the contacts sub-package. All our elements are now visualized in the diagram.
Procedure 2.8. Create Implementation Diagram
We already learned that each logic-component depends on a single database-component in order to store its information persistently. There are multiple ways to use the CASE tool in order to define this dependency relationship. This can be done either by the specification dialogs of the components or by modeling in the diagram. Latter one is described following:
|
Tip |
|---|---|
|
See also the Reference Manual for Dependency (Repository). |
|
Re-arrange the model elements so the logic-component is beside its database-component.
In the tool-bar left-click on the icon Dependency , which is then locked and your mouse cursor changes from an arrow to a cross.
Move the mouse cursor over the adm component until the blue border appears.
Now left-click on the adm component, a black line appears which follows your mouse cursor.
Move the mouse cursor over the admDB component until the blue border appears.
Left-click on the admDB component. Now you see that a dependency relation is visible where the arrow points to admDB component, which means: adm depends on admDB.
The tool may places the dependency relation somewhere in the Containment Tree, but we need to ensure that the dependency relation is owned by the appropriate element and this is the sub-package.
In order to get the relation selected in the Containment Tree select from its context menu ( Alt-b ). The relation is selected in the Containment Tree .
If the listed relation doesn't reside in the folder contacts/relations just drag and drop the relation to this folder.
Also create a dependency from petstore-populate to adm component. The dependency should be owned by the Data element.
Procedure 2.9. Create Dependency between Components
Well done, the big sketch of the system is finished and we already have an implementation diagram. Let us continue with the real interesting stuff - the business logic inside the logic-component.
UML: Class diagram when finished this PetStore model.
In the logic-component we need to model the business information we want to store persistently. The model will cover the structure of the information --the business classes and their attributes, the relationships between the classes, and the methods how they are processed. These business classes are so-called entities, e.g. a ZIP-coded address.
|
Tip |
|---|---|
|
See also the Reference Manual for Entity. |
|
Abstract
Now let us visualize our first class in a diagram. The class diagram is the right type for this. Creating a class diagram is done similarily to the implementation diagram.
->
A dialog opens with an empty list since no class diagrams has been created for our project yet.
Click on the button
1.Another dialog opens with a list of all project's containments. Type the name of the implementation diagram: Cl_address
Select our model's top-level node Data as parent element of the diagram.
Click on the button
You are back on the previous dialog and now you can see our recently created diagram.
Select the created diagram and click on the button
Now the main panel shows you an empty diagram. The tab on top informs you which diagram is shown.
In order to see the elements in the diagram click on the ZipAddress class element in the Containment Tree to drag and drop them to the empty diagram in the main panel.
Usually entity classes have not just simple instances (=objects), these objects often should have attributes containing specific information. You can create attributes in the specification dialog of the class or directly in the class diagram. The latter approach is described here:
|
Tip |
|---|---|
|
See also the Reference Manual for Attribute. |
|
Left-click on the ZipAddress class in the class diagram
Move your mouse cursor over the class, so an orange and a green filled circle appears on the right border of the class.
Click on the upper orange circle to insert a new attribute.
In the attribute section of the class a new line appears. Here type in the name of the attribute: zipCode
Repeat this to create an attribute named locationName
Double-click on the zipCode attribute, so the specification dialog for this attribute appears
Click into the field Type , which represents the datatype of the values storable in this attribute, and type Text.
As soon as you start typing the tool jumps to the matching datatypes.
Select the datatype Text from pyswarmProfile. You can see the owner profile in the brackets following the datatype name.
Repeat this with attribute locationName
Procedure 2.12. Create Business Attributes
|
Tip |
|---|---|
|
In later versions of pyswarm you will be able to specify other attribute datatypes than Text. |
|
|
Tip |
|---|---|
|
You can also apply Section 1.1, “ Visibility ” and Section 1.2, “ Multiplicity ” to attributes. |
|
|
Tip |
|---|---|
|
The generator creates setter and getter methods for the class to access the
attribute values of an object. Example to get the value of zipCode
the method is named
|
|
In general zone classes are entity classes. But they are primus inter pares , which means first among equals . One obvious difference to normal entity classes is that each logic-component holds exactly one single zone class, but as many entity classes you want. For the purpose of this tutorial it is enough to know that the zone class plays a key role in several architectural aspects of an pyswarm application like rights management. In our address management example a good zone class would be an address-book, since it is a common purpose of address-books to store addresses. Creation is almost the same as in entity classes.
|
Tip |
|---|---|
|
See also the Reference Manual for Zone. |
|
In Containment Tree right-click on adm component
->
Enter the class name: AddressBook
Apply the stereotype «pyswarm.zone»
Drag and drop the AddressBook class into the class diagram
Procedure 2.13. Create a Business Zone Class
While discussing the zone class concept it is worthwhile to mention another special object which should be available in any logic-component: the primary object . The primary object is unique, it has the entity ID 1 in the component, and it is an object of the zone sub-class of the logic-component --in our example it will be an AddressBook object. A primary object can be used by any client to enter the arrangement of persistent entity objects of the logic-component. Later it is described how you can easily set up the primary object for the adm component and how you can use it to populate adm with many more objects.
By using inheritance we can design so-called base classes that are common for different kinds of entities, e.g. we could use a zip-code address class which is the base class for geographical addresses (street, house-number) and of PO box addresses. Both, geo addresses and PO box addresses, would need a ZIP code and a location name. By making them sub-classes of a common base-class ZipAddress they will implicitely inherit the two attributes.
|
Tip |
|---|---|
|
See also the Reference Manual for Generalization. |
|
Create a new entity class in adm: GeoAddress
Apply stereotype: «pyswarm.entity»
Add attribute: street : Text
Add attribute: houseNumber : Text
Create a new entity class in adm: PoboxAddress
Apply stereotype: «pyswarm.entity»
Add attribute: poboxCode : Text
Ensure that both classes are child elements of adm. Drag and drop them into the class diagram.
In the tool-bar click on the Generalization icon.
Move your mouse cursor over the GeoAddress class until blue border appears, then left-click the class.
Move your mouse cursor over the ZipAddress class until blue border appears, then left-click the class.
You see now a generalization (=inheritance) relation between the GeoAddress and the ZipAddress class. The closed arrow points to the ZipAddress which means, that ZipAddress is the general class (base-class), and GeoAddress is the specific class (sub-class), speak: GeoAddress is a ZipAddress. By doing this GeoAddress automatically inherits from ZipAddress the two attributes, so we do not model them in GeoAddress. Inheritance helps to avoid redundant and fault-prone work. Vice versa, ZipAddress do not inherit from GeoAddress.
Now repeat these steps, so ZipAddress is also base-class of PoboxAddress.
Procedure 2.14. Create Class Inheritance
|
Caution |
|---|---|
|
By the way, you must not model cycles of inheritances, e.g. A is a B and B is a C and C is an A . Currently such model mistakes are not checked by the SDK. |
|
If we study our model we will easily see that ZipAddress doesn't fit that well. Different from a GeoAddress or PoboxAddress we can't imagine an address that is just a ZIP code and a location name -- although some countries probably have such addresses. Let us assume that we don't need and don't want that a ZipAddress can be instantiated as a concrete object, but we want only sub-classed objects, whether GeoAddress or PoboxAddress. This is possible. We need to specify the ZipAddress class as an abstract class. This would allow us to use the features derived from the ZipAddress class, but ZipAddress can not be directly instantiated.
Open the specification dialog of ZipAddress
In the right panel you should see Is Abstract , if not, switch the perspective of the properties from Standard to Expert or All. Check the Is Abstract box, so you see true as value.
Close the specification dialog
Depending on your tool settings the ZipAddress is now looking slightly different from the other entity classes. Usually the class name is in italic font, indicating that this is an abstract class. Other settings or tools may visualize instead by an {a} or an {abstract} . Also in the containment tree the look of ZipAddress changed.
Procedure 2.15. Specify Abstract Business Class
I suppose you are getting better in working with the model. Since object-orientation would be only half the fun with-out the inheritance concept, we will extend our model by some more classes and generalizations.
Address-books are fine to store addresses like the addresses we already learned. But there are other things we can consider to be addresses and to be stored in address-books, e.g. email-addresses, Web-addresses, telephone numbers, facsimile (fax) numbers, and so on. Many of them have no ZIP code and no location name as implicated by the ZipAddress. Thus we would need a more general class for all kind of addresses. Surely you already have a name for it: Address.
Create a new entity class in adm: Address
Apply stereotype: «pyswarm.entity»
Add attribute: subject : Text
Make Address an abstract class , since we do not want concrete instances of it.
In Containment Tree ensure that Address is child element of adm, then drag and drop Address to the class diagram.
Make ZipAddress a sub-class of Address, so ZipAddress will inherit the attributes of Address, as well as GeoAddress and PoboxAddress will indirectly inherit these attributes via their base-class ZipAddress.
Create a new entity class in adm: WebAddress
Apply stereotype: «pyswarm.entity»
Add attribute: url : Text
Make WebAddress a sub-class of Address
Create a new entity class in adm: Telephone
Apply stereotype: «pyswarm.entity»
Add attribute: prefix : Text (for the prefix to dial, e.g. the mobile network or the city prefix)
Add attribute: number : Text (for the number including extension; this is also a Text since we do not math processing on it and there also vanity numbers with letters, like 0800-GETMEOUTTAHERE)
Add attribute: isVoice : Bool (which is set to True if it is a voice number, else False)
Add attribute: isFax : Bool (which is set to True if it is a fax number, else False. You can easily combine it with isVoice, so a single Telephone object can be one of them or both, wich is closer to reality and more flexible than making two different classes.)
|
Note |
|---|---|
|
Do not use inheritance for any little gimmick! In some cases it makes more sense to specify the character of an object by attributes instead of different classes. Moreover morphing an object from one class to another class is more difficult than just changing attribute values. |
|
Make Telephone a sub-class of Address
Procedure 2.16. Create Multiple Inheritance
Enumeration classes are different from business classes. A logic-component can contain enumeration classes which of course are persistently stored, but they are rather simple, since they have no attributes and no operations. The purpose of an enumeration is providing a fix list of possible values (literals) an attribute of an business class might have. Enumeration literals are not objects like business objects are. They are only used for reference purpose. Imagine an identity management where you can assign in an attribute Person.gender an attribute type Gender which is an enumeration class. The Gender class may have the literals m (represenging male ), f ( female ) and u ( unknown ) --or even x?-- well, depending on your model and the requirements. The literals have no additional information. So the Person object linda may have the attribute gender=f, john may have gender=m, and smith may have gender=u.
|
Tip |
|---|---|
|
See also the Reference Manual for Enumeration. |
|
In an pyswarm application any enumeration is immutable , which means, that this list is generated and inserted into the database as defined in the model, and that during run-time this list neither can be extended or shortened, nor its literals can be modified.
In our PetStore we use countries --more precisely: ISO-codes of countries-- for our example of an enumeration class. In the reality this would be not a good practice, and we would better use an business class instead, since country-codes change, rarely, but it happens. However, I have no better idea for an enumeration example here.
In Containment Tree right-click on adm component element.
->
Provide a name: Country
Left-click the element to drag and drop into the class diagram. As you can see the enumeration class looks different from the entity classes, in particular it has no sections for attributes and operations.
Instead you see an orange filled circle with the bubble text Insert New Enumeration Literal . Click on it
Enter the first literal consisting of two-letter: au --which by the way is the ISO code for Australia
|
Note |
|---|---|
|
Alternatively you can use the specification dialog of Country. Browse to Literals and add literals by clicking on the button. |
|
Add the other literals: br, cn, de, es, fr, it, jp, ru, uk, us, za
In the class diagram you now can see all literals listed inside the Country class.
Procedure 2.17. Create Business Enumeration
Again let us have a look on our model. There is the AddressBook class and the different kind of addresses it will keep. Some of them could very well use the information to which country the address belongs. Obviously the WebAddress is independent of a country. But Telephone needs to be related to a country, e.g. let our users know which international access code to dial before the actual prefix and number. Of course GeoAddresses and PoboxAddresses, speak: all ZipAddresses, need a country information. Yes, they are country-dependent addresses. Let us make a new base-class named CountryAddresses.
|
Tip |
|---|---|
|
See also the Reference Manual for Enumeration Literal. |
|
Create a new entity class in adm: CountryAddress
Apply stereotype: «pyswarm.entity»
Add attribute: isoCode : Country (here we take as attribute type the enumeration class Country)
Ensure that the class is child of adm. Drag and drop it to the class diagram
Make CountryAddress an abstract class .
Make CountryAddress base-class of Telephone.
Make CountryAddress base-class of ZipAddress.
Procedure 2.18. Using Enumeration as Attributes
|
Note |
|---|---|
|
In this case it wasn't really necessary to create a new base-class CountryAddress. Instead we could have also add the isoCode attribute explicitely to the ZipAddress and the Telephone classes. But I wanted to show another UML feature also supported by pyswarm: multiple inheritance. Both, ZipAddress and Telephone, are inheriting from two totally independent base-classes. |
|
Before we add operations to our model it is the right time to get some background-information on operations. Operations are getting easily a rather complex issue, especially in the meaning of code-generation. Many code-generators do not support the generation of method implementation code, or the support is rather rudimentary. The reason relies in the very complex nature operations can have if they contain advanced business logic and that this complexity is hard to simplify in an abstraction level we hope is able to save us time.
Operations define what an object can do. In general there are operations which can
create other objects -- either of the same class or of a different class --,
modifying objects --usually by calling other operations, manipulating attribute values, or modifying the set of relations to other objects--,
destroying objects
or a mixture of these three kinds.
|
Tip |
|---|---|
|
See also the Reference Manual for Operation. |
|
In some cases an operation may (!) return something, the so-called return-type . It can be a boolean True or False, any other supported datatype value (a text, a date, a number, ...), a single entity object or even a collection of business objects. If the return-type of the operation is None, nothing will ever be returned by the operation.
Operations in the following mean business operations. There are also many other operations in your system, where some are automatically generated, but we don't care of them yet.
Many business operations are very simple, e.g. when only an attribute value has to be accessed with-out any verification to take place. But other business operations can be very powerful, processing loads of complex tasks and conditional validations, e.g. verifying if the user has sufficient privileges to execute the operation on this object, or if the business rules may be violated by a specific combination of attribute values --such as if date of death is before date of birth-- and many more. It heavily depends on the specific requirements to a system, how simple or complex business operations can be, and this may even vary from one operation to another. In general we should try to keep operations as simple as possible wherever requirements advice to do so, since complexity is worth to avoid, else they may bring us big headache much easier than a very long party night.
However, there are many cases where simple business operations meet the requirements and for these cases pyswarm offers you a comfortable way to handle them easy and fast. These operations are called auto logic, since almost everything is done automatically. They are stereotyped as «pyswarm.logic.auto». You just need to specify the absolutely necessary basics in your model.
The first question to consider is what the operation should do. As already mentioned before, we can let our operation creating , modifying or destroying an object. We have to decide between these three modes if working with auto-logic, although there are work-arounds even for a combined use-case, we leave it for other documents to explain.
The three different modes are described here before we add some of them to our PetStore model.
The destroy mode auto-logic is the easiest to model. In short: You just add an auto-logic operation to the class of which you want to delete the objects. So if you want a destroy operation to delete Foo objects, you add the operation to the Foo class. That's simple. Provide a name to the operation. Stereotype as «pyswarm.logic.auto». Tag mode as destroy. As return-type define the datatype None from the pyswarmProfile. Defining return-types is also explained later in this chapter.
Some more specification is needed for auto-logic operations with the tag mode=modify. Similar to the destroy operation place the modify operation in the class Foo if you want the operation to modify Foo objects. You need to specify the attributes that should be modified. In run-time the appropriate values have to be provided to the operation in order to let the operation know which new attribute values the object should have. This is done by extending the method signature with parameters. So if you want that the operation can modify the Text attributes title and sub-title of a Foo object, the operation would have two parameters with the same names and the same types as the corresponding attributes. The return-type is defined as None.
The create auto-logic operation works similarily to the modify operation. But there are two important differences: First, you can, but you don't have to, put the operation in the class of which you want to create objects. Let us say that you have the classes Foo and Moo and that you want an operation that creates Moo objects where each Moo object is owned by a Foo object. In this case you add the create operation, e.g. newMoo() to the Foo class. Otherwise if you want that a Moo object can create Moo objects put the operation to the Moo class. Depending on which object you call the create operation this object ( self ) will become the owner of the created object. Will the owner object be destroyed also its owned objects will be destroyed, which is called cascading delete and is related to the UML lifetime concept of composition relations between classes. The second difference between modifying and creating operations is that the creating operations need a return-type defined, where the return-type of course is the entity class of which the operation should create objects. By doing so the recently created object is returned to the calling method or client which then might do further processing with it.
Okay, enough theory, let us have some practice with our model. We start by specifying the operations that create new objects:
Add operation to AddressBook: newWebAddress()
Double-click the operation to open specification dialog
Apply stereotype: «pyswarm.logic.auto»
Tag mode=create
As return-type define WebAddress
Add a parameter by clicking on Parameters on the left tree and clicking on the button. WebAddress has two attributes: url, and the attribute subject inherited from Address. As name of the first parameter name enter: subject, and as type define Text.
Add the second parameter: url :Text
Add operation to AddressBook: newPoboxAddress()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=create
Set return-type: PoboxAddress
Add the parameter: subject :Text
Add the parameter: isoCode :Text (attribute inherited from CountryAddress)
Add the parameter: zipCode :Text
Add the parameter: locationName :Text
Add the parameter: poboxCode :Text
Add operation to AddressBook: newGeoAddress()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=create
Set return-type: GeoAddress
Add the parameter: subject :Text
Add the parameter: isoCode :Text
Add the parameter: zipCode :Text
Add the parameter: locationName :Text
Add the parameter: streetName :Text
Add the parameter: houseNumber :Text
Add operation to AddressBook: newTelephone()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=create
Set return-type: Telephone
Add the parameter: subject :Text
Add the parameter: isoCode :Text
Add the parameter: prefix :Text
Add the parameter: number :Text
Add the parameter: isVoice :Bool
Add the parameter: isFax :Bool
Procedure 2.19. Create Business Operation
Following we specify the operation which modify objects:
Add operation to Address: changeSubject()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=modify
As return-type define None
Add parameter: subject :Text
Add operation to WebAddress: change()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=modify
As return-type define None
Add parameter: url :Text
Add operation to PoboxAddress: change()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=modify
As return-type define None
Add parameter: isoCode :Text
Add parameter: zipCode :Text
Add parameter: locationName :Text
Add parameter: poboxCode :Text
Add operation to GeoAddress: change()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=modify
As return-type define None
Add parameter: isoCode :Text
Add parameter: zipCode :Text
Add parameter: locationName :Text
Add parameter: streetName :Text
Add parameter: houseNumber :Text
Add operation to Telephone: change()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=modify
As return-type define None
Add parameter: isoCode :Text
Add parameter: prefix :Text
Add parameter: number :Text
Add operation to Telephone: defineFeatures()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=modify
As return-type define None
Add parameter: isVoice :Bool
Add parameter: isFax :Bool
Procedure 2.20. Create Modification Operation
And finally the operations that will delete objects:
Add operation to AddressBook: delete()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=destroy
As return-type define None
Add operation to Address: delete()
Apply stereotype: «pyswarm.logic.auto»
Tag mode=destroy
As return-type define None
Procedure 2.21. Create Automatically Generated Destroy Operation
|
Tip |
|---|---|
|
You can also apply Section 1.1, “ Visibility ” to operations. |
|
|
Tip |
|---|---|
|
Also supported already are operations with stereotype «pyswarm.logic.taboo». The generator doesn't generate the method implementation. Instead it inserts regular Python source code you provided in a taboo source code directory. See also the Reference Manual for Operation (Taboo Logic). |
|
|
Tip |
|---|---|
|
Support of «pyswarm.logic.model» is planned. It is based on UML activities. |
|
You may already missed one of the most important UML features, which we want to describe now: associations. A world full of objects with attributes and operations is a good world. A world where the objects can be linked to each other surely is a better world --well, hermits are rare. The UML therefore supports so-called associations --or as I sometimes prefer to name it in order to emphasize the more general mean: relations .
An association allows links between objects of the associated classes. You can model an association which can link two objects of the same class, or associations for two objects of two different classes. The UML also knows another kind of association, the n-ary association, which can link more than two objects together, but this is often not recommended since it is considered to be not really object-oriented. This pyswarm version doesn't support n-ary associations and currently there are no plans to realize them.
|
Tip |
|---|---|
|
See also the Reference Manual for Association. |
|
Let us in our model quickly create a new class that helps to make the nature of associations clear. We can use this class to learn who is the addressee if we select an address.
Create a new entity class in adm: Recipient
Apply stereotype: «pyswarm.entity»
Add attribute: name : Text
Add operation to Recipient: change
Apply stereotype: «pyswarm.logic.auto»
Tag mode= modify
Add parameter: name :Text
Return-type: None
Add operation to Recipient: change
Apply stereotype: «pyswarm.logic.auto»
Tag mode= modify
Add parameter: name :Text
Return-type: None
Add operation to AddressBook: newRecipient
Apply stereotype: «pyswarm.logic.auto»
Tag mode= create
Add parameter: name :Text
Return-type: Recipient
Procedure 2.22. Create Associations between Classes
Let us assume that for each Address we store there might be stored none Recipient --typically for WebAddresses--, one Recipient or multiple Recipients. And for each Recipient we store there might be stored none Address, one Address, or multiple Addresses. Cardinalities of associations are working the same as in multiplicity of attributes. They define ranges or limits how many objects can be related to an object via the given association. A lower value is defined, which specifies what the lowest amount of related objects is required. And an upper value is defined, which specifies the highest amount of related objects accepted.
Alternatively we could say: 1 Address can have 0, 1, or indefinitely many Recipients related. And vice versa: 1 Recipient can have 0, 1, or indefinitely many Recipients related.
UML makes this shorter. The term indefinitely is represented by the asterisk sign * . And only the range is provided, which would result into something like:
1 Address associated with 0..* Recipient which hold(s) the Address
1 Recipient associated with 0..* Address which is/are holded by the Recipient
And both statements are summarized by the following statement, representing the nature of this association:
0..* Address associated for AddressHolding purpose with 0..* Recipient
Let us model this association. There are multiple ways, e.g. using the specification dialog, but the way I prefer is using the class diagram:
In the tool-bar click on Association .
Move the mouse cursor over the Address class until blue border appears, then left-click
Move the mouse cursor over the Recipient class until blue border appears, then left-click
1.You see a simple black line linking the two classes. This is our association. Double-click on it, so the specification dialog appears
Provide an expressive name to the association: AddressHolding
Below you find two groups of information named Association End A and Association End B. This are the association-roles, since you can consider an association always from both objects of the association. An Address has Recpient. A Recipient has Address. If you are in Address, the opposite type is the Recipient class, and vice versa.
In the association-end where the type is Recipient provide as name: RecipientsOfAddress
In the association-end where the type is Address provide as name: AddressesOfRecipient
In the association-role RecipientsOfAddress select as multiplicity: 0..*
In the association-role AddressesOfRecipient select as multiplicity: 0..*
In both association-roles for Visibility now select public.
Click on the button and you will see the association fully labelled in your class diagram.
In Containment Tree ensure that the association directly resides in adm/relations node. Since the association is between two classes of the same logic-component (here: adm) it is required that also the association element resides in the common parent node. Please note that associations for each node, as logic-components are, are collected in a relations node. So the association won't reside directly under adm , but in adm/relations .
Procedure 2.23. Create Association
Assume we have an Address object, whatever it is, let us say a telephone number, e.g. 0123-45 referenced as phone. Now it would be possible to associate this Address with a Recipient object, e.g. linda referenced as linda. And if we want we can even associate phone with another Recipient object paul. And we could associate the Recipient object linda with another Address object, e.g. the WebAddress object having the url http://127.0.0.0.
Whenever we have one of these objects, we can access the opposite association role to retrieve all objects associated by this association to the given object. If the opposite role defines in multiplicity an upper value higher than 1, it means that not a single object is returned, but a collection of objects. Collections give us some additional methods we can call in order to classifying them, filtering by criteria, sorting, browsing page by page, and so on. These features are described later on the small example client module we want to write.
Not every association-role provides you a collection. There are also association-roles with the cardinality 0..1 or 1..1, which means that for any given object there can be no object associated or only a single object. So you won't access a collection, but an entity object -- or nothing, speak: None.
Add a new association AddressBookHierarchy between AddressBook and AddressBook
On the one assoication-role provide the name ParentBook with the multiplicity 0..1
On the one assoication-role provide the name ChildBooks with the multiplicity 0..*
Declare both roles as public
In Containment Tree ensure that the association directly resides in adm/relations node.
If in any case exactly one associated object is required the multiplicity can be defined as 1, which is the short-cut of 1..1.
A good example for multiplicity 1 is a special type of associations: compositions. A composition defines an association where lifetime aspects are applied. As you may remember we already planned that each Address object is owned by an AddressBook object. Associating the two classes by a composition will ensure that whenever an AddressBook object is deleted all its owned Address objects will also be deleted. A mechanism which sometimes is called cascade delete.
In the tool-bar click on the Composition icon.
Move mouse cursor over AddressBook until blue border appears, then left-click on it.
Move mouse cursor over Address until blue border appears, then left-click on it
A new composition has been added, which looks the same as an association, but on the side of AddressBook it has a black diamond. The filled black diamond indicates that AddressBook is the owner class .
Open the specification dialog of the composition
Name: AddressAdmin
On the one association-role with AddressBook provide the name AddressBookOfAddress with the multiplicity 1, which means that each Address object has exactly one AddressBook.
On the other association-role provide the name AddressesOfAddressBook with the multiplicity 0..*
Declare both roles as public
If you switch the view in Properties to All then you can see on the association-end AddressesOfAddressBook a property isComposite , which is set to true. This specifies that the Address is composited, speak: owned by class of the opposite association end.
Click button
In Containment Tree ensure that the association directly resides in adm/relations node
Procedure 2.25. Create Association
As mentioned you can change an association to a composition, if needed, as far as you ensure that each class has exactly one owner class and no cycle will appear. The ownership concept is based on a hierarchical pattern. Probably you already noticed that AddressBook itself has no owner class yet. Since AddressBook is the zone class of our logic-component it is the natural candidate to be the top owner class in our model. This implies that the owner class of AddressBook is AddressBook itself.
Open the specification dialog of the AddressBookHierarchy by double-clicking the association.
Switch the view in Properties to All and look at the association-end ChildBooks.
Here select checkbox of the property isComposite to be true.
Close the specification dialog.
Procedure 2.26. Create Association
In the class-diagram you see now that the association AddressBookHierarchy has turned to a composition with a black filled diamond on the end ParentBook. This kind of composition means that an AddressBook object may have no or one owner object and that this owner object is an other AddressBook object. At least there is always one AddressBook object --as we learned in the chapter about zone classes-- the primary object and a primary object has no owner object. Depending on the requirements of your logic-component you can add more AddressBook objects in a fictive production environment, which can be ChildBooks of the primary object , or some of them can be also owned by none ParentBook, which puts them on the same hierarchy level as the primary object . You probably want to build hierarchical trees of such AddressBook objects, where the top-level object --the primary object-- is aliased system, the next children are corporation and partners, below corporation you have division AddressBook, than departments and so on.
Now let us add another composition, so also Recipient class will have an owner class:
Add a new composition between AddressBook and Recipient. The black filled diamond should be on the side of AddressBook!
Name: RecipientAdmin
Association-role of AddressBook, name as: AddressBookOfRecipient
Multiplicity of AddressBookOfRecipient is 1
Association-role of Recipient name as: RecipientsOfAddressBook
Multiplicity of RecipientsOfAddressBook is 0..*
Declare both roles as public.
Click button.
In Containment Tree ensure that the association directly resides in adm/relations node
Procedure 2.27. Create Association
|
Tip |
|---|---|
|
We could also define visibility of association-roles, like we can do in attributes. According to visibility related objects can be accessed inside the object only (private, sign: - ), also inside sub-classed objects (protected, sign: # ), from all objects inside the same package, speak: logic-component (package, sign: ~ ), or from any object (public, sign: + ). By default association-roles are declared as public. |
|
Even inside an pyswarm applicaton it makes much sense to access the powerful features coming with your Python distribution, residing in extra modules, as well as any other Python product provided by you or 3rd-parties. Accessing such external features would allow you to extend your pyswarm application with any other Python library or application available. As far as I know there is no technological restriction in pyswarm which prevents you from using the same powerful import features for your pyswarm application as you can do in any other program written in Python.
However, in order to access such external features, you have first to model them so in a second step you can import them into specific parts of your pyswarm application. In a third step you can provide a manual implementation which accesses external features, e.g. you have a method that imports some MailMan modules, retrieves subscribers of a specific mailing-list and then does some processing with that objects like comparing with business objects in an pyswarm repository.
If you use the PetStore example or the pyswarm XMI master-file some external packages and modules are already specified in the model. Otherwise you can specify or extend them as following:
|
Tip |
|---|---|
|
See also the Reference Manual for External and Dependency (Import). |
|
Specify an abstract top-level package under the Data tree of your UML model --in parallel to your PetStore package node-- and stereotype it as «pyswarm.external». As name use something convenient, e.g. site-packages. It is an abstract node to collect all external packages and modules you want to import. It doesn't point to a specific directory, but it is assumed that your packages or modules you want to import will reside in a place where its import path can be resolved by Python.
Add another package as child-node of the site-packages node. Stereotype as «pyswarm.external.package». Such packages can contain modules or even more packages to import. A good example is the shared library which contains some handy modules. In this case name the package node as shared which is the name of the library directory.
In the package-node shared add a package node Randomizer and stereotype as «pyswarm.external.module». This points to the Python module shared/Randomizer.py. A module may also reside directly in the site-packages node, e.g. the string module which is part of the Python distribution.
In the Randomizer module add now the class node Randomizer and stereotype as «pyswarm.external.class». Note that classes to import need to be child nodes of a module.
You are rather liberate in modeling external features. There are only some rules to notice. Each UML model has exactly one package stereotyped as «pyswarm.external» and usually named as site-packages. It does not necessarily represent a concrete directory on the disc. It is an abstract element representing all root paths which the Python interpreter will look at for an absolute import path. This directory is never part of the absolute import path.
You can add one or more packages to the external root package. You can also add external modules directly to the external root package. But you can not add external classes directly to the external root package since all external classes have to be owned by an external module. External modules can reside either directly in external root package or any other external package or sub-package below root.
As already mentioned earlier alternatively to the auto-logic approach pyswarm offers you the manual implementation of business methods.
Regarding generators many developers have concerns that whatever generator suit they use, most of them don't fit all their needs. Especially regarding the implementation of business methods some generators tend to restrict developers to the way how the generator creates business method implementations. Other generators --often referred as passive generators-- allow manual implementations to be inserted, but these implementations are then overwritten by the generator at the next step. In other cases the orginal code is modified on each generation with undesired side-effects. In these cases developers may think, that a generator should go out their way for specific methods. Thus pyswarm support an approach where simple or complex method implementation can be hand-made by the developer and the original code is never touched by the pyswarm SDK. In specified methods developer's code should be used and it should be untouchable for the SDK --it should be taboo !
In the UML model business methods can be stereotyped as «pyswarm.logic.taboo», which means that the generator is only providing the stub of the method, the implementation for this method have to be provided by the developer. Manually implented method code is stored in a custom source code directory of the application. This directory should reside in the same directory as your UML model's XMI file, e.g.:
projects<dir>
PetStore<dir>
PetStore.xmi
taboo<dir>
In the given example we named the directory taboo, but you can use any other name. In any case provide the name of the taboo source code directory in the system node of your UML model:
Let us start with a simple example of a taboo-logic method. We want the ZIP-based addresses to have a method which returns a string, concatenated of all attribute values of the individual address object, so we can use it to print the mail address into a letter document.
Add operation to ZipAddress: printLetter()
Double-click the operation to open specification dialog
Apply stereotype: «pyswarm.logic.taboo»
|
Note |
|---|---|
|
Different from auto-logic, you don't provide a mode. |
|
Add a parameter named withSubject with type defined as Bool (that of the pyswarmProfile)
As return-type define Text
Create a new Python source code file named printLetter.py in this path below your taboo directory: petstore/internet/contacts/adm/ZipAddress/
|
Important |
|---|---|
|
Note that all directory names that represent a package are beginning with lower-case letter. ZipAddress represents a class, so its name begins with an upper-case letter. |
|
In this file enter the following code (the downloadable PetStore tutorial example has this code and the other files already included):
Procedure 2.30. Specification of Taboo Business Operation
# petstore/internet/contacts/adm/ZipAddress/printLetter.py
# Here we go with our custom business logic
strText = ''
if withSubject:
# Return address information including
# attribute subject
strText += self.getSubject(oOp)
strText += '\n'
strText += self.getZipCode(oOp) + ' '
strText += self.getLocationName(oOp)
return strText
No big surprise: You can use comments in your Python code. A good start, hum? ;)
First we create an empty string object strText (actually it is an unicode object). Here we append all string values we want to be returned by this method.
As you can see the implementation code is written as vanilla Python source code, very straight-forward. It is important to keep in mind that the source must not begin with indentation! The necessary indentation level is added in the copy (!) of the code by the SDK when the generator is pasting developer's code into the respective method stub.
This is the only modification the SDK applies on the code copy: intending the entire copy of the code to the appropriate intendation needed in the target method stub. In any case: The original source-code is absolutely taboo for the SDK and will stay unmodified. The reason for the indentation level insertion is that developer can use any Python editor with-out the editor perpetually nagging on them that the code is beginning with indentation which is not Python conform.
|
Important |
|---|---|
|
Each following indentation with-in the taboo code, if needed, is 4 white-spaces. Tabs should be replaced by white-spaces! You probably need to re-configure your favorite Python editor. |
|
Then we make a condition check. Please note that the identifier withSubject results from our Bool parameter specified in our model. pyswarm SDK will generate the method signature which will include this identifier so in runtime the Python interpreter will already have this identifier in the name-space of this method.
Let us assume that our printLetter() method have been called with the withSubject parameter value set to True. In this case a method self.getSubject(oOp) is called, which is derived from the base-class Address. This method is generated by pyswarm SDK like all setter and getter methods for each attribute of a business class. By calling these methods an implementation (whether client-side, inside the logic component package or in the class) can read or write values of the corresponding attribute values of the object. This feature has been already mentioned earlier in the chapter on modeling attributes.
Please note also that the oOp parameter identifies the Operation object which is needed to process all read-only and read-write activities on business objects. Since it is required for any business method pyswarm SDK is automatically inserting the oOp as parameter of type Operation into all business method signatures if generating your pyswarm application. This is provided although the oOp parameter is not specified explicitely in the model and even it must not be specified there. Since operation control (begin, close, etc.) is initiatially driven by client implementations, inside taboo any method has to use the oOp parameter.
Getter methods automatically generated by pyswarm SDK for attributes owned by this class.
As defined in the model printLetter() returns a Text value.
|
Tip |
|---|---|
|
Future releases may support interaction between business objects of different (local or distributed) components. Therefore it could be possible that the taboo source-code of one class may play the role of a client-implementation accessing the object of the other component. In such cases the taboo source-code would cover also the client-specific features like starting or closing an Operation object on the other component - but never on its own component. |
|
|
Tip |
|---|---|
|
You can also specify additional taboo methods for the same purpose in sub-classes like GeoAddress or PoboxAddress and add the corresponding source files into the taboo directory. Their implementations may contain super-calls for the base-method printLetter() and then adds sub-class specific attribute values in order to return a more complete information on the address object. - The PetStore tutorial example contains such implementation examples. |
|
More information on implementation of taboo methods are provided in section General Implementation Concepts. Please note that all the concepts described in that section can be used in the taboo methods as well as in any client. Eventually you need to adopt the Operation identifier in your taboo source, which is oOp for taboo source but oOp in the client script example.
By using taboo a developer can write common Python code, even importing and using external Python modules independent of pyswarm: If the manually implemented code needs features of an external Python module, those would need to be imported. Python supports multiple patterns for import. pyswarm supports most of them, but not the __future__ pattern:
import X
from X import Y
or from X import *
Each pattern has its advantages and disadvantages, and different effects on the namespace and the way how and where you can access the imported assets.
|
Note |
|---|---|
|
Please see also the following resources on Python import handling.
|
|
pyswarm uses and supports imports for different purposes. There are imports which are automatically built into your pyswarm application by the SDK, although you haven't specified them explicitely in your model. These implicite import dependencies are required by your pyswarm application to work appropriately, e.g. in order to
import base-class(es) of a custom business class, whether they are custom business classes itself or runtime classes provided by pyswarm_serve (module level),
import datatypes used by business attributes in a custom business class (module level)
import business operations' return-types which are either pyswarm datatypes or custom business classes (operation level)
For this reason you don't have to explicitely specify that kinds of dependencies in your model. Doing could result in dublicate import dependencies, although the SDK performs some simple verifications on base of the import expressions.
However there are many use-cases where you will require imports that are not implied by the SDK. For example if needing modules representing a class which is specified neither as base-class, atttribute or operation's return-type in this class or operation. Another useful case would be to access a custom business class or an external feature provided by 3rd parties.
For achieving this in the model you explicitely specificy an import dependency where each dependency has one client element (the importing business class module or one of its operations) and one supplier element (the package, module, or class to be imported).
|
Important |
|---|---|
|
The pattern from X import * can never be used inside an operation. In this cases the SDK will redirect the client element to its owner element, which is a custom business class. However, usage of this pattern is sometimes not recommended due to higher risk of name conflicts. |
|
As mentioned, a client UML element can be:
a custom business class, as already learned they are stereotyped as «pyswarm.entity» or «pyswarm.zone»
a business operation you modeled in one of your custom business classes
As supplier element you can choose from a broader palette:
one of the custom business classes in the same logic-component as the client node of the import dependency
one of the custom enumerations in the same logic-component as the client node of the import dependency
one of the datatypes supported by pyswarm (they reside in the pyswarmProfile)
one of Python products you specified in your UML model and which will be available during runtime of your application
Paths from client modules to the targets to be imported are resolved according to the path modeled in the external package. The SDK generator will then create the appropriate absolute or relative imports in the module of the class in which the method code will reside.
Relative imports are only used at imports where the supplier is a custom business class or custom enumeration in the same logic-component (only local classes or enumerations are supported yet).
Absolute imports are generated for datatype suppliers (e.g. import serve.DtText) or for pyswarm-external products, whether they are packages, modules, or classes (e.g. import string, import math, from MyPackage import MySubpack or from MyPackage.MySubpack.MyModule import MyClass). As you see in the examples you can import features that are part of the Python distribution as well as any other feature that Python can access via sys.path.
Following some examples, how pyswarm SDK resolves import dependencies.
If your operation depends on a specific node Bar (whatever it is: package, module, or class) the import dependency will result in slightly different import statements generated.
Example 2.1. from X import *
Apply the from X import * pattern, this will result in a statement:
from Bar import *
if Bar is a root element, e.g. a package or module directly in Python's sys.path.
If Bar is not a root element it will have an absolute path and this path will be also generated, e.g.:
from NodeA.NodeB.NodeC.Bar import *
Example 2.2. from X import Y
Apply the from X import Y pattern, this will result in a statement:
from Bla import Bar
Thus there has to be an absolute path, so the complete statement would be including the path to Bar:
from NodeA.NodeB.NodeC import Bar
|
Important |
|---|---|
|
The from X import Y notation doesn't work if Bar is a root element. The reason is that it would result in a corrupt import statement: from import Bar |
|
Example 2.3. import X
Apply the import X pattern, this will result in a statement:
import Bar
if Bar is a root element, e.g. a package or module directly in Python's sys.path. If Bar is not a root element it will have an absolute path and this path will be also generated, e.g.
import NodeA.NodeB.NodeC.Bar
|
Note |
|---|---|
|
Relative imports to nodes in parent's node or above as described in PEP 328 [http://www.python.org/dev/peps/pep-0328/] are not supported. |
|
In order to specify that you need a module import in your taboo method do the following:
Change the main panel view to the appropriate class diagram.
Drag and drop the Randomizer module node into your class diagram. Move it to an empty space close to the ZipAddress.
In the tool bar click on Dependency .
Move mouse over the operation printLetter() in ZipAddress until a blue frame appears around the operation --not around the class, even this would also be supported by pyswarm SDK. Now left-click.
Move the mouse to the Randomizer package (alternatively to the Randomizer class) until a blue border frame appears around this class. A black line shows you how the UML tool understands the connection. Now left-click again.
Open specification dialog of the dependency and stereotype as «pyswarm.import».
Tag importMode = fromImportAll
Procedure 2.31. Create Explicite Import Dependency
The resulting import statement in the generated source code of ZipAddress.py will be:
from shared.Randomizer import *
If you alternatively specified to import the class instead the module:
from shared.Randomizer.Randomizer import *
In order to import the string module into the CountryAddress sub-classes for outputting the ISO code uppercase, it is sufficient to specify this import dependency in the CountryAddress. Importing modules works like in any vanilla Python module, so inheritance is also applied here, but may cause difficulties in complex use-cases with multiple inheritance.
| Pattern | importMode | Notice |
|---|---|---|
| from X import * | fromImportAll | On module level only - operation must not be client |
| from X import Y | fromImportSingle | Only for import of external packages or external modules that DO NOT reside directly in external root. |
| import X | importSingle |
Table 2.1. Python Import Patterns Supported by pyswarm
Change the main panel view to the appropriate class diagram.
Drag and drop the string module node into your class diagram. Move it to an empty space close to the CountryAddress.
In the tool bar click on Dependency .
Move mouse over the CountryAddress until a blue frame appears around the class. Now left-click.
Move the mouse to the string module until a blue border frame appears around this class. A black line shows you how the UML tool understands the connection. Now left-click again.
Open specification dialog of the dependency and stereotype as «pyswarm.import».
Tag importMode = importSingle
Procedure 2.32. Create Explicite Import Dependency
import string
The pyswarm SDK will try to ensure that all import statements will be unique per business class. It will verify all your explicitely defined import dependencies and also compares them with the implicitely necessary import dependencies (all on base of the generated string values). This is done on a class base --which in the case of pyswarm business classes corresponds to a per module base. pyswarm SDK analyses all datatypes used in the attributes, parameters or return-types in methods' signatures of the class and adds them to the import list of the module, so you don't need to specify them explicitely if you need one of them in your taboo method.
pyswarm SDK also analyses operations' return-types if they have as type any specific datatype or business class of your UML model assigned. Those pyswarm datatypes or custom business class modules are also already imported in the respective module. This works not only for Entity sub-classes (representing single business objects) but also for Collector sub-classes (representing bunches of business objects).