Supposons que vous ayez deux interfaces:
interface Readable {
public void read();
}
interface Writable {
public void write();
}
Dans certains cas, les objets d'implémentation ne peuvent prendre en charge que l'un d'entre eux, mais dans de nombreux cas, les implémentations prendront en charge les deux interfaces. Les personnes qui utilisent les interfaces devront faire quelque chose comme:
// can't write to it without explicit casting
Readable myObject = new MyObject();
// can't read from it without explicit casting
Writable myObject = new MyObject();
// tight coupling to actual implementation
MyObject myObject = new MyObject();
Aucune de ces options n'est terriblement pratique, d'autant plus si l'on considère que vous le souhaitez comme paramètre de méthode.
Une solution serait de déclarer une interface d'encapsulation:
interface TheWholeShabam extends Readable, Writable {}
Mais cela a un problème spécifique: toutes les implémentations qui prennent en charge Readable et Writable doivent implémenter TheWholeShabam si elles veulent être compatibles avec les personnes utilisant l'interface. Même s'il n'offre rien à part la présence garantie des deux interfaces.
Existe-t-il une solution propre à ce problème ou dois-je opter pour l'interface wrapper?
MISE À JOUR
En fait, il est souvent nécessaire d'avoir un objet à la fois lisible et inscriptible, donc séparer simplement les préoccupations dans les arguments n'est pas toujours une solution claire.
UPDATE2
(extrait comme réponse, il est donc plus facile de commenter)
UPDATE3
Veuillez noter que le principal cas d'utilisation n'est pas les flux (bien qu'ils doivent également être pris en charge). Les flux font une distinction très spécifique entre les entrées et les sorties et il y a une séparation claire des responsabilités. Pensez plutôt à quelque chose comme un bytebuffer où vous avez besoin d'un objet sur lequel vous pouvez écrire et lire, un objet qui a un état très spécifique qui lui est attaché. Ces objets existent car ils sont très utiles pour certaines choses comme les E / S asynchrones, les encodages, ...
UPDATE4
L'une des premières choses que j'ai essayées était la même que la suggestion donnée ci-dessous (vérifiez la réponse acceptée) mais elle s'est avérée trop fragile.
Supposons que vous ayez une classe qui doit retourner le type:
public <RW extends Readable & Writable> RW getItAll();
Si vous appelez cette méthode, le RW générique est déterminé par la variable qui reçoit l'objet, vous avez donc besoin d'un moyen de décrire cette var.
MyObject myObject = someInstance.getItAll();
Cela fonctionnerait, mais une fois de plus le lie à une implémentation et peut en fait lever des exceptions classcastex au moment de l'exécution (en fonction de ce qui est retourné).
De plus, si vous souhaitez une variable de classe de type RW, vous devez définir le générique au niveau de la classe.
la source
Réponses:
Oui, vous pouvez déclarer votre paramètre de méthode comme un type sous-spécifié qui étend à la fois
Readable
etWritable
:La déclaration de méthode semble terrible, mais son utilisation est plus facile que d'avoir à connaître l'interface unifiée.
la source
process(Readable readThing, Writable writeThing)
et si vous devez l'invoquer en utilisantprocess(foo, foo)
.<RW extends Readable&Writable>
?process
fait plusieurs choses différentes et viole le principe de la responsabilité unique.S'il y a un endroit où vous avez besoin
myObject
à la fois d'unReadable
etWritable
vous pouvez:Refactoriser cet endroit? La lecture et l'écriture sont deux choses différentes. Si une méthode fait les deux, elle ne suit peut-être pas le principe de la responsabilité unique.
Passez
myObject
deux fois, en tant que aReadable
et en tant queWritable
(deux arguments). Que la méthode se soucie-t-elle de savoir s'il s'agit ou non du même objet?la source
StreamWriter
vs.StreamReader
(et bien d'autres)Aucune des réponses ne traite actuellement de la situation lorsque vous n'avez pas besoin d'un élément lisible ou inscriptible mais des deux . Vous avez besoin de garanties que lorsque vous écrivez dans A, vous pouvez lire ces données depuis A, pas écrire vers A et lire depuis B et espérer simplement qu'elles sont en fait le même objet. Les cas d'utilisation sont nombreux, par exemple partout où vous utiliseriez un ByteBuffer.
Quoi qu'il en soit, j'ai presque terminé le module sur lequel je travaille et actuellement j'ai opté pour l'interface wrapper:
Maintenant, vous pouvez au moins faire:
Mes propres implémentations de conteneur (actuellement 3) implémentent toutes Container par opposition aux interfaces séparées, mais si quelqu'un oublie cela dans une implémentation, IOUtils fournit une méthode utilitaire:
Je sais que ce n'est pas la solution optimale, mais c'est toujours la meilleure solution pour le moment, car le conteneur est toujours un cas d'utilisation assez important.
la source
Étant donné une instance générique de MyObject, vous aurez toujours besoin de savoir si elle prend en charge les lectures ou les écritures. Vous auriez donc du code comme:
Dans le cas simple, je ne pense pas que vous puissiez améliorer cela. Mais si vous
readThisReadable
voulez écrire le Readable dans un autre fichier après l'avoir lu, cela devient gênant.Donc j'irais probablement avec ceci:
En prenant ceci comme paramètre,
readThisReadable
maintenantreadThisWholeShabam
, vous pouvez gérer n'importe quelle classe qui implémente TheWholeShabam, pas seulement MyObject. Et il peut l'écrire s'il est accessible en écriture et ne pas l'écrire s'il ne l'est pas. (Nous avons un vrai "polymorphisme".)Ainsi, le premier ensemble de codes devient:
Et vous pouvez enregistrer une ligne ici en demandant à readThisWholeShebam () de vérifier la lisibilité.
Cela signifie que notre ancien Readable-only doit implémenter isWriteable () (renvoyer false ) et write () (ne rien faire), mais maintenant il peut aller dans toutes sortes d'endroits où il ne pouvait pas aller auparavant et tout le code qui gère TheWholeShabam les objets s'en occuperont sans autre effort de notre part.
Une autre chose: si vous pouvez gérer un appel à read () dans une classe qui ne lit pas et un appel à write () dans une classe qui n'écrit pas sans jeter quelque chose, vous pouvez ignorer isReadable () et isWriteable () méthodes. Ce serait la façon la plus élégante de le gérer - si cela fonctionne.
la source