Wiki

Imprimir Propiedades
Methodology_Implement

In the implementation phase, pair programmers will write and test the code required to execute the task in which they are involved according to the domain model defined in the previous phase. Implementation has to be simple and understandable, i.e., anyone should be able to read the code and understand how it is modelled and what functionality it is intended to perform in order to facilitate its refactorization and extension in future iterations.
The steps to develop the domain model follow the methodology shown in [Ric06]. The interface is implemented using a Test Driven Development (TDD) approach, in which tests are implemented before writing code, facilitating their execution after developing code to ensure that requirements are fulfilled with respect to the task that is being developed.
The activities that are performed in the implementation phase are shown in the next figure.



The recommended environment to develop Romulus applications consists of Romulus framework, Eclipse or Netbeans IDE and Romulus plugins.

I.1 – Interface definition
In this activity, design information is adapted to the specific implementation technology, i.e., Romulus framework, and interfaces or empty classes are defined. The domain model and a task to implement serve as input for this activity.

The interface of each class is composed of attributes and method definitions. It can be defined in two steps:
  1. Classes are identified, which correspond to entities and value objects with their attributes and relationships that are defined in the domain model.
  2. Behaviour is added to the domain model by defining responsibilities and collaborations, according to the current task under development. Responsibilities determine what classes have to do and are implemented through one or more methods. Collaborations indicate what classes are required by a class to achieve its responsibilities.

I.2 – Test definition

Most time test cases will be generated automatically from class interfaces. Romulus test generator will use annotations to complete the definition of the tests. In some cases, tests cannot be fully automatically generated and developers will have to complete them with the desired functionality.

In this activity, a test suite that covers a reasonable number of cases should be defined. It should include, at least, automatically generated test cases and manually filled semi-automatically generated test cases.

I.3 – Interface implementation
In this activity, classes are filled with the appropriate code so that the test suite executes with no errors.
Romulus framework allows the development of enterprise Java applications. It is composed of a set of aspects that manages the required features to develop these kinds of applications, such as persistence or view. Each aspect is implemented through one or more modules, allowing to choose among different underlying technologies.

Using Romulus, the development effort is focused on the implementation of the domain. Therefore, after defining the task required to implement user stories and defining the simple domain model in the design phase, it is time to implement it through POJO's. POJO's are simple Java classes that are focused on the business problem. Using POJO's, development is easier, faster, and technologically independent.

As any Java class, POJO's consists of a set of attributes and methods. It is a design best practice that these attributes and methods represent information that is present in the domain model, and this approach is used in Romulus. However, certain information that is necessary for the application to work is not present in the domain. This information is called cross-cutting concerns.

In Romulus, cross-cutting concerns are modelled through aspects. Aspects represent independent views of the application that affect different logical or domain units. To achieve this, POJO's have a set of associated Java annotations which conforms the metadata of classes. This allows the definition of each aspect.

Modules implement one or various aspects of an application. This lets Romulus be a metaframework: applications are defined in a technologically independent way according to the different aspects. Different modules can be plugged in or out later, and Romulus architecture will allow that the implementation of the application remains untouched. Examples of available modules are Persistence-JPOX (which covers persistence aspect), View-Echo2 (view aspect) or Users module (authentication aspect).

To sum up, in a Romulus application domain concepts are defined through POJO's. Annotations are included in these POJO's to define aspect details. Finally, modules are selected to implement these aspects.

A brief example regarding this approach could be the web presentation of an object. Consider the domain concept Comment, which represents a comment made on an item in a collaborative work platform. A comment has a text and an e-mail which associates the comment with an author. Therefore, the Java implementation of this POJO should be:
public class Comment {
    private String text;
    private String email;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
   
    public String toString() {
        return text;
    }
}

To define the presentation of comments, annotations can be used to enhanced the model with a metamodel. By default, Romulus will show all attributes with public accessor methods, but this behaviour can be overriden. In Romulus, annotations can be defined purely in Java or through an XML file. It is a good practice to separate the domain definition from view specifications, so in this case view annotations could be defined in a separate XML file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<class xmlns="http://www.romaframework.org/xml/roma" xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
    xsd:schemaLocation="http://www.romaframework.org/xml/roma http://www.romaframework.org/schema/roma.xsd">
    <fields>
        <field name="entity">
            <class>
                <aspects>
                </aspects>
                <fields>
                    <field name="email">
                        <aspects>
                            <view visible="false"></view>
                        </aspects>
                    </field>
                </fields>
            </class>
        </field>
    </fields>
