JaroslavTulach: /* Separation & Composition */ - 2011-03-21 13:05:49

Separation & Composition

←Older revision Revision as of 13:05, 21 March 2011
Line 11: Line 11:
== Separation & Composition ==
== Separation & Composition ==
-
The worst thing (with respect to [[evolution]]) an [[API]] design can make is to create a type that serves both purposes - it is a [[ClientAPI]] which is supposed to be called by everyone and also [[ProviderAPI]] implemented by everyone (everyone here is used in the meaning used while talking about [[distributed development]]).
+
The worst thing (with respect to [[evolution]]) an [[API]] designer can make is to create a type that serves both purposes - it is a [[ClientAPI]] which is supposed to be called by everyone and also [[ProviderAPI]] implemented by everyone (everyone here is used in the meaning used while talking about [[distributed development]]).
If you try to fit an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
If you try to fit an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).

JaroslavTulach: /* Mounting new Open Space via a Fixed Point */ - 2011-03-21 12:54:49

Mounting new Open Space via a Fixed Point

←Older revision Revision as of 12:54, 21 March 2011
Line 63: Line 63:
[[Image:OpenFixed.png|thumb|right]]
[[Image:OpenFixed.png|thumb|right]]
-
There is a FileSystem [[API]] in [[NetBeans]] and it is also capable to deliver events in batches. There were some listeners wanting to be notified about the last modification only. However, due to complex releations between objects and listeners in the system, it was impossible to tell in advance, whether an event comming from the batch is the last one to be delivered to the listener or not (thus we cannot reliably count getRemaining()) value and had to try different solution.
+
There is a FileSystem [[API]] in [[NetBeans]] capable to deliver events in batches. There were some listeners wanting to be notified about the last modification only. However, due to complex releations between objects and listeners in the system, it was impossible to tell in advance, whether an event comming from the batch is the last one to be delivered to the listener or not (thus we could not reliably count the return value of the ''getRemaining'' method) and had to try different solution.
-
We could add a method ''lastEventProcessed'' into the listener interface. However this is not allowed, as the listener (aka [[ProviderAPI]]) shall remain immutable for the sake of [[BackwardCompatibility]]. We could follow the advice described in [[ExtendingInterfaces]] page and create new marker interface ''LastProccessed'' with the above metioned method. But rather than that, we resolve to a trick that ''mounts'' new open space on top of existing one. First of all let's define new listener (or one could reuse ''Runnable'' in this case):
+
Adding a method ''lastEventProcessed'' into the ''ModificationListener'' interface would be the simplest solution. However this would harly be [[BackwardCompatibility|backward compatible]] as existing listener (aka [[ProviderAPI]]) implementation would not be compilable anymore. We could use the advice described in [[ExtendingInterfaces]] page and create new marker interface ''LastProccessed'' (extending ''ModificationListener'') with the above metioned method. But rather than that, we resolve to a trick that ''mounts'' new open space on top of existing one. First of all let's define new listener (alternatively one could reuse old good ''Runnable'' in this case):
<source lang="java" snippet="openfixed.postprocessor"/>
<source lang="java" snippet="openfixed.postprocessor"/>
Line 77: Line 77:
<source lang="java" snippet="openfixed.usemount"/>
<source lang="java" snippet="openfixed.usemount"/>
-
The implementation just makes sure to collect all registered and to notify them when all regular listeners are processed:
+
The implementation just makes sure to collect all registered ''ModificationPostListener''s and to notify them when processing of regular ''ModificationListener''s is over:
<source lang="java" snippet="openfixed.postimpl"/>
<source lang="java" snippet="openfixed.postimpl"/>
-
The solution enhances the existing ''open space'' (which is allowed), creates new ''fixed point'' and make sure it again contains new ''open space'' (so it is ready for future [[evolution]]). The whole set up is displayed on the picture with two ''open space'' arcs. However one does not have stop at two, the extensibility is unlimited...
+
The solution enhances the existing ''open space'' (which is allowed), creates new ''fixed point'' and makes sure it again contains new ''open space'' (so it is ready for future [[evolution]]). The whole set up is displayed on the picture with two ''open space'' arcs. However one does not have stop at two, the extensibility is unlimited...
[[Category:APIDesignPatterns]] [[Category:APIDesignPatterns:Evolution]] [[Category:APIDesignPatterns:Clarity]]
[[Category:APIDesignPatterns]] [[Category:APIDesignPatterns:Evolution]] [[Category:APIDesignPatterns:Clarity]]

