AssemblableTypes

From APIDesign

(Difference between revisions)
Jump to: navigation, search
Current revision (20:14, 25 May 2011) (edit) (undo)
 
(6 intermediate revisions not shown.)
Line 1: Line 1:
-
By splitting [[API]]s into multiple [[module]]s one increases their flexibility. The [[API]] become independent and can be used in new contexts, without the rest of the [[API]]s being present. However one also complicates the usage, as for some, original (in case the [[API]] used to be present as a whole) [[usecase]]s, the users need to try harder to make the [[API]]s work together. That is indeed not [[good]] (as it increases [[Time To Market]]). This article describes one of my advantures where I had to deal with similar situation.
+
By splitting [[API]]s into multiple [[module]]s one increases flexibility of their reuse. The [[API]]s become more independent and can be used in new contexts, without the rest of the [[API]]s dragged around. However one also complicates the usage, as for some, original (in case the [[API]] used to be present as a whole) [[usecase]]s, the users need to try harder to make the [[API]]s work together. That is indeed not [[good]] (as it increases [[Time To Market]]). This article describes one of my advantures where I had to deal with similar situation.
== Setup ==
== Setup ==
Line 11: Line 11:
Users of the [[NetBeans]] [[API]] People often create their own {{NB|org-openide-loaders|org/openide/loaders|DataObject}} implementations to represent textual data. Good, there is support for that in the ''Loaders API''. There is however common requirement to show not only editor, but also other view, e.g. to use ''MultiView API''.
Users of the [[NetBeans]] [[API]] People often create their own {{NB|org-openide-loaders|org/openide/loaders|DataObject}} implementations to represent textual data. Good, there is support for that in the ''Loaders API''. There is however common requirement to show not only editor, but also other view, e.g. to use ''MultiView API''.
-
However ''Loaders API'' knows nothing about ''MultiView API'' and ''MultiView API'' knows nothing about ''Loaders API''. Their only common dependency is the ''Text API''. Can I write a wizard to generate simple, maintainable code that defines {{NB|org-openide-loaders|org/openide/loaders|DataObject}} represented by a
+
However ''Loaders API'' knows nothing about ''MultiView API'' and ''MultiView API'' knows nothing about ''Loaders API''. Their only common dependency is the ''Text API''. Can I write a wizard to generate simple, maintainable code that defines {{NB|org-openide-loaders|org/openide/loaders|DataObject}} represented by few
-
{{NB|org-netbeans-core-multiview|org/netbeans/core/spi/multiview|MultiViewElement}}?
+
{{NB|org-netbeans-core-multiview|org/netbeans/core/spi/multiview|MultiViewElement}}s including one for text editor?
== History ==
== History ==
-
[[TBD]]
+
The common style to achieve this is to create a new, [[AbstractedTypes]] and put them into new module that both ''text'' and ''loaders'' [[API]] would depend on. This is fine approach, but requires creation of whole new [[API]] module. In my case that was not really appropriate. Any other option?
== Improvements ==
== Improvements ==
-
[[TBD]]
+
Interesting new possibility is to use [[Java]] generics. Instead of abstracting the common interface (that ''text'' and ''loaders'' [[API]] needs), one can enumerate a set of common interfaces comming for example from [[JDK]] and require them all:
 +
 
 +
<source lang="java">
 +
<T extends Runnable & Callable & PropertyChangeListener> int myMethod(T multiType);
 +
</source>
 +
 
 +