</class>
This XML file is equivalent to including the annotation in the Java file:
    @ViewField(visible=AnnotationConstants.FALSE)
    public String getEmail() {
        return email;
    }
In both cases, an annotation is being defined to indicate the view aspect to hide the email attribute when displaying a comment. This way, comments will only include the text attribute when shown.

Using aspects facilitates the development of applications, enabling the developer to focus on the domain definition. Next, we explain when and how to use each aspect to know which is the most suitable for each situation.

* Authentication aspect
Authentication is the process of verifying that someone is who claims to be. Therefore, this aspect is useful to control users access to some application functionalities that requires a security access.

Authentication aspect is based on the users module, which presents a login form in which users have to identify themselves. Also, this module implements a profiling system that allows to create users groups with different permissions over fields and actions of each class that is present in the application.

* Flow aspect
The flow aspect is used to define the execution flow of the application.
Implementation of this aspect is based on core module which presents a Java annotation, called FlowAction, that allows developers to define next classes to be executed and other class in case of error. For example:
class MainWindow {
    @FlowAction(next= NextWindow.class, error= ErrorWindow.class)
      public void next() {
      }
}

* Internationalization aspect
This aspect enables to present the application in different languages, according to the current location.

I18N Aspect is implemented as a part of the core module and is based on Java resource bundles. These are implemented as text files with a common base name and a specific suffix that identify their location. When a locale-specific resource is required, it is loaded from the resource that is associated to the location.

By default, a set of these files, called default_messages_xx.properties, are created at the beginning and stored under i18n package. These files define default messages that the application presents to the users, being possible to create new files to define messages in another languages. Also, this kind of files can be created to be associated to one class, in order to represent its fields and actions on several languages. Some IDE's provide facilities to create them.

* Logging aspect
This aspect facilitates the use of logs to control the execution of the application.

The logging aspect is implemented by the admin module, which provides a set of interfaces to export log files in order to analyse them easily.

* Monitoring aspect
With this aspect, business objects can be monitored externally, being accessible outside the application.

The annotations MonitoringClass, MonitoringField and MonitoringAction are used to define classes, fields and actions to be monitored. For example:
@MonitoringClass(enabled=true)
class Class{
    @MonitoringField(enabled=false)
      private String name;
    ...
    public String getDescription() {
            return description;
      }
}
Monitoring aspect can be implemented with JMX or MX4J technology.

* Persistence aspect
Persistence aspect is used to store Java objects and retrieve them from database.

It can be implemented with JPOX or Datanucleus technology. JPOX is an implementation of JDO and JPA specifications. DataNucleus extends JPOX, supporting from persistence of Java objects to LDAP, Excel and XML.

We use JDO Metadata to define the persistence of a class. We indicate  which classes are persistent in package.jdo file. By default, JDO 2.0 fetches fields of all primitive and simple types, so we have to indicate only fields of complex types such as relationships or collections.

For example, if we have the following classes:
package org.romulus.blog.domain;
public class Post {
    String title;
    String text;
    List<Comment> comments;
     ...
}
public class Comment {
    String title;
     ...
}

To make them persistent, we have to include both classes and their relationship in package.jdo file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jdo PUBLIC
    "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
    "jdo.dtd">
<jdo>
    <package name="org.romulus.blog.domain">
    <class name="Post" detachable="true">
        <field name="comments" default-fetch-group="true">
            <collection element-type="Comment" />
        </field>
    </class>
    <class name="Comment" detachable="true"/>
    </package>
</jdo>

Moreover, persistence aspect allows to access to database and create, update or delete objects stored in it. There are patterns to make queries by example or filter. Also, you can use JDOQL, a query language based on Java, to make your own queries.

ETL-XPath module allows to import data from existing sources automatically. Data is extracted from the original source, transformed and loaded into our application. 

* Reporting aspect
This aspect is useful to generate reports. View annotations are used to generate a template that can be customized in order to obtain the desired result.

This aspect is implemented by Reporting-JR module, which is based on JasperReports library.

* Service aspect
The purpose of the service aspect is to facilitate the management of POJO's as web services.