JaroslavTulach: /* Extending Open Space */ - 2011-03-21 12:48:54

Extending Open Space

←Older revision Revision as of 12:48, 21 March 2011
Line 47: Line 47:
=== Extending ''Open Space'' ===
=== Extending ''Open Space'' ===
-
Sometimes, when there is storm of events, it is important to distinquish the last one. For example [[X Window]] system supports such kind of ''reminding'' counter. Applications can them mark their state as dirty and only react to the last event in such batch event storm.
+
Sometimes, when there is a storm of events, it is important to be able to identify the last one. For example the [[X Window]] system supports a kind of ''reminding'' counter. Each event comes with a number of known events yet to be delivered. Applications can then mark their state as dirty and wait with any action until the last event in a batch is delivered.
-
Such storms are unlikely to be real problem in our ''Calculator'' example, but still, let's try to enhance the system with a way to find out amount of remaining events:
+
Such storms are unlikely to be real problem in our ''Calculator'' example, but still for educational purposes, let's try to enhance the system with a way to find out amount of remaining events:
<source lang="java" snippet="openfixed.addgetter"/>
<source lang="java" snippet="openfixed.addgetter"/>
Line 57: Line 57:
<source lang="java" snippet="openfixed.pendingCount"/>
<source lang="java" snippet="openfixed.pendingCount"/>
-
We have evolved the [[ClientAPI]]. The event class. This is a classical approach used many times in the [[JDK]] itself (for example ''PropertyChangeEvent'' got additional ''getPropagationId''). For many purposes this is sufficient. However there can be more complicated use-cases that may require more complex evolution.
+
We have evolved the [[ClientAPI]] - the event class. This is a classical approach used many times in the [[JDK]] itself (for example ''PropertyChangeEvent'' got additional ''getPropagationId''). For many purposes this is sufficient. However there can be more complicated use-cases that may require more complex evolution.
=== Mounting new ''Open Space'' via a ''Fixed Point'' ===
=== Mounting new ''Open Space'' via a ''Fixed Point'' ===

JaroslavTulach: /* Calculator */ - 2011-03-21 12:45:26

Calculator

←Older revision Revision as of 12:45, 21 March 2011
Line 19: Line 19:
=== Calculator ===
=== Calculator ===
-
Let's go through this example while trying to design a bean (that is [[ClientAPI]]) and its listener (which is a [[ProviderAPI]]).
+
Let's go through an example trying to design a bean (e.g. [[ClientAPI]]) and its listener (which is a [[ProviderAPI]]).
<source lang="java" snippet="openfixed.bean"/>
<source lang="java" snippet="openfixed.bean"/>
-
The calculator has an internal sum which can change by adding numbers to it. Whenever a change happens, listeners are notified:
+
The calculator has an internal sum which can change by adding numbers to it. Whenever a change happens, listeners are notified. The actual handing of the listeners is abstracted from the bean implementation and we will provide four different ways to deal with them later:
<source lang="java" snippet="openfixed.listener"/>
<source lang="java" snippet="openfixed.listener"/>
-
Using [[Java]] interface is appropriate. We want the providers to implement all the methods of the interface. We know this is a ''fixed point'' that should not change - e.g. [[Java]] '''interface''' keyword is perfect. On the other hand, we are sure some [[evolution]] will be needed. Thus we associate this ''fixed point'' with a [[ClientAPI]] like ''open space'':
+
Using [[Java]] interface is appropriate. We want the providers to implement all the methods of the interface. We know this is a ''fixed point'' that should not change - e.g. [[Java]] '''interface''' keyword is perfect. On the other hand, we are sure some [[evolution]] will be needed. Thus we associate this ''fixed point'' with a [[ClientAPI]] to form an ''open space'' to help us with future changes:
<source lang="java" snippet="openfixed.event"/>
<source lang="java" snippet="openfixed.event"/>
Line 35: Line 35:
There are few different implementation of the listeners. The ''trivial'' one delivers events synchronously:
There are few different implementation of the listeners. The ''trivial'' one delivers events synchronously:
-
<source lang="java" snippet="openfixed.trivial'/>
+
<source lang="java" snippet="openfixed.trivial"/>
-
However as calling foreing code while holding a lock often leads to [[deadlock]]s, there is also trivial asynchronous implementation:
+
However as calling foreing code while holding own locks often leads to [[deadlock]]s, it maybe be more wise to avoid it. One option is to change the locking scheme. The other is to deliver events asynchronously. Let's try the asynchronous approach:
<source lang="java" snippet="openfixed.asynch"/>
<source lang="java" snippet="openfixed.asynch"/>

