Recent Changes - Search:

Software Engineering

This website demonstrates using wikis as teaching and learning tool.

The course instructor is also happy to share the teaching materials here with those who find it readable.

Mapping Models to Code

A Software Engineering Lecture by Steven Choy

Lecture Overview:

In the previous lectures, we learned to refine a system design model into an object design model. In this lecture, we are ready to turn the object design model into executable code.

We have been working on models. We transform a model by improving certain aspects within it. Following the direction of normal transformation--from model to code--we are said to be forward engineering a system. Reversing the direction of normal transformation--for example, recovering a model from executable code--we are said to be reverse engineering a system. If we transform existing code into better quality code, we are said to be refactoring the code.

In this lecture, you will also learn how to write code to implement associations in the object models, how to code the contracts with Java exceptions, and how to implement object models in a relational database.

Reading: Chapter 10 of the textbook Mapping Models to Code


Where are we now?

Mapping Concepts

Model Transformation

Refactoring

  • Operate on source code, ie. Improving a single aspect of the system without changing its functionality
Example

Before Refactoring

After Refactoring

 public class Player {
   private String email;
   //...
 }

 public class LeagueOwner {
   private String eMail;
   //...
 }

 public class Advertiser {
   private String email_address;
   //...
 }
 public class User {
   private String email;
 }

 public class Player extends User {
   //...
 }

 public class LeagueOwner extends User {
   //...
 }

 public class Advertiser extends User {
   //...
 }

Forward Engineering

  • Object design model before transformation
  • Source code after transformation
 public class User {
   private String email;
   public String getEmail() {
     return email;
   }
   public void setEmail(String value){
     email = value;
   }
   public void notify(String msg) {
     // ....
   }
   /* Other methods omitted */
 }
 public class LeagueOwner extends User {
   private int maxNumLeagues;
   public int getMaxNumLeagues() {
     return maxNumLeagues;
   }
   public void setMaxNumLeagues(int value) {
     maxNumLeagues = value;
   }
   /* Other methods omitted */
 }

Reverse Engineering

  • Applied to a set of source code elements and results in a set of model elements
  • Recreate the model for an existing system

Mapping models to code: Overview on Mapping Associations

Unidirectional One-to-One Assoication

public class Student {

	private Account account;
	public Student() {
		account = new Account();
	}
	public Account getAccount() {
		return account;
	}

}

public class Account {

	public Account() {
	}

}

Bidirectional One-to-One Association

public class Student {

	private Account account;
	public Student() {
		account = new Account(this);
	}
	public Account getAccount() {
		return account;
	}

}

public class Account {

	private Student owner;
	public Account(Student student) {
		owner = student;
	}
	public Student getOwner() {
		return student;
	}

}

One-to-Many Association

public class Student {

	private Set accounts;
	public Student() {
		accounts = new HashSet();
	}

	public void addAccount(Account account) {
		accounts.add(account);
	}

	public Account removeAccount(Account account) {
		accounts.remove(account);
	}

}

public class Account {

	public Account() {
	}

}

Mapping Activities

  • Optimizing the Object Design Model
  • Mapping Associations
  • Mapping Contracts to Exceptions
  • Mapping Object Models to Database Tables

Optimizing the Object Design Model:

Collapsing an object without interesting behavior

Optimizing the Object Design Model:

Delaying expensive computations

Realization of a unidirectional, one-to-one association

  • Object design model before transformation
  • Source code after transformation

public class Advertiser {

	private Account account;
	public Advertiser() {
		account = new Account();
	}
	public Account getAccount() {
		return account;
	}

}

Bidirectional one-to-one association

  • Object design model before transformation
  • Source code after transformation
 The account field is initialized
 in the constructor and never modified.
 The owner field is initialized during
 the constructor and never modified.
 public class Advertiser {

   private Account account;

   public Advertiser() {
     account = new Account(this);
   }

   public Account getAccount() {
     return account;
   }
 }
 public class Account {

   private Advertiser owner;

   public Account(owner:Advertiser) {
     this.owner = owner;
   }

   public Advertiser getOwner() {
     return owner;
   }
 }

Bidirectional, one-to-many association

  • Object design model before transformation
 public class Advertiser {
private Set accounts;
   public Advertiser() {
     accounts = new HashSet();
   }

   public void addAccount(Account a) {
     accounts.add(a);
     a.setOwner(this);
   }

   public void removeAccount(Account a) {
     accounts.remove(a);
     a.setOwner(null);
   }
 }
 public class Account {

   private Advertiser owner;

   public void setOwner(Advertiser newOwner) {
     if (owner != newOwner) {
       Advertiser old = owner;
       owner = newOwner;
       if (newOwner != null)
         newOwner.addAccount(this);
       if (oldOwner != null)
         old.removeAccount(this);
     }
   }
 }

