17.3 Subject and Observer Aspects – old

The following is an example of two related aspects, subject and observer, which have to be used in tandem. Objects of classes with a subject aspect may be observed by a set of objects with an observer aspect. An observer object subscribes to certain events in the subject object, events that typically change the state of the subject object. In a case of such an event, the observer will be notified.

The types of these subject and observer aspects are defined by classes, in a module ObserverPattern:

ObserverPattern: obj  
   class Subject:
      observers: obj Set(#Observer)
      subscribe(obs: ref Observer):
         observers.insert(obs)
      publish:
         observers.scan
            current.notify(this(Subject))
      inner(Subject)
   class Observer:
      class ObservedSubject:< Subject
      notify(theSubject: ref ObservedSubject):<
         inner(notify)

Subjects have a set observers of Observer objects; the method subscribe makes an Observer object an element of this set; publish is called in case the observeres should be notified about changes, so it will for each of the observers call their notify.

+++ måske bullet opstilling og/eller en figur – helt afgørende at man forstår denne her.

+++Forslag: Subject and observer works this way:

  • Given a Subject object denoted by subjectToBeObserved, an Observer object anObserver subscribes to events in the subject by invoking subjectToBeObserved.subscribe(anObserver). Several Observer objects may subscribe to the same Subject object; subscribing implies that the observer is included the the observers set.
  • In case of an event that changes the state of the Subject object in such a way that its observers should get to know, the Subject object invokes its publish method: this will for each of the observers in the observers set call its notify method.

In order for an Observer to know which Subject notifies it, the notify method has a reference parameter, theSubject, that denotes the actual Subject:

Note that the type of the theSubject parameter is the virtual class ObservedSubject. Typically a subject is an object of not just the class Subject, but rather of a subclass of Subject. ObservedSubject is therefore constrained by Subject.

A scenario that calls for these aspects is the following: Every day the bank checks if there has been some suspicious transactions on accounts, and in case, both a special alarm part of the bank and the customer are notified of the event. This is done by giving each account a subject aspect and both alarm and each customer an observer aspect.

Account objects have a subject aspect by means of an object data-item asSubject, while Customer objects and the alarm object each has an asObserver data-item object. However, it is not enough to type these object data-items with Subject or Observer as illustrated below:

class Account(owner: ref Customer):   
   -:-   
   asSubject: obj Subject

class Customer(id: var Integer):
   -:-
   asObserver: obj Observer

alarm: obj
   asObserver: obj Observer

The type of asSubject object cannot just be Subject, as it has to report things about the Account-object. We therefore define a subject aspect specifically for Account objects:

class AccountSubject(whichAccount: ref Account): Subject
   issueWithTransactions:
      "There is an issue with the account of: ".print
      whichAccount.owner.print
   inner(AccountSubject)

This class is used to define the asSubject in Account:

class Account(owner: ref Customer):
   -:-
   asSubject: obj AccountSubject(this(Account))
      subscribe(alarm.asObserver) 
      subscribe(owner.asObserver)

Recall that an object defined by obj is generated and executed as part of the generation of the object in which the object is. So, each time an Account object is generated, the asSubject object is generated and executed, with the implication that the alarm and the owner are subscribed as observers of this account. Note that the parameters to the invocation of subscribe are not references to alarm or owner, but rather to their asObserver aspect objects. +++ tror generelt at flere figurer vil være en god ide.

In a similar way, the asObserver object data-items of Customer and alarm are defined subclassed from Observer:

class Customer(name: var String):
   asObserver: obj Observer
      class ObservedSubject:: AccountSubject  
      notify::
         theSubject.issueWithTransactions   
...
alarm: obj
   asObserver: obj Observer
      class ObservedSubject:: AccountSubject 
      notify::
         theSubject.issueWithTransactions

The virtual class ObservedSubject in Observer represents the type of the aspect of observed subjects. In general, i.e. in class Observer, this is constrained to be subclassed from Subject, while in the above code, ObservedSubject is said to be (::) AccountSubject, as this is the (super)type of the observed aspects.

As mentioned, the scenario for the Accounts being subject for observation, and for Customer and alarm to be observers is that the bank on regular basis go through all its account to find if there has been any issue with the transactions on accounts. This may be that the transactions form a suspicious pattern, e.g. because the account has been hacked.

Money mule example

As a simple example we consider the case of money mules. A money mule is someone who transfers (typically illegally acquired) money on behalf of someone else. Money mules can move money in various ways, including through bank accounts.

Some money mules know they are supporting criminal enterprises; others are unaware that they are helping criminals profit.

Money mules often receive a commission for their service, or they might provide assistance because they believe they have a trusting or romantic relationship with the individual who is asking for help.

Acting as a money mule is illegal, therefore the customer is also included as an observer.

For the purpose of simplicity we assume that money muling takes place in one day, i.e. there is a deposit of a certain amount and a transfer of the same amount to another account on the same day. This activity may not be money muling, but it is still worth being reported as an issue. In real life banking there has to be a pattern of this pair of deposit/transfer.

From before we have the class Transaction:

class Transaction(
   what: var String, 
   whichDate: var Date,
   whichTime: var Time.Hours,
   whichAmount: var Real):
   ...

For the purpose of checking illegal transactions, we define a special class of transactions:+++ why is this needed?

class TransferTransaction(from, to: ref Account): Transaction
   ... +++ nothing here?

In addition we add the transferTo method to class Account, and as we just consider transaction for one day, we also introduce a new set of transactions:

class Account(owner: ref Customer):
   -:-
   transferTo(amount: var Real, to: ref Account):
      to.deposit(amount)
      transactionsOfToday.insert(
         TransferTransaction(
            "transferTo", clock.today, clock.now, amount, this(Account),to))

   transactionsOfToday: obj Set(Transaction)

Account will for this purpose also have the method checkTransactions: +++ nu er der lidt kompliceret

class Account(owner: ref Customer):
   -:-
   checkTransactions:
      -- find if there is a deposit and a transfer of the same amount
      -- at the same day, a simple case of money muling
      
      sameAmountSameDay -> b: var Boolean:
         scan:  
            transactionsOfToday.scan
               amount: var Real  
               if (current.what = "deposit") :then
                  amount := current.whichAmount
                  transactionsOfToday.scan
                     if (current.what = "transferTo") :then
                        if (current.whichAmount = amount) :then
                           b := true
                           leave(scan)

      if (sameAmountSameDay) :then   
         asSubject.publish 
  

Det vil være fint om der er et komplet eksempel som kan checkes af compileren og som kan køre. Kompleksiteten gør jo at man nemt overser noget!

The Bank object (or some object within the Bank) calls this method for each Account object in the list of accounts:

...
accountsFile: obj OrderedList(#Account)
...
accountsFile.scan
   current.checkTransactions 

The following Sequence Diagram shows the sequence of method calls for a partial run.