JaroslavTulach: /* Separation & Composition */ - 2011-03-21 12:39:21

Separation & Composition

←Older revision Revision as of 12:39, 21 March 2011
Line 13: Line 13:
The worst thing (with respect to [[evolution]]) an [[API]] design can make is to create a type that serves both purposes - it is a [[ClientAPI]] which is supposed to be called by everyone and also [[ProviderAPI]] implemented by everyone (everyone here is used in the meaning used while talking about [[distributed development]]).
The worst thing (with respect to [[evolution]]) an [[API]] design can make is to create a type that serves both purposes - it is a [[ClientAPI]] which is supposed to be called by everyone and also [[ProviderAPI]] implemented by everyone (everyone here is used in the meaning used while talking about [[distributed development]]).
-
If you try to fix an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
+
If you try to fit an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.

JaroslavTulach at 12:27, 21 March 2011 - 2011-03-21 12:27:58

←Older revision Revision as of 12:27, 21 March 2011
Line 1: Line 1:
-
This page provides description of differencies between so called [[API]] and '''SPI''. As the understanding of these terms is often present in minds of many developers (and as it often differs), let's start with description of [[ClientAPI]] and [[ProviderAPI]]. Then let see how to combine these together.
+
This page provides description of the differencies between so called [[API]] and '''SPI'''. As the understanding of these terms is often present in minds of many developers (and as it is often hard to find two developers to agree on the difference), let's start with description of [[ClientAPI]] and [[ProviderAPI]]. Then let see how to combine these together.
== [[ClientAPI]] ==
== [[ClientAPI]] ==

JaroslavTulach: /* Separation & Composition */ - 2011-03-20 19:49:31

Separation & Composition

←Older revision Revision as of 19:49, 20 March 2011
Line 14: Line 14:
If you try to fix an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
If you try to fix an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
-
 
-
[[Image:OpenFixed.png|thumb|right]]
 
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.
-
Let's imagine we want to design a listener (which is a [[ProviderAPI]]):
+
=== Calculator ===
 +
 
 +
Let's go through this example while trying to design a bean (that is [[ClientAPI]]) and its listener (which is a [[ProviderAPI]]).
 +
 
 +
<source lang="java" snippet="openfixed.bean"/>
 +
 
 +
The calculator has an internal sum which can change by adding numbers to it. Whenever a change happens, listeners are notified:
<source lang="java" snippet="openfixed.listener"/>
<source lang="java" snippet="openfixed.listener"/>
Line 27: Line 31:
<source lang="java" snippet="openfixed.event"/>
<source lang="java" snippet="openfixed.event"/>
-
The event is typical [[ClientAPI]]. It is called by the providers of the listener, but its implementation is provided by the [[API]] itself. It is made '''final''' and as such it may safely grow in the future. For example we may enhance it with a new getter counting the sequence number of the event:
+
The event is typical [[ClientAPI]]. It is called by the providers of the listener, but its implementation is provided by the [[API]] itself. It is made '''final''' and as such it may safely grow in the future. Moreover it has package private constructor (another good defensive [[API]] design habbit), so only the ''Calculator'' class may instantiate the instances.
 +
 
 +
There are few different implementation of the listeners. The ''trivial'' one delivers events synchronously:
 +
 
 +
<source lang="java" snippet="openfixed.trivial'/>
 +
 
 +
However as calling foreing code while holding a lock often leads to [[deadlock]]s, there is also trivial asynchronous implementation:
 +
 
 +
<source lang="java" snippet="openfixed.asynch"/>
 +
 
 +
All such implementations can be used and tested by a common code that looks like this:
 +
 
 +
<source lang="java" snippet="openfixed.commontest"/>
 +
 
 +
=== Extending ''Open Space'' ===
 +
 
 +
Sometimes, when there is storm of events, it is important to distinquish the last one. For example [[X Window]] system supports such kind of ''reminding'' counter. Applications can them mark their state as dirty and only react to the last event in such batch event storm.
 +
 
 +
Such storms are unlikely to be real problem in our ''Calculator'' example, but still, let's try to enhance the system with a way to find out amount of remaining events:
<source lang="java" snippet="openfixed.addgetter"/>
<source lang="java" snippet="openfixed.addgetter"/>
-
This is a classical example that has been used many times in the [[JDK]] itself (for example ''PropertyChangeEvent'' got additional ''getPropagationId''). However this is ''just'' an [[evolution]] of the [[ClientAPI]] part. However, it is possible to use the same trick to evolve the listener - e.g. [[ProviderAPI]] too!
+
The field is initialized by the listener support class before the event is dispatched to registered listeners:
-
Imagine you'd like to add a method ''lastEventProcessed'' into the listener interface. Of course, this is not allowed, as [[ProviderAPI]] shall remain immutable for the sake of [[BackwardCompatibility]]. However you can ''mount'' a new fixed point on the ''open space'' type:
+
<source lang="java" snippet="openfixed.pendingCount"/>
 +
 
 +
We have evolved the [[ClientAPI]]. The event class. This is a classical approach used many times in the [[JDK]] itself (for example ''PropertyChangeEvent'' got additional ''getPropagationId''). For many purposes this is sufficient. However there can be more complicated use-cases that may require more complex evolution.
 +
 
 +
=== Mounting new ''Open Space'' via a ''Fixed Point'' ===
 +
 
 +
[[Image:OpenFixed.png|thumb|right]]
 +
 
 +
There is a FileSystem [[API]] in [[NetBeans]] and it is also capable to deliver events in batches. There were some listeners wanting to be notified about the last modification only. However, due to complex releations between objects and listeners in the system, it was impossible to tell in advance, whether an event comming from the batch is the last one to be delivered to the listener or not (thus we cannot reliably count getRemaining()) value and had to try different solution.
 +
 
 +
We could add a method ''lastEventProcessed'' into the listener interface. However this is not allowed, as the listener (aka [[ProviderAPI]]) shall remain immutable for the sake of [[BackwardCompatibility]]. We could follow the advice described in [[ExtendingInterfaces]] page and create new marker interface ''LastProccessed'' with the above metioned method. But rather than that, we resolve to a trick that ''mounts'' new open space on top of existing one. First of all let's define new listener (or one could reuse ''Runnable'' in this case):
-
<source lang="java" snippet="openfixed.mount"/>
 
<source lang="java" snippet="openfixed.postprocessor"/>
<source lang="java" snippet="openfixed.postprocessor"/>
-
<source lang="java" snippet="openfixed.postevent"/>
 
-
The above changes enhance the existing ''open space'' (which is allowed), create new ''fixed point'' and make sure it again contains new ''open space'' (so it is ready for future [[evolution]]). The extensibility is unlimited.
+
Then add new method to the event class. As the class is [[ClientAPI]] it can absorb new methods without any problems:
 +
 
 +
<source lang="java" snippet="openfixed.mount"/>
 +
 
 +
The idea is that those willing to find out when batch processing is over, can implement the new listener and register themselves when they receive the first event:
 +
 
 +
<source lang="java" snippet="openfixed.usemount"/>
 +
 
 +
The implementation just makes sure to collect all registered and to notify them when all regular listeners are processed:
 +
 
 +
<source lang="java" snippet="openfixed.postimpl"/>
 +
 
 +
The solution enhances the existing ''open space'' (which is allowed), creates new ''fixed point'' and make sure it again contains new ''open space'' (so it is ready for future [[evolution]]). The whole set up is displayed on the picture with two ''open space'' arcs. However one does not have stop at two, the extensibility is unlimited...
 +
 
 +
[[Category:APIDesignPatterns]] [[Category:APIDesignPatterns:Evolution]] [[Category:APIDesignPatterns:Clarity]]

JaroslavTulach at 19:06, 20 March 2011 - 2011-03-20 19:06:13

←Older revision Revision as of 19:06, 20 March 2011
Line 1: Line 1:
 +
This page provides description of differencies between so called [[API]] and '''SPI''. As the understanding of these terms is often present in minds of many developers (and as it often differs), let's start with description of [[ClientAPI]] and [[ProviderAPI]]. Then let see how to combine these together.
 +
