Home - this site is powered by TWiki(R)
PROBRAL/InternalArea > CaseStudies > HealthWatcherEScala
TWiki webs: Main | TWiki | Sandbox   Log In or Register

Changes | Index | Search | Go

Design Rules in EScala

Introduction

EScala is a language integrating declarative events in Scala programming language. It makes it possible to declaratively define and combine events, quantifying over relationships between objects.

EScala aims to enable modelling of event-driven program in a modular way. Here are some thoughts about encoding design rules using the (E)Scala mechanisms.

Possibilities and Limitations

EScala conserves the ideas of object-oriented languages, and can only describe the interface of an object. It cannot enforce the behavior of methods or the absence of some element.

However, some enhancements are planned which may also help to write more complex Design Rules in EScala. See below for more details and ideas.

Some Design Rules

Some of the structural design rules may be encoded in EScala. This section summarizes a first attempt to encode Health Watcher DRs in the language. The used constructs are explained in the listings.

  • Using Scala's abstract type members, we can enforce that a DR instance defines a type implementing a specific interface
  • With structural types, no inheritance hierarchy is required for specifying the interface

abstract class TransactionManagementDR {
  // the type member ITransactionMechanism represents an abstract type which
  // has three methods, begin, commit, rollback
  type ITransactionMechanism <: {
    def begin()
    def commit()
    def rollback()
  }

  // the facade must expose three events corresponding to transactional methods
  // theses events are abstract
  type HWFacade <: {
    evt transactionStarted[Unit]
    evt transactionFinished[Unit]
    evt transactionAborted[Unit]
  }

  // this type is a subtype of the ITransactionManagement trait
  // in this case, a trait is used to make it possible to have an initialization block
  // the initialization block registers the transaction management methods with 
  // the event of the associated facade
  type ITransactionManagement <: TransactionManagement
  trait TransactionManagement {
    // a ITransactionManagement instance must have a reference to a facade
    val facade: HWFacade
    val transactionMechanism: ITransactionMechanism

    // initialization block (constructor code)
    // this is a simple case where the pieces of advice just call the corresponding
    // transaction methods. This is a way to fake the design rule enforcing the advice to call
    // the transatcion management methods
    facade.transactionStarted += transactionMechanism.begin _
    facade.transactionFinished += transactionMechanism.commit _
    facade.transactionAborted += transactionMechanism.rollback _
  }
}

An instance of this design rule may be a singleton object or a normal class. It must specify all things that are abstract. In this case, the three abstract types

object TMDR extends TransactionManagementDR {
  type ITransactionMechanism = TransactionMechanism
  type HWFacade = SystemFacade
  type ITransactionManagement = MyTransactionManagement
}

The three concrete implementation of the type may be dependent types, defined in the DR instance or not.

// no inheritance hierarchy is imposed here, due to the use of duck typing in the abstract type declaration 
class TransactionMechanism  {
      def begin() { ... }
      def commit() { ... }
      def rollback() { ... }
} 

class SystemFacade {
  evt transactionStarted[Unit] = beforeExec(this.registerComplaint) ||  beforeExec(this.registerEmployee) || ... 
  evt transactionFinished[Unit] = afterExec(this.registerComplaint) ||  afterexec(this.registerEmployee) || ... 
  // this feature is still to be implemented in EScala
  evt transactionAborted[Unit] = afterThrowing(this.registerComplaint) ||  afterThrowing(this.registerEmployee) || ...

  def authenticate(String login, String password): Employee = { ... }
  def registerComplaint(Complaint complaint) { ... }
  def registerEmployee(Employee employee) { ... }      
   ...
 }

class MytransactionManagement(val facade: TMDR.HWFacade, val transactionMechanism: TMDR.ITransactionMechanism) extends TMDR.TransactionManagement

Modularization Mechanisms

Using mixin composition, the implementation of a DR may be splitted in different traits that are then mixed into the instance.

trait TransactionMechanisms {
  // self type is a kind of `requires' this trait can only be mixed with TransactionManagementDR
  this: TransactionManagementDR =>

  type ITransactionMechanism = TransactionMechanism
  class TransactionMechanism { ... }
}

trait BusinessFacades {
  this: TransactionManagementDR =>

  type HWFacade = SystemFacade
  class SystemFacade { ... }
}

object TMDR extends TransactionManagementDR
                    with TransactionMecahnisms
                    with BusinessFacades

Possible Extensions to EScala

EScala aims to stay a general purpose language, however some new features may help to define more interesting DRs without being to specific.

  • Adding support for afterThrowing implicit events.
  • An action (a method in an OO design) consists in a list of steps which are mor or less explicit. These steps could be part of the method signature, exposing more events on the internal structure without breaking modularity. The details of implementation are not shown, only the specified steps of the method.
  • Having virtual classes in Scala could also help to refine DRs. For the moment Scala supports a Virtual Class Pattern which achieves the same goal but is quite verbose. An attempt was made to implement virtual classes in Scala, but not finished yet.

Quantifying over classes

An important problem to solve is the quantification over many different instances of a class, or over objects of different classes. There exists no declarative way to do it in EScala for the moment.

Encoding quantification over different instances/classes with the current version

In the example above, if the transactionManagement class observes many different Facade, we could encode it as follows

import scala.events.VarList

...
  trait TransactionManagement {
    // the VarList class allows to observe a mutable list
    // of variables.
    val facades = new VarList[HWFacade]
    val transactionMechanisms: ITransactionMechanism

    // using the any operator, we can observe any occurrence of the 
    // referenced event occuring in the list
    // e.g. the first event is
    // if any facade in the facade list triggers its transactionStarted event
    // the reaction is called
    facades.any(_.transactionStarted) += transactionMechanisms.begin _
    facades.any(_.transactionFinished) += transactionMechanisms.commit _
    facades.any(_.transactionAborted) += transactionMechanisms.rollback _

  }
But we must imperatively registers any new HWFacade in the facades list. This is good if we do not want to observe all existing instances but dynamically add or remove them from the list. An example of the syntax is showed below

  trait TransactionManagement {
    val transactionMechanisms: ITransactionMechanism

    // by referencing the event on the trait name, we observe all the existing instances
    HWFacade.transactionStarted += transactionMechanisms.begin _
    HWFacade.transactionFinished += transactionMechanisms.commit _
    HWFacade.transactionAborted += transactionMechanisms.rollback _

  }

Quantifying over classes

Another solution would be to allow quantification over classes and not over object relationships. The HWFacade type must be observable. One need a way to indicate that the IHWFacade is globally observable, that is to say one may observe all the existing instances of this type.

  type HWFacade <: IHWFacade
  observable trait IHWFacade {
    evt transactionStarted[Unit]
    evt transactionFinished[Unit]
    evt transactionAborted[Unit]
  }

(the use of the observable keyword here is not definitive, it is a first idea). By using this, we do not loose the OO modularity and can observe all the instances, the class/trait is instrumented when it is compiled. This mechanism could also mean that one can observe method executions of all instances of a class. This observable modifier for a class/trait would mean, that all events (explicit and implicit) may be observed on all instances. So this code would be a regular code

observable class C {
  def m(...) = ...
}

evt e = afterExec(C.m)

-- LucasSatabin - 14 Oct 2010

Edit | Attach | Print version | History: r5 < r4 < r3 < r2 < r1 | Backlinks | Raw View | Raw edit | More topic actions


Parents: WebHome > CaseStudies
This site is powered by the TWiki collaboration platform Powered by Perl This site is powered by the TWiki collaboration platformCopyright © 2008-2023 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback

mersin escort bayan adana escort bayan izmit escort ankara escort bursa escort