Tutorial: principles of port-based software and systems engineering

The robust principles of port-based engineering known to disciplines such as electrical engineering, mechatronics, and systems engineering are now finding practical expression in graphical software engineering through aspects of UML2 including:

UML2 Classes and Components are thus Holons. From http://en.wikipedia.org/wiki/Holon_(philosophy):


'A holon is a system (or phenomenon) that is a whole in itself as well as a part of a larger system. It can be conceived as systems nested within each other. Every system can be considered a holon, from a subatomic particle to the universe as a whole. On a non-physical level, words, ideas, sounds, emotions—everything that can be identified—is simultaneously part of something, and can be viewed as having parts of its own, similar to sign in regard of semiotics.

Since a holon is embedded in larger wholes, it is influenced by and influences these larger wholes. And since a holon also contains subsystems, or parts, it is similarly influenced by and influences these parts. Information flows bidirectionally between smaller and larger systems as well as rhizomatic contagion. When this bidirectionality of information flow and understanding of role is compromised, for whatever reason, the system begins to break down: wholes no longer recognize their dependence on their subsidiary parts, and parts no longer recognize the organizing authority of the wholes. Cancer may be understood as such a breakdown in the biological realm.

A hierarchy of holons is called a holarchy. The holarchic model can be seen as an attempt to modify and modernise perceptions of natural hierarchy.'.

Through progressive encapsulation of functionality as EncapsulatedClassifiers with declaration of provided and required services as Interfaces, UML2 Classes and Components promise to enable greatly improved development and testing of increasingly complex, reusable software components.


Visit also:

 

Using a Class with ports in a higher level system

You can use a Class with Ports as a part Property in a higher level system.

If the context using the Class only every interacts with the "instance" of the Class via its Ports (with the provided/required Interfaces as declarations), then the Class is replaceable within the context, promoting true design-by-contract and service orientation. The part Property can thus play a replaceable "role" within the higher context.



TIP: In MagicDraw UML one can also use a Class with Ports as the type of a Port to create nested ports, a powerful systems engineering feature !

UML2 Classes and Components are StructuredClassifiers: they can be decomposed into nested parts

The example shows how the composite structure compartment of a Class can be used to show deeply nested part Properties.

UML class diagram using composition Associations: SimplifiedVehicle: as an associative Class holarchy

This associative graphical composition view (well known to software engineers through UML Class Diagrams) is equivalent to the composite structure diagram view (familiar to systems, electrical, and mechatronics engineers through various engineering diagrams):

  • in the Class Diagram, named part Properties with multiplicity indicators appear as the end of Associations of AggregationKind 'composite'.
  • in the Composite Structure Diagram, part Properties appear as instance-like rectangles with multiplicities  in [brackets] and :Classifier type indicator.

Recomended naming conventions for port-based engineering in MagicDraw UML and Java

I have found the following notation conventions to be a good compromise between readability and Java-friendliness for port-based systems engineering in MagicDraw UML. (Note also that the MD SysML plugin with SysML FlowPort and ItemFlow support makes representation of signal processing systems easier.)

Consider a system with Audio. I recommend:

  • Use additional stereotypes to aid your port-based engineering.
  • Use Audio for the Interface (if needed, in some cases an Interface is overkill).
    • Do NOT use the IAudio "Eclipse" style, it is too easily confused with "input".
  • <<port>>: Use Audio_ (with a trailing underscore) for the "default implementor <<port>> Class" that provides the Interface Audio.
  • <<conjugate>>: Use Audio$ (which a trailing doller) for the conjugate that requires the Interface Audio.
    • Do NOT use the ~Audio conjugate convention, it is not Java-friendly, and it is too easily confused with the signal convention Audio~ (below)
  • <<signal>>: Use Audio~ (with a trailing tilde) to type InformationFlows.
    • The '~' looks a bit like wave.
    • This is NOT Java Friendly.

The relationship between the Interface name and the implementor/conjugate pair breaks down (as it should) for complex ports.

The example below is from the "signal processing" domain, however the principles scale well to UMl-driven port-based Java software engineering. 

Why port-based engineering is so powerful: "poor portless Arthur" vs. "smarter Martha with ports"