== [[ClientAPI]] ==
== [[ClientAPI]] ==

JaroslavTulach: /* Separation & Composition */ - 2011-03-20 07:26:52

Separation & Composition

←Older revision Revision as of 07:26, 20 March 2011
Line 17: Line 17:
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.
 +
Let's imagine we want to design a listener (which is a [[ProviderAPI]]):
 +
<source lang="java" snippet="openfixed.listener"/>
-
[[TBD]]: How to compose client and provider [[API]]s?
+
Using [[Java]] interface is appropriate. We want the providers to implement all the methods of the interface. We know this is a ''fixed point'' that should not change - e.g. [[Java]] '''interface''' keyword is perfect. On the other hand, we are sure some [[evolution]] will be needed. Thus we associate this ''fixed point'' with a [[ClientAPI]] like ''open space'':
 +
 
 +
<source lang="java" snippet="openfixed.event"/>
 +
 
 +
The event is typical [[ClientAPI]]. It is called by the providers of the listener, but its implementation is provided by the [[API]] itself. It is made '''final''' and as such it may safely grow in the future. For example we may enhance it with a new getter counting the sequence number of the event:
 +
 
 +
<source lang="java" snippet="openfixed.addgetter"/>
 +
 
 +
This is a classical example that has been used many times in the [[JDK]] itself (for example ''PropertyChangeEvent'' got additional ''getPropagationId''). However this is ''just'' an [[evolution]] of the [[ClientAPI]] part. However, it is possible to use the same trick to evolve the listener - e.g. [[ProviderAPI]] too!
 +
 
 +
