J'ai un problème conceptuel avec une implémentation correcte du code qui semble nécessiter un héritage multiple, ce ne serait pas un problème dans de nombreux langages OO, mais comme le projet est pour Android, il n'y a rien de tel que multiple extends
.
J'ai un tas d'activités, provenant de différentes classes de base, comme simples Activity
, TabActivity
, ListActivity
, ExpandableListActivity
, etc. De plus j'ai quelques fragments de code que je dois lieu dans onStart
, onStop
, onSaveInstanceState
, onRestoreInstanceState
et autres gestionnaires d'événements standard dans toutes les activités.
Si j'ai une seule classe de base pour toutes les activités, je placerais le code dans une classe dérivée intermédiaire spéciale, puis créerais toutes les activités en l'étendant. Malheureusement, ce n'est pas le cas, car il existe plusieurs classes de base. Mais placer les mêmes portions de code dans plusieurs classes intermédiaires n'est pas une solution, à mon humble avis.
Une autre approche pourrait être de créer un objet d'assistance et de déléguer tous les appels des événements susmentionnés à l'aide. Mais cela nécessite que l'objet d'assistance soit inclus et que tous les gestionnaires soient redéfinis dans toutes les classes intermédiaires. Il n'y a donc pas beaucoup de différence avec la première approche ici - encore beaucoup de doublons de code.
Si une situation similaire se produisait sous Windows, je sous-classerais la classe de base (quelque chose qui "correspond" à la Activity
classe dans Android) et y intercepterais les messages appropriés (en un seul endroit).
Que peut-on faire en Java / Android pour cela? Je sais qu'il existe des outils intéressants tels que l'instrumentation Java ( avec de vrais exemples ), mais je ne suis pas un gourou de Java, et je ne sais pas si cela vaut la peine d'essayer dans ce cas spécifique.
Si j'ai raté d'autres solutions décentes, veuillez les mentionner.
MISE À JOUR:
Pour ceux qui pourraient être intéressés à résoudre le même problème dans Android, j'ai trouvé une solution de contournement simple. Il existe la classe Application , qui fournit, entre autres, l'interface ActivityLifecycleCallbacks . Il fait exactement ce dont j'ai besoin, nous permettant d'intercepter et d'ajouter de la valeur à des événements importants pour toutes les activités. Le seul inconvénient de cette méthode est qu'elle est disponible à partir du niveau 14 de l'API, ce qui n'est pas suffisant dans de nombreux cas (la prise en charge du niveau 10 de l'API est aujourd'hui une exigence typique).
decordator pattern
. Il s'agit d'un dernier recours, qui montre bien ce que je préfère éviter: la duplication de code. J'accepterai votre réponse, si aucune autre idée instante n'apparaît. Puis-je éventuellement utiliser des génériques pour généraliser le code des "intermédiaires"?Je pense que vous essayez d'éviter le mauvais type de duplication de code. Je crois que Michael Feathers a écrit un article à ce sujet mais malheureusement je ne le trouve pas. La façon dont il le décrit est que vous pouvez considérer votre code comme ayant deux parties comme une orange: l'écorce et la pulpe. L'écorce est l'étoffe comme les déclarations de méthode, les déclarations de champ, les déclarations de classe, etc. La pulpe est l'étoffe à l'intérieur de ces méthodes; la mise en oeuvre.
En ce qui concerne SEC, vous voulez éviter la duplication de la pulpe . Mais souvent, dans le processus, vous créez plus de croûte. Et ça va.
Voici un exemple:
Cela peut être refactorisé en ceci:
Nous avons ajouté beaucoup d'écorce dans ce refactoring. Mais, le deuxième exemple a un beaucoup plus propre
method()
avec moins de violations DRY.Vous avez dit que vous aimeriez éviter le modèle de décorateur car il entraîne une duplication de code. Si vous regardez l'image dans ce lien, vous verrez qu'elle ne fera que dupliquer la
operation()
signature (par exemple: rind). L'operation()
implémentation (pulp) doit être différente pour chaque classe. Je pense que votre code finira par être plus propre et qu'il y aura moins de duplication de pulpe.la source
Vous devriez préférer la composition à l'héritage. Un bon exemple est le " modèle " IExtension dans le framework .NET WCF. En bref, vous avez 3 interfaces, IExtension, IExtensibleObject et IExtensionCollection. Vous pouvez ensuite composer les différents comportements avec un objet IExtensibleObject en ajoutant des instances IExtension à sa propriété Extension IExtensionCollection. En java, cela devrait ressembler à ça, mais pas que vous deviez créer votre propre implémentation IExtensioncollection qui appelle les méthodes attach et detach lorsque des éléments sont ajoutés / supprimés. Notez également qu'il vous appartient de définir les points d'extension dans votre classe extensible. L'exemple utilise un mécanisme de rappel de type événement:
Cette approche présente l'avantage de la réutilisation des extensions si vous parvenez à extraire des points d'extension communs entre vos classes.
la source
processor.addHandler(console)
fourni quiConsoleProcessor
implémenterait l'interface de rappel elle-même. Le modèle "d'extension" ressemble à un mélange devisitor
etdecorator
, mais est-il nécessaire dans ce cas?À partir d'Android 3.0, il peut être possible de résoudre ce problème avec élégance en utilisant un fragment . Les fragments ont leurs propres rappels de cycle de vie et peuvent être placés dans une activité. Je ne suis pas sûr que cela fonctionnera pour tous les événements.
Une autre option dont je ne suis pas sûr non plus (manquant d'un savoir-faire Android approfondi) pourrait être d'utiliser un décorateur de la manière opposée suggérée par k3b: créer un
ActivityWrapper
dont les méthodes de rappel contiennent votre code commun, puis transmettre à unActivity
objet encapsulé (votre classes d'implémentation réelles), puis demandez à Android de démarrer ce wrapper.la source
Il est vrai que Java n'autorise pas l'héritage multiple, mais vous pouvez le simuler plus ou moins en faisant que chacune de vos sous-classes SomeActivity étende la classe Activity d'origine.
Vous aurez quelque chose comme:
la source