Imagine two school students, "Arthur" and "Martha". Both of them are going to provide services to their school, and community, and both will require some things as well (from their families). They are going to be both "used" by various Actors, who see them as Interfaces from different points of view:

  • Since both are at school they will have to be able to be educated by a Teacher (we hope), who will make them study(), swat(), research(), and sitExams(), activities which are supposed to help them Learn.
  • They both do sport for Exercise and have a Trainer (who will make them run(), jump(), skip() and swim()).
  • They both earn some money on the side in the kitchen of a local hotel run by a KitchenManager, who will ask them to Cook: fry(), stir(), and bake(). (Note that the KitchenManager is not very interested in how either of them can skip() or run(), let alone their ways of swat()ing for exams.)

And they are going to "use" the services of their Parents, who Support them by feed()ing them both with Food and imburse()ing them some pocket Money (presumably their sport classes cost more than they earn at with their hotel kitchen job).

We can represent the interaction of these Actors with different Interfaces as shown below, without yet specifying how those services will be encapsulated ! (We will compare the different encapsulation strategies of "poor portless Arthur" and "smarter Martha" next).

The PortlessHuman: poor mixed up Arthur (or how not to design reusable software)

Poor Arthur's way of doing things -without ports - is very cognitively mixed up. (He has been studying old-style software engineering at university and classic object-orientation, and he has been using some of those awfully bloated "GUI God Classes" with hundreds of methods of different kinds from certain well-known software houses, all the time thinking that it is "supposed" to be done that way.)

He tried to implement all the operations of all provided Interfaces with methods in a single Class !

He mixed up all the signatures for all provided Operations for all Interfaces as methods in one Class, and presumably he mixed up his data, too. He has his swim()ming near his swat()ing, and he can't add in any new Operations to one service Interface without changing and even recompiling his whole portless body to include a new implementing method ! If he wants to add in a new Operation in Exercise like squat() he will have to recompile methods that already work and are unchanged and have nothing to do with Exercise like stir() and research();

But isn't this the way most "object-oriented" software systems are implemented ?

Isn't this service orientation and design-by-contract, and aren't we meant to be using it ?

Isn't this what is shown in nearly all Java examples, where a single Class can implement many Interfaces, yet can only extend once ?

In fact - although still widely taught and used - it is a merely a very highly coupled and inflexible form of both service orientation and design-by-contract. There is a good reason why your home HiFi, your TV, planes that fly, and robots that work, are not built like this ! Let's see a smarter way of achieving service orientation by using some port-based systems engineering techniques that are centuries old.

But first, let us try to use poor portless Arthur and see how inflexible and hard to reuse he is !

Portless service orientation: strong coupling to implementation