ServiceClass annotation is used to define a POJO as a web service. This annotation has two properties: one to define the interface and other to indicate the name of the service. For instance:
public class HelloWorld {
  public static void main(String[] args) {
   System.out.println("Hello World!");
  }
}

public interface HelloService {
    public String printHello();
}
@ServiceClass(interfaceClass = HelloService.class, serviceName = "Hello")
public class HelloServiceImpl implements HelloService {
    public String printHello () {
            return "Hello";
      }
}
The address to access the service will be http://applicationPath/services/Hello

* Scheduler aspect
This aspect enables applications to schedule events.

It is supported by Scheduler-Quartz module, which is based on Quartz scheduler, in which jobs and triggers can be defined to create events.

* Scripting aspect
This aspect adds server-side scripting capabilities to applications, evaluating scripts in a language, which can be indicated explicitly.

Currently, scripting aspect is implemented with Scripting-Java6 module, which is based on Java6 Rhino JavaScript engine, allowing applications to execute JavaScript code.

* Semantic aspect
The semantic aspect aims to add semantic to applications.

SemanticClass annotation is used to define the URI that is related to a class. It presents two properties: subjectPrefix, which contains the prefix that identifies instances of the class, and subjectId, which defines the name of the property that contains the value to identify instances of this class. Also, SemanticField annotation can be used to define the predicate related to a field.

The semantic aspect also contains methods to manage semantic information, such as transforming POJO's into semantic representations or creating SemanticModel instances.

This aspect is implemented by the Semantic-Jena module, which is based on Jena Semantic Web Framework.

* Session aspect
Session aspect's aim is to manage users sessions and information related to them. Information under the form of POJO's is stored as properties that will be available until the end of the user session.

Session Aspect can be implemented with View-Echo2 module, which is based on Echo2 session, or View-HTML module, which is based on Http session.

Active users can exchange messages thanks to the Messaging module.

* View aspect
View aspect manages the view part of an application. Its aim is to automatically  generate the code that is required to represent the domain defined through POJO's. By default, each field is represented according to its type, for example, a string is represented as a text field, and each method is represented as a link. Components are rendered over a layout, which by default presents methods and fields.

Representation can be customized with view annotations. There are three types of these annotations, each of those has a set of properties such as label, description, entity or render, among others.

ViewClass defines how each instance of a class has to be rendered.

ViewField is used to define the representation of the fields, it is used over them or their accessor methods. ViewField has a set of properties that can be used to validate input data, like required, min, max and match.

ViewAction is suitable for methods that have no arguments and return void type.

The view aspect can be implemented with Echo2 or HTML technologies.

View-Echo2 module is based on Echo2 Web Framework to generate AJAX web pages automatically that can be integrated with JSP.
View-HTML module is based on XHTML and CSS2 to facilitate the customization of web pages automatically generated from POJO's.

Moreover, Chart-JFreeChart module can be used to improve the view, rendering  POJO's as charts. This module is based on JFreeChart Open Source project.

* Workflow aspect
This aspect provides an interface to interact with a workflow engine. There are three modules that implement this aspect. Workflow-POJO provides basic workflow functionalities, while Workflow Tevere Engine and Workflow Tevere GUI are based on Tevere Flow engine, with the latter presenting a GUI that facilitates the workflow definitions.

In general, aspects are related to non-functional requirements that were elicited in the planning phase. The relation between the different elicited requirements and their possible implementation aspects is shown in figure 13.

Further code-level information about how to code using Romulus Framework can be found in the Roma Handbook [Aqu08].

I.4 – Refactorization

In this activity, the code is refactored to try to achieve maintainability. Unnecessary complexity should be removed immediately.

Common refactorization tasks are automated in the development environment, and are described next:

- Rename and move: Rename classes in order to give them a relevant name from a high level point of view. Move classes to group them in modules with same functionalities.
class UserData { ... }



class Profile { ... }

- Change method signature: Method signatures are subject to be changed whenever:
  - A method has too many parameters. The method might be made more specific or certain parameters could be grouped in a parameter object.
  - A method modifies parameters. Think if the modified parameter should be returned instead.
  - A method accepts many parameters with null value. Writing several signatures for the same method can be a way of polishing the use of the method.
  - A method can be accessed from too many contexts. Think if the method should be declared private or protected.
public Book selectBook(Author author, Title title);



private Book selectBook(Query query);

