Another approach is to deprecate the direct
dependency declaration and
instead introduce a factory that is responsible for selecting the default,
but this breaks backwards compatibility.
Not completely true. Imagine you have (Role, "default") as your current
component and you wish to be able to choose various implementation from now
on. What you could do is this:
* Write a new component, a Factory, that overrides the current component,
i.e. by using the same (Role, "default"). This factory will then delegate to
whatever other implementation it wishes.
That's only possible if the public API of the CM allows you to grab
overriden components (non-default defaults :) )
From the user of (Role, "default")'s POV he won't see a single
change.
The "I don't care, just give me the
default" strategy works as long as
the component implementation is self-contained and doesn't involve data
communication. Let's take an example, PDF export.
Currently the PDF export is only possible via FOP. If we were to convert
the current interface + implementation class into a proper component, we'd
name it "default", since it's the only one and who needs a factory for
only
one implementation.
Yes but we could also be thorough and instead implement a
Provider<PDFExporter> instead.
People use it and they're happy because it
just works. But we might soon
add support for export via an office server. Suppose we want either FOP or
Office to be usable as the default PDF export implementation. And suppose
we'll want to keep both types of export active, so that we can use either
one as the implementation for the default export of documents, the FOP
implementation for exporting some kinds of documents like scientific
articles, and the office connector for generating PDFs for presentations.
Using the overrides mechanism we can only have one active at the same time,
unless we introduce yet another factory which we can bypass using manual
component lookup.
Based on your use case you'll need a UI to ask the user what he wants to
export, i.e. either a "scientific article" or a "presentation" and
from his
choice you'll pick the right implementation. You could also try to guess
that dynamically by looking at what is being exported but that'll require a
Factory to hold that logic.
I know what you mean though: for some reason you don't want that to be
dynamic and you wish it to be static.
There's a problem with dynamicity though. Imagine an extension that wants
to replace an implementation. With your proposal the best practice would be
to introduce a new Hint since the "default" is chosen statically at
configuration time. So that wouldn't work. After you install extensions
you'd need to stop the wiki, change the binding to the new implementation
from the extension and restart it. Of course you'd need to read the
documentation of the extension to know you have to use it in replacement.
Basically it would mean that extensions cannot override behaviors. They
would just be able to add new components (like new Macros) but not modify
behaviors at runtime.
But this only means we need to be able to change configuration without
stopping the wiki and make it reload configs (might prove useful for other
things as well), it doesn't mean we cannot do it "static".
If at some point we decide that the office
implementation works better,
we might consider changing the default implementation for the pdf component.
This means that we'll change the hint of the FOP implementation from
"default" to "fop", change the hint of the Office implementation
from
"office" to "default", and our new version of XE works great if
people read
the installation guide and properly configure the office connector.
In practice we would have 2 choices with our current component impl.:
Choice 1:
* Add 2 new implementations with hint1 and hint2 (hint1 being what was
"default" before)
* Keep the implementation with "default" hint but deprecate it and move it
to legacy. Refactor it so that it delegates yo hint1
* Add a Provider to decide which impl to use based on whatever conditions
we want
Choice 2:
* Move "default" impl to "hint1"
* Add "hint2"
* Change "default" implementation to be a Composite that delegates to
hint1 or hint2
But what about those that want to upgrade, but
are happy with the older
FOP implementation and don't want to add support for the office connector?
Yes, in this case this is transparent for them (and it's a good thing in
most caes!). This means they get autoupgrade to something better.
No, it means that for some reason we decided to change the underlying
technology (e.g. change of licence), it doesn't mean that everybody is happy
with the change.
They'll have to use patched versions of the
two component implementations
where their hints are reverted back to the old values. Easy? No. And even
though "default" means "I don't care what the implementation
does"
It doesn't really mean this. We use "default" only when there's a
single
implementation. When there are more than 1 each one has a hint that is a
qualifier to the implementation since there's no reason one is more default
than the other.
, here it does matter a lot which implementation
you're using. They have
different requirements, and it's important to know if the implementation
will require an office instance or not.
Saying that this won't happen since we're all really careful when
designing our components is wishful thinking. We've refactored other more
critical pieces of code than providing alternative implementations for a
component, so we will find ourselves in situations where we'll have to
switch from only one "default" implementation to several.
I agree that we've refactored. And it has worked so far right? ;)
> Designing our component manager to make it easy to transition is the
> right thing to do.
>
> Still, my major problem is not about overrides, but about the semantics
> of "default" (or lack of it). This says nothing about the actual
mechanisms
> behind the implementation, it just reflects the state of that particular
> component implementation: it's the default at the moment. Not caring what
> the implementation actually does works only when there is indeed just one
> possible implementation that is straight-forward. But in most cases, we do
> rely on another library that does the work for us, and libraries die, better
> alternatives come along, and changing that internal aspect of the
> implementation will sometimes be backwards incompatible, or have a different
> behavior. Sure, it does the same job, but it does it so differently that
> some will prefer to use the other approach. We have to let users decide
> which is their "default", and having multiple implementations with the
> "default" hint but different priorities is not very intuitive. Why not
make
> everything default and remove hints completely if we don't really put any
> meaning into the hint?
This also means that we should only have components where there is a chance
that another implementation might exist, because to the limit you can
separate the interface from the implementation for any little piece of code
that you write.
I sort of feel you for this default thing, but at the same time, it's also a
matter of education of the developer, which needs to make sure that they put
a technology hint to the component, besides the default hint. The pb is
that, as long as there is only one implementation, regardless of the
technology it's based on, you also need to put the default hint since
otherwise you'll have to hardcode the reference to the technology everywhere
if you wanna use that service. so in this case default would mean 'this is
the one that should be used because there's no other, you dumb CM that is
not capable of seeing that'.
This brings back some memories, but I don't know from what, about a system
that was giving the available implementation regardless of its name. For
example, we could make the CM return the only implementation, if only one
exists, when asking for a component, regardless of its hint, so we don't
have to put default everywhere. But then we need a strategy for the case
when there are more.
And "default" adds another assumption: XWiki Enterprise is the ultimate
target. Our defaults are the only ones that matter. As an example, all the
*Configuration components have just one "default" implementation, which
relies on xwiki.properties, XWiki.XWikiPreferences etc. Doesn't that tie the
platform to the XWiki Enterprise wiki?
This not true in xwiki commons and rendering because I've made sure that
we could use them outside of the XWiki Platform. They have default
implementation that don't use XWiki Configuration module.
It's not a direct dependency visible at
compilation time, it's worse, and
invisible assumption about the final runtime. It's certainly not the default
for other types of users that want to embed xwiki-commons or xwiki-platform
components in a different type of end product. To me this isn't the default
configuration, this is the default configuration used by XWiki Enterprise,
thus my proposal of using something else as the generic component hint
instead of "default".
Ok thanks for the explanations. I understand better now what you mean.
Actually what you suggest could already be implemented using a best
practice of always using Providers when you want a Component injected. In
this manner by default you'll get the Generic Provider but anyone could
implement a specific Provider implementation for it that would choose
between various implementation based on whatever (a value in a
META-INF/role-bindings.txt file, data from DB, etc).
<side note>Only issue with having Providers everywhere is that you get
late verification of your system coherence since dependencies will be
resolved only when they're used. OTOH this is a necessity in a fully-dynamic
system ;)</side note>
Also, the notion of default doesn't always have a meaning. There are lots
of cases when there are NO default. For example Macros or Transformations
or…
Let's continue the discussion it's interesting :)
I'd like to review a bit the other Component system out there again to see
what they do for this. It's important that they have support for this since
we want to be able to switch to them one day. The 3 that I would review are:
* Guice
* CDI
* OSGi
yes, we should learn from others. Maybe even use one?
Thanks,
Anca
Thanks
-Vincent
_______________________________________________
devs mailing list
devs(a)xwiki.org
http://lists.xwiki.org/mailman/listinfo/devs
_______________________________________________
devs mailing list
devs(a)xwiki.org
http://lists.xwiki.org/mailman/listinfo/devs
_______________________________________________
devs mailing list
devs(a)xwiki.org