Imagine you'd like to add a method ''lastEventProcessed'' into the listener interface. Of course, this is not allowed, as [[ProviderAPI]] shall remain immutable for the sake of [[BackwardCompatibility]]. However you can ''mount'' a new fixed point on the ''open space'' type:
 +
 
 +
<source lang="java" snippet="openfixed.mount"/>
 +
<source lang="java" snippet="openfixed.postprocessor"/>
 +
<source lang="java" snippet="openfixed.postevent"/>
 +
 
 +
The above changes enhance the existing ''open space'' (which is allowed), create new ''fixed point'' and make sure it again contains new ''open space'' (so it is ready for future [[evolution]]). The extensibility is unlimited.

JaroslavTulach: /* Separation & Composition */ - 2011-03-20 06:57:48

Separation & Composition

←Older revision Revision as of 06:57, 20 March 2011
Line 13: Line 13:
If you try to fix an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
If you try to fix an ''open space'' into a ''fixed point'', what will you get? You'll get a ''fixed point'' - e.g. no changes allowed. [[Evolution]] prevented (only [[ExtendingInterfaces]] approach is possible, but tons of ''instanceof'' spread around all usages are not [[beautiful]] nor sustainable).
-
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together!
+
[[Image:OpenFixed.png|thumb|right]]
 +
 
 +
Rather than mixing [[ClientAPI]] and [[ProviderAPI]], separate them and compose them together! Everytime you design a ''fixed point'', make sure that it is extensible. One example of that is [[RequestResponse]] pattern, but in general it is enough if you pass to the ''fixed point'' an ''open space'' argument. That will guarantee extensibility.
-
[[Image:OpenFixed.png|thumb|right]]
 
[[TBD]]: How to compose client and provider [[API]]s?
[[TBD]]: How to compose client and provider [[API]]s?