- Extract method: Extract blocks of code into new methods whenever they are repeated code patterns along a class.
public String[] getGreets(String name1, String name2) {
  String[] greets = new String[2];
  greets[0] = "Hello " + name1;
  greets[1] = "Hello " + name2;
  return greets;
}



public String[] getGreets(String name1, String name2) {
  String[] greets = new String[2];
  greets[0] = greet(name1);
  greets[1] = greet(name2);
  return greets;
}
private String greet(String name) {
  return "Hello " + name;
}

- Extract local variable: Define local variables at the beginning of methods to avoid including complex expressions into the content of the method, especially if the expression is repeated throughout the method body.
void locateUser(User user) {
  ...
  System.out.println("Distance to origin is " + Math.sqrt(user.getX()*user.getX() + user.getY()*user.getY()));
}



void locateUser(User user) {
 double distance = Math.sqrt(user.getX()*user.getX() + user.getY()*user.getY());
  ...
  System.out.println("Distance to origin is " + distance);
}

- Extract constant: Define constants to avoid including numbers or strings into the content of methods. This allows changing the behaviour of the methods easily.
void showHtmlImage(Image i, Double factor) {
  if (factor == null) factor = new Double(1.0);
  ...
}



Double DEFAULT_IMAGE_FACTOR = new Double(1.0);
void showHtmlImage(Image i, Double factor) {
  if (factor == null) factor = DEFAULT_IMAGE_FACTOR;
  ...
}

- Inline: Inline a method body whenever you find that a method is too simple and is not used very often.
void increaseCredit(int amount) {
  credit = addMoney(credit, amount);
}
void reduceCredit(int amount) {
  credit = addMoney(credit, -amount);
}
int addMoney(int a, int b) {
  return a+b;
}



void increaseCredit(int amount) {
  credit += amount;
}
void reduceCredit(int amount) {
  credit -=  amount;
}

- Convert anonymous class to nested: Classes should not be anonymous if they are used repeatedly. They should better be converted at least into nested classes.
public class HomePage {
  public void showBooks() {
    Book book = new Book() { public String getTitle() {return "<h1>" + title + "</h1>";} };
    ...
    return;
  }
  ...
}



public class HomePage {
  class HtmlBook extends Book {
    public String getTitle() {
      return "<h1>" + title + "</h1>";
    }
  }
  public void showBooks() {
    Book book = new HtmlBook();
    ...
    return;
  }
  ...
}

- Convert member type to top level: If member types need to be used outside an interface, they should be converted into top level classes.
public interface Constants {
  class BookInstance { ... }
  ...
}



public class BookInstance { ... }
public interface Constants { ... }

- Convert local variable to field: A local variable should be converted to field if it provides an instance-level stateful value that can be used later in another method.
public class Post {
  public void getContent() {
    ...
    unread = false;
    ...
    return content;
  }
  ...
}



public class Post {
  private boolean unread = true;
  public void getContent() {
    ...
    unread = false;
    ...
    return content;
  }
  ...
}

- Extract superclass: If two or more classes implement similar methods, they might have a relation of inheritance with a superclass. To avoid code repetition, a superclass might be extracted so that methods are not repeated in the subclasses.
public class Article {
  public String getText() {
    return text;
  }
  public ArrayList<String> getSections()  { ... }
  ...
}

public class Book {
  public String getText() {
    return text;
  }
  public ArrayList<String> getChapters() { ... }
  ...
}



public class Document {
  public String getText() { ... }
  ...
}

public class Article extends Document {
  public ArrayList<String> getSections() { ... }
  ...
}

public class Book extends Document {
  public ArrayList<String> getChapters() { ... }
  ...
}

- Extract interface: Whenever two classes share the same method signatures on some methods, an interface might be declared to use classes in a uniform way.
public class Post {
  public void addComment(String comment) { ... }
  ...
}
public class WikiPage {
  public void addComment(String comment) { ... }
  ...
}



public interface Commentable {
  public void addComment(String comment);
}
public class Post implements Commentable {
  public void addComment(String comment) { ... }
  ...
}
public class WikiPage implements Commentable {
  public void addComment(String comment) { ... }
  ...
}

- Use supertype where possible: Supertypes might be used after a class inheritance structure has changed to take advantage of polymorphism to obtain simpler code.
public class Article {
  public String getText() {
    return text;
  }
  public ArrayList<String> getSections()  { ... }
  ...
}