Exceptions as building blocks for contract violations

  • Many object-oriented languages, including Java, do not include built-in support for contracts.
  • However, we can use their exception mechanisms as building blocks for signaling and handling contract violations
  • In Java we use the try-throw-catch mechanism
  • Example:
    • Let us assume the acceptPlayer() operation of TournamentControl is invoked with a player who is already part of the Tournament.
    • In this case acceptPlayer() should throw an exception of type KnownPlayer.
    • See source code on next slide

The try-throw-catch mechanism in Java

public class TournamentControl {

	private Tournament tournament;
	public void addPlayer(Player p) throws KnownPlayerException {
		if (tournament.isPlayerAccepted(p)) {
			throw new KnownPlayerException(p);
		}
		//... Normal addPlayer behavior
	}

}

public class TournamentForm {

	private TournamentControl control;
	private ArrayList players;
	public void processPlayerApplications() {
	// Go through all the players
		for (Iteration i = players.iterator(); i.hasNext();) {
			try { // Delegate to the control object.
				control.acceptPlayer((Player)i.next());
			} catch (KnownPlayerException e) {
			// If an exception was caught, log it to the console
				ErrorConsole.log(e.getMessage());
			}
		}
	}

}

Implementing a contract

  • For each operation in the contract, do the following
    • Check precondition: Check the precondition before the beginning of the method with a test that raises an exception if the precondition is false.
    • Check postcondition: Check the postcondition at the end of the method and raise an exception if the contract is violoated. If more than one postcondition is not satisfied, raise an exception only for the first violation.
    • Check invariant: Check invariants at the same time as postconditions.
    • Deal with inheritance: Encapsulate the checking code for preconditions and postconditions into separate methods that can be called from subclasses.

Mapping an object model to a relational database

  • UML object models can be mapped to relational databases:
    • Some degradation occurs because all UML constructs must be mapped to a single relational database construct - the table.
  • UML mappings
    • Each class is mapped to a table
    • Each class attribute is mapped onto a column in the table
    • An instance of a class represents a row in the table
    • A many-to-many association is mapped into its own table
    • A one-to-many association is implemented as buried foreign key
  • Methods are not mapped

Mapping the User class to a database table

Primary and Foreign Keys

  • Any set of attributes that could be used to uniquely identify any data record in a relational table is called a candidate key.
  • The actual candidate key that is used in the application to identify the records is called the primary key.
    • The primary key of a table is a set of attributes whose values uniquely identify the data records in the table.
  • A foreign key is an attribute (or a set of attributes) that references the primary key of another table.

Example for Primary and Foreign Keys

  • User table
  • League table

Buried Association

  • For one-to-many associations we add the foreign key to the table representing the class on the "many" end.
  • Associations with multiplicity "one" can be implemented using a foreign key. Because the association vanishes in the table, we call this a buried association.
  • For all other associations we can select either class at the end of the association.

Realizing Inheritance

  • Relational databases do not support inheritance
  • Two possibilities to map UML inheritance relationships to a database schema
    • With a separate table (vertical mapping)
      • The attributes of the superclass and the subclasses are mapped to different tables
    • By duplicating columns (horizontal mapping)
      • There is no table for the superclass
      • Each subclass is mapped to a table containing the attributes of the subclass and the attributes of the superclass

Realizing inheritance with a separate table

Realizing inheritance by duplicating columns

Comparison: Separate Tables vs Duplicated Columns

  • The trade-off is between modifiability and response time
    • How likely is a change of the superclass?
    • What are the performance requirements for queries?
  • Separate table mapping
    • We can add attributes to the superclass easily by adding a column to the superclass table
    • Searching for the attributes of an object requires a join operation.
  • Duplicated columns
    • Modifying the database schema is more complex and error-prone
    • Individual objects are not fragmented across a number of tables, resulting in faster queries

Thanks for Reading

If you would rather like to have this lecture note in printed format, please click the print action link in the top right corner.

If you find any problem in this lecture note, please feel free to tell Steven by steven@findaway.hk

Edit - History - Print - Recent Changes - Search
Page last modified on September 09, 2009, at 11:02 PM