This is gettting closer to the style of functional programming as pioneered by [[Haskell]] (or [[Clean]]). Define a lot of abstract ''categories'' (in their terminology ''classes'', but don't confuse that with [[OOP]] classes) and then use these categories to assemble appropriate type for each function/method.
 +
 
 +
In case one can find suitable types in the common dependencies (like in [[JDK]]), this kind of ''on the fly'' creation of types is usually more flexible.
 +
 
== Result ==
== Result ==
-
[[TBD]]
+
Thus I tried to use similar approach in the new version of the ''multiview'' [[API]]. Sure, I knew the most common use is to pass in ''DataObject'' (but I could not). Then I realized that the only two capabilities I need from such ''DataObject'' are ability to query for additional interface (e.g. [[Lookup]]) and being {{JDK|java/io|Serializable}}. For a while I was considering to use:
 +
 
 +
<source lang="java">
 +
<T extends Lookup & Serializable> Component createMultiView(String mimeType, T context);
 +
</source>
 +
 
 +
This would require people to create their own subclasse of [[Lookup]] and make sure they know how to persist it (at least as a reference) and recover its state during serialization. Not impossible, but a bit complicated. Then I got another idea:
 +
 
 +
<source lang="java">
 +
<T extends Lookup.Provider & Serializable> Component createMultiView(String mimeType, T context);
 +
</source>
 +
 
 +
Not only {{NB|org-openide-util-lookup|org/openide/util|Lookup.Provider}} is simpler interface with a single method (and thus easier to honor the contract and make it serializable), but if you look at type definition of {{NB|org-openide-loaders|org/openide/loaders|DataObject}}, you'll notice that it already implements both - the {{NB|org-openide-util-lookup|org/openide/util|Lookup.Provider}} as well as {{JDK|java/io|Serializable}}!
 +
 
 +
Here we are getting to the concept of [[AssemblableTypes]]. Not only we disassemble the parameters of some method, but we make sure, that there exist important types in the system that implement all these [[AssemblableTypes]]. If we have such types we can marry the modularity with ease of use. Type parameters stay modular, yet there are implementations that naturaly fullfil them all.
 +
 
 +
 
 +
[[Category:APIDesignPatterns]] [[Category:APIDesignPatterns:Evolution]] [[Category:APIDesignPatterns:Meta]]

Current revision

By splitting APIs into multiple modules one increases flexibility of their reuse. The APIs become more independent and can be used in new contexts, without the rest of the APIs dragged around. However one also complicates the usage, as for some, original (in case the API used to be present as a whole) usecases, the users need to try harder to make the APIs work together. That is indeed not good (as it increases Time To Market). This article describes one of my advantures where I had to deal with similar situation.

Contents

Setup

There are following modules In the NetBeans APIs:

  • Text API - contains class like CloneableEditorSupport.Pane and some of its implementations and can open editor for any source of characters
  • MultiView API - depends on Text API and provides another implementation of the pane which can be created by MultiViewFactory.
  • Loaders API - which depends on Text API and connects it with real files on disk via functionality provided by DataObject.

Users of the NetBeans API People often create their own DataObject implementations to represent textual data. Good, there is support for that in the Loaders API. There is however common requirement to show not only editor, but also other view, e.g. to use MultiView API.

However Loaders API knows nothing about MultiView API and MultiView API knows nothing about Loaders API. Their only common dependency is the Text API. Can I write a wizard to generate simple, maintainable code that defines DataObject represented by few MultiViewElements including one for text editor?

History

The common style to achieve this is to create a new, AbstractedTypes and put them into new module that both text and loaders API would depend on. This is fine approach, but requires creation of whole new API module. In my case that was not really appropriate. Any other option?

Improvements

Interesting new possibility is to use Java generics. Instead of abstracting the common interface (that text and loaders API needs), one can enumerate a set of common interfaces comming for example from JDK and require them all:

<T extends Runnable & Callable & PropertyChangeListener> int myMethod(T multiType);

This is gettting closer to the style of functional programming as pioneered by Haskell (or Clean). Define a lot of abstract categories (in their terminology classes, but don't confuse that with OOP classes) and then use these categories to assemble appropriate type for each function/method.

In case one can find suitable types in the common dependencies (like in JDK), this kind of on the fly creation of types is usually more flexible.

Result

Thus I tried to use similar approach in the new version of the multiview API. Sure, I knew the most common use is to pass in DataObject (but I could not). Then I realized that the only two capabilities I need from such DataObject are ability to query for additional interface (e.g. Lookup) and being Serializable. For a while I was considering to use:

<T extends Lookup & Serializable> Component createMultiView(String mimeType, T context);

This would require people to create their own subclasse of Lookup and make sure they know how to persist it (at least as a reference) and recover its state during serialization. Not impossible, but a bit complicated. Then I got another idea:

<T extends Lookup.Provider & Serializable> Component createMultiView(String mimeType, T context);

Not only Lookup.Provider is simpler interface with a single method (and thus easier to honor the contract and make it serializable), but if you look at type definition of DataObject, you'll notice that it already implements both - the Lookup.Provider as well as Serializable!

Here we are getting to the concept of AssemblableTypes. Not only we disassemble the parameters of some method, but we make sure, that there exist important types in the system that implement all these AssemblableTypes. If we have such types we can marry the modularity with ease of use. Type parameters stay modular, yet there are implementations that naturaly fullfil them all.

Personal tools
buy