2010-03-22 24 views
12

Je veux avoir des objets Java immuables comme ceci (fortement simplifié):classe Mutable comme un enfant d'une classe immuable

class Immutable { 

    protected String name; 

    public Immutable(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 

} 

Dans certains cas, l'objet ne doit pas seulement être lisible mais muable, pour que je puisse ajouter mutabilité par héritage:

public class Mutable extends Immutable { 

    public Mutable(String name) { 
     super(name); 
    } 

    public void setName(String name) { 
     super.name = name; 
    } 

} 

Bien que ce soit techniquement bien, je me demande si elle est conforme à la POO et l'héritage que mutable est également de type immuable. Je veux éviter le crime OOP pour lancer UnsupportedOperationException pour un objet immuable, comme le fait l'API de collections Java.

Qu'en pensez-vous? D'autres idées?

+1

C'est exactement ce que fait l'objectif C: 'NSMutableString' hérite de' NSString', 'NSMutableArray' hérite de' NSArray', etc. – Nefrubyr

Répondre

14

Évitez d'appeler le parent "Immutable" car il devient un mensonge dans la classe enfant - si vous voulez voulez faire une classe immuable, il devrait être final aussi afin d'éviter exactement ce problème.

Joda Time utilise "ReadableXXX" pour donner l'idée que "cette classe fournit seulement un accès en lecture, d'autres sous-classes peuvent être mutables". Je ne suis pas sûr si j'aime cette idée, mais je ne peux pas dire que j'ai vu beaucoup d'alternatives.

Fondamentalement, le problème est avec l'expression d'un négatif - Immutable décrit ce que vous ne pouvez pas ne (muter) et qui ne peuvent être sensiblement exécutées dans les sous-classes. (Même si les champs au sein de Immutable étaient définitifs, cela n'arrêterait pas une sous-classe ayant ses propres champs mutables.)

3

Les classes immuables doivent être final précisément pour éviter les sous-types modifiables.

Permettre à un sous-type d'une classe immuable de rompre le contrat immuable rend la classe immuable inutilisable. Cela peut être légal dans le sens où Java vous permet de le faire (l'immuabilité n'est pas imposée dans la langue) mais une telle classe n'est pas immuable tant qu'elle peut être sous-classifiée.

C'est pourquoi String est final.

+1

Considérons que vous avez besoin de la classe de base ImmutableCollection et des sous-classes ImmutableList, ImmutableSet. Comment pouvez-vous faire ici la classe de base finale? Le fait est qu'aucun compilateur ne peut exprimer toutes les contraintes qui peuvent apparaître dans votre modèle. Nous devrions donc nous attendre à ce que le compilateur vérifie tout pour nous. – Alexey

1

Je trouve votre code plutôt curieux.

Pour implémenter un tel comportement Immuable, je me serais plutôt reposé sur une interface Immutable, fournissant uniquement la méthode getter, si l'objet contient les deux. De cette façon, les opérations s'appuyant sur les objets immuables auraient appelé l'interface, alors que d'autres auraient appelé l'objet. Et, si vous ne voulez vraiment pas que vos objets immuables soient castés en tant que mutables, vous pouvez alors utiliser des proxies, et tous les outils d'entreprise (aspects, etc.). Mais généralement, compter sur la bonne volonté des autres développeurs est une manière douce de les rendre responsables de leurs erreurs (comme jeter l'immuable dans mutable).

+1

Les proxies peuvent être utilisés pour rendre un objet en lecture seule, mais ils ne rendent pas un objet immuable. La seule façon de rendre immuable un objet potentiellement mutable est de détacher chaque partie qui pourrait être mutable, auquel cas l'objet n'est plus vraiment un proxy. – supercat

2

Votre sous-classe est incorrecte car elle enfreint le Liskov substitution principle. Ne fais pas ça.

+2

Non, ce n'est pas le cas. Les instances de 'Immutable' peuvent être remplacées par des instances de' Mutable' sans modifier le comportement du code. – emlai

+0

Je ne comprends pas pourquoi cette mauvaise réponse est toujours là, sans aucune modification. – uetoyo

+0

Le code qui nécessite un objet immuable devrait être mis à jour pour faire des copies défensives. sinon, de mauvaises choses pourraient se produire dans certaines situations. peut-être que ce n'est pas techniquement LSP. – les2

4

Je suggérerais que vous ayez une classe "ReadableFoo" de base héritable, une classe ImmutableFoo scellée dérivée et d'autres classes MutableFoo dérivées. Le code qui ne se soucie pas de savoir si un Foo est mutable ou non peut accepter un ReadableFoo. Code qui veut un Foo qui est garanti de ne pas changer peut accepter un ImmutableFoo. Code qui peut avoir besoin de changer un Foo peut accepter un MutableFoo.

Notez que les constructeurs pour ImmutableFoo et MutableFoo doivent généralement accepter un ReadableFoo. De cette façon, n'importe quel Foo sera convertible en une version mutable ou immuable.