Just some of the issues when attempting to use our portless Arthur class include:

  1. If you want to replace your implementation of portless Arthur you would have to replace the whole of Arthur !
  2. There is no easy way of injecting different port implementations of the provided Interfaces.
    One thus has to sacrifice dependency injection of fine-grained service implementations !
    (One can't have an injected PortFactory for creating port implementors which provide the fine-grained service interfaces.)
  3. In Java: if you want to just access a particular Interface like Cook, you have to subassign (implicit cast):
    Cook c = arthur;
    There is no easy way of prompting on your PortlessHuman to discover what it does or does not provide.
  4. It is very difficult to represent details of the interactions between portless Arthur and his environment.
  5. MagicDraw UML does not (yet) support display of required/provided Interfaces from part Properties.

 

Port-based service orientation: "Martha" the SmarterHuman

Student "Martha" is much smarter than portless Arthur. (This is probably because "Martha" has studied and applied real-world engineering instead of just software engineering for harmless web applications, and she wants to build things that actually work and do what they are meant to.) Smarter Martha realises that:

  1. Classes with hundreds, or even thousands, of methods are very hard to maintain.
  2. It does not make cognitive sense to try to implement different sorts of things all in the same place.
  3. It makes cognitive sense to keep your mind on things OF THE SAME NATURE within one implementor (don't have swim()ming near cook()ing).
  4. If you distribute your intelligence over well-defined Ports you become more autonomous.
  5. If you use Ports you can implement Interfaces piecewise and test them progressively.
  6. If you use Ports you can fetch the implementations of the Interfaces easily from an injected Factory as implementing ports.
  7. If you use Ports you can more easily handle Operation extensions to the service Interfaces or even the addition of completely new Interfaces without recompiling things that have nothing to do with the new Operation or Interface.
  8. If you use Ports you can also use service accessor operations to get at the Interfaces provided via ports.
  9. You can prompt on the service accessor operations in your IDE to get at and discover which service Interfaces are provided !

Best of all, if a service is never required, YOU NEVER HAVE TO CREATE AN IMPLEMENTOR AND WASTE ANY RESOURCES FOR IT ! (Poor portless softy engineer Arthur has to create resources to Exercise and Learn even when he only needs to Cook in a hotel kitchen as a holiday job.)

To make this magic happen, we need a new kind of top-level contract, one that says what interfaces the ports must provide and require, which is what the «meta»-Interface is all about. First however, let us see (next page) how one might use "Martha the SmarterHuman with Ports" as a part Property in a higher context.

Using Martha and the SmarterHuman Class as a part Property in a higher context

One can now connect clientinstances (and the ever-serving :Parent) to the Ports of a part Property martha:SmarterHuman_ in a higher context.

(The smarter humans amongst you will note that in this example I have used the port-based Class SmarterHuman_ - NOT used the Meta-Interface SmarterHuman - to type the part Property, because in UML2 an Interface most unfortunately can't support port notation ! We'll return to this significant problem later.)

In pure port-based engineering the Port always acts "on behalf" of its parent.

Walk up to your TV or home HiFi and turn the volume up or down:

  • If it is an old device you might have a a single volume knob as a control.
  • If it is a newer device it might have a group with volume up (+), volume down (-), and maybe also a "loudness mode" toggle if it is a HiFi.

Either way you are interacting with the device via a Port. However you are apparently changing the volume of the entire device (TV or HiFi):

The services provided via true ports always appear to act "on behalf" of the parent device.

Similarly, when you plug your headphones into the headphone socket of a device you are apparently hearing the whole device, not just the headphone Port.

(By comparison, consider now taping a small MP3 player onto the side of your TV set. There is a headphone socket, and there is a volume control group, however there is no sense in which these ports are now acting on behalf of the TV set ! Because there is no delegation from the ports of the MP3 player to the internal parts or Ports of parts of the TV.)

In Java, this effect of "ports acting on behalf of a parent" is quite easily acheived using inner classes to implement Ports, or by passing a parent reference on construction to an existing Port Class implementation that is "mixed in" to act on behalf of the parent. (We will see later exactly how this can be done in Java in the Instrument ModelServer system tutorials.)

 

Port-based software engineering is a way of promoting reuse through aggregation over inheritance

As your systems become increasingly port-based you are effectively moving from "vertical" inheritance-based reuse to "horizontal" aggregation-based reuse.

This can be represented as shown below. The Port (which acts on behalf of its parent), is more tightly bound to its parent than a regular Property. Recall that a Port is a specialisation of Property.

By design the special Port classes can act on behalf of their parent. In Java, this can easily be achieved by:

  1. EITHER implicitly as an inner Class acting on behalf of the outer Class.
  2. OR by expliclity acting via a reference to the parent that was passed on construction of the Port class

This means you can completely avoid the tedious "mixin and delegation" coding needed to evoke the impression that a reusing Class provides an Interface that a reused Class provides. Wow !

The «meta»-Interface and The Golden Rules of truly port-based systems engineering

I use the term «meta»-Interface to describe an Interface that serves only to declare the service Interfaces that form the purely port-based contract. The only operations of a «meta»-Interface are called Service Accessors. It is related to what I call The Golden Service Rule of port-based software engineering:

A truly port-based Class should only ever offer services via Ports as Operations of Interfaces, it should NEVER offer services directly.
(There is a loophole, namely that «friend»  Classes may need direct access to public methods not in the public «meta»-Interface.)

Implementations of the «meta»-Interface can choose to create "port implementors" of the promised Interfaces by:

  1. creating them on construction (not very efficient or flexible, however sometimes necessary to prevent latency on later access)
  2. lazy instantiation: create them on-the-fly as and when needed (and never before being accessed, thus saving resources)
  3. lazy instantiation combined with Factory-based Dependency Injection: very flexible and efficient.

There is a corresponding Golden Client Rule of port-based software engineering:

Clients should only ever access services of truly port-based Classes via the Service Accessor Operations of a Meta-Interface.

In Java, including some dependency injection using a factory, it looks like this:

HumanPortFactory f = new  SimulatedHumanPortFactory_(); // could of course be specified outside the application as XML
Human h = new SmartHuman_(f); // The Port implementors will be obtained from an injected factory.
h.cook().stir(); // will trigger creation of a Port that provides the grouped Cook service

You can now pass exactly the interfaces as needed to the relevant clients, like this:

   Teacher t = new Teacher_(h.learn());

The teacher client does not know - and need never know - what Port implementation was used to provide Learn, or even who provided the service. Nor does the teacher client ever have to deal with Interfaces like Cook that have nothing to do with the required Learn service.

The «meta»-Interface is great in Java code, but not in UML2, because an Interface can't show Ports.

The «meta»-Interface concept is simply wonderful for port-based engineering in Java:

  • It encourages clients to access services via service accessor Operations that provide service Interfaces via Ports.
  • The service Interfaces group related Operations together nicely, and make for very efficient, robust, and easy programming.
  • One can prompt on the service accessor Operations in IDE to discover the Interfaces effectively provided by a port-based Class.
  • One can use the service accessor Operations to trigger creation methods for Ports:
    • these creation methods can exploit flexible techniques like:
      • on-the-fly (lazy) instantiation of Port implementors
      • fetching Port implementations from an injected PortFactory for maximum flexibility.

Sounds simply wonderful. Just one big problem:

The «meta»-Interface approach fails notationally in UML2 because
a UML2 Interface is not an EncapsulatedClassifier, so it can't show Ports !

In other words, if you type a part Property by a «meta»-Interface you completely lose nearly all the advantages of holons and port-based engineering NOTATIONALLY (only), in UML2. Again: there is no problem with the elegant «meta»-Interface concept for port-based engineering in Java, it is only a notational problem for UML2 -compliant tools. And yet the Interface does support attributes typed by Property, and a Port is a specialization of Property, so the «meta»-Interface strategy is tantalizingly close to working in UML2.

There is another possibility; use an abstract Class to declare the Port contract. However it turns out to be a problem for single-inheritance Java .. 

 

The abstract UPMS <<serviceProvider>> Class is not good in Java !

So if the Java-friendly «meta»-Interface idea does not work well notationally in UML2 (because an Interface can't show Ports) why not just use an abstract UML2 Class with abstract Ports providing/requiring interfaces to specify the port-based contract (instead of a UML2 Interface) ?

In fact, this works quite well - notationally - in UML2, and that is the approach adopted for the «serviceProvider» proposed by the OMG's UPMS working group:

UML Profile and Metamodel for Services RFP UPMS “Services Metamodel”

However, that approach makes it very hard to get direct correspondence with Java, which only admits single inheritance of implementations:

The BIG PROBLEM for port-based engineering in Java: Once you use a Class for merely specifying the "contract",
you have used up your precious one shot at inheriting an existing implementation of the set of promised ports.

It truly is a headache for Java users (fans of multiple-inheritance languages are ROTF laughing.).


As explained above, there is another solution that would rescue the wonderful port-based «meta»-Interface concept for single-inheritance Java:

The UML Interface could be promoted to be an EncapsulatedClassifier that can show Ports

One problem with that, however, is that it implies a Interface might be instantiable (as, for example, a Classifier of an InstanceSpecification).

Example: using a port-based contract declared by an abstract <<serviceProvider>> Class

Now that we have defined a «serviceProvider» contract for the port-based HiFi as an abstract Class we can use it in a higher context, such as a HomeEntertainmentSystem. Because the «serviceProvider» stereotype has been applied to a Class (not an Interface), and because a UML2 Class can have and can show Ports (whereas a UML2 Interface can't) we can reuse the abstract HiFi as a "port-based contract" without specifying the implementation. A particular implementation of the port-based HiFi contract -such as HiFi_ - could also be injected.

While a bit fiddly (it takes patience if hand-coded, although it is easily forward-engineered), it looks very promising notationally. However:

BIG PROBLEM for Java: this strategy fails badly in Java in all but the simplest cases because
the one precious shot at inheritance of the implemented port set has been used up already !

(I hear fans of multiple-inheritance languages ROTF laughing yet again.)

Further Reading: the UPMS <<serviceProvider>>

I highly recommend also this series of articles on Service Oriented Architecture (SOA) by Jim Amsden:

Modeling SOA: Part 1. Service identification  

Modeling SOA: Part 2. Service specification

Modeling SOA: Part 3. Service realization

Modeling SOA: Part 4: Service composition

Although the application domain business service modelling is much removed from own areas of interest (such as systems engineering of scientific instruments and signal processing systems), many of the principles discussed in these articles overlap with traditional port-based engineering design recipes.