public class Book {
  public String getText() {
    return text;
  }
  public ArrayList<String> getChapters() { ... }
  ...
}

public class Reader {
  private Book favouriteBook;
  ...
}



public class Document {
  public String getText() { ... }
  ...
}

public class Article extends Document {
  public ArrayList<String> getSections() { ... }
  ...
}

public class Book extends Document {
  public ArrayList<String> getChapters() { ... }
  ...
}

public class Reader {
  private Document favouriteDocument;
  ...
}

- Push down: Moves methods to subclasses. It should be used whenever certain subclasses should not support these methods and therefore those methods are not generalizable to all the subclasses.
public class Article {
  public String getText() {
    return text;
  }
  public ArrayList<String> getSections()  { ... }
  ...
}

public class Book {
  public String getText() {
    return text;
  }
  public ArrayList<String> getChapters() { ... }
  ...
}

public class Reader {
  private Book favouriteBook;
  ...
}



public class Document {
  public String getText() { ... }
  ...
}

public class Article extends Document {
  public ArrayList<String> getSections() { ... }
  ...
}

public class Book extends Document {
  public ArrayList<String> getChapters() { ... }
  ...
}

public class Reader {
  private Document favouriteDocument;
  ...
}

- Push up: Moves methods to superclass. It should be used whenever the a set of methods that are the same are declared in all the subclasses.
public class Document {
  public String getText() {
    ...
  }
}

public class Article extends Document {
  public void addAuthor(Author a) { ...}
  ...
}

public class Book extends Document {
  public void addAuthor(Author a) { ...}
  ...
}



public class Document {
  public String getText() { ... }
  public void addAuthor(Author a) { ...}
  ...
}

public class Article extends Document {
  ...
}

public class Book extends Document {
  ...
}

- Introduce indirection: Indirections should be used by methods in the case that they use a similar method but should have a different signature.
public class Book {
  public Book(String title, String author) {
    this.title = title;
    this.author = author;
  }
  public Book(String title) {
    this.title = title;
    this.author = “Anonymous”;
  }
  ...
}



public class Book {
  public Book(String title, String author) {
    this.title = title;
    this.author = author;
  }
  public Book(String title) {
    super(title, “Anonymous”);
  }
  ...
}

- Introduce factory: Use a factory whenever the class of the returned object is not necessarily known or if the object is not necessarily new (as happens with singleton classes).
Document doc;
if (abstract == null) {
  doc = new Book(title); DocumentFactory.createDocument(...);
} else {
  doc = new Document(title, abstract);
}



Document doc = DocumentFactory.createDocument(title, abstract);

public class DocumentFactory {
  public static createDocument(String title, String abstract) {
    if (abstract == null) {
      return new Book(title);
    } else {
      return new Document(title, abstract);
    }
  }
}

- Introduce parameter object: A parameter object should be declared if a method has too many parameters. This way, the parameter object would group some of the method parameters.
public Book selectBook(Author author, Title title);



private Book selectBook(Query query);

- Encapsulate field: Fields should be encapsulated with setters and getters and not be directly accessible.
public class Book {
  public String title;
}



public class Book {
  private String title;
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
}

- Generalize declared type: When declaring variables, the use of more general types should be reconsidered after a refactorization of the class hierarchy.
public class Article {
  public String getText() {
    return text;
  }
  public ArrayList<String> getSections()  { ... }
  ...
}

public class Book {
  public String getText() {
    return text;
  }
  public ArrayList<String> getChapters() { ... }
  ...
}

public class Reader {
  private Book favouriteBook;
  ...
}



public class Document {
  public String getText() { ... }
  ...
}

public class Article extends Document {
  public ArrayList<String> getSections() { ... }
  ...
}

public class Book extends Document {
  public ArrayList<String> getChapters() { ... }
  ...
}

public class Reader {
  private Document favouriteDocument;
  ...
}

- Infer generic type arguments: If there are generic types in the code, they should be made specific by including arguments for the classes they manage.

public class Book {
  private ArrayList chapters;
  public void addChapter(String chapter) {
    chapters.add(chapter);
  }
  ...
}

public class Book {
  private ArrayList<String> chapters;
  public void addChapter(String chapter) {
    chapters.add(chapter);
  }
  ...
}
 

1489 Accesos, 0 Ficheros adjuntos 0 Ficheros adjuntos

  • Comentarios