Vous devez soit utiliser une interface (pas d'héritage) ou une sorte de mécanisme de propriété (peut-être même quelque chose qui fonctionne comme le cadre de description des ressources).
Donc soit
interface Colored {
Color getColor();
}
class ColoredBook extends Book implements Colored {
...
}
ou
class PropertiesHolder {
<T> extends Property<?>> Optional<T> getProperty( Class<T> propertyClass ) { ... }
<V, T extends Property<V>> Optional<V> getPropertyValue( Class<T> propertyClass ) { ... }
}
interface Property<T> {
String getName();
T getValue();
}
class ColorProperty implements Property<Color> {
public Color getValue() { ... }
public String getName() { return "color"; }
}
class Book extends PropertiesHolder {
}
Clarification (modifier):
Ajouter simplement des champs facultatifs dans la classe d'entité
Surtout avec le wrapper facultatif (modifier: voir la réponse de 4castle ) Je pense que cela (en ajoutant des champs dans l'entité d'origine) est un moyen viable d'ajouter de nouvelles propriétés à petite échelle. Le plus gros problème avec cette approche est qu'elle pourrait fonctionner contre un faible couplage.
Imaginez que votre classe de livres soit définie dans un projet dédié à votre modèle de domaine. Vous ajoutez maintenant un autre projet qui utilise le modèle de domaine pour effectuer une tâche spéciale. Cette tâche nécessite une propriété supplémentaire dans la classe de livres. Soit vous vous retrouvez avec l'héritage (voir ci-dessous), soit vous devez changer le modèle de domaine commun pour rendre la nouvelle tâche possible. Dans ce dernier cas, vous pouvez vous retrouver avec un tas de projets qui dépendent tous de leurs propres propriétés ajoutées à la classe de livre tandis que la classe de livre elle-même dépend en quelque sorte de ces projets, car vous ne pouvez pas comprendre la classe de livre sans le projets supplémentaires.
Pourquoi l'héritage est-il problématique lorsqu'il s'agit de fournir des propriétés supplémentaires?
Quand je vois votre exemple de classe "Book", je pense à un objet de domaine qui finit souvent par avoir de nombreux champs et sous-types facultatifs. Imaginez simplement que vous souhaitez ajouter une propriété pour les livres qui incluent un CD. Il existe maintenant quatre types de livres: livres, livres avec couleur, livres avec CD, livres avec couleur et CD. Vous ne pouvez pas décrire cette situation avec héritage en Java.
Avec les interfaces, vous contournez ce problème. Vous pouvez facilement composer les propriétés d'une classe de livres spécifique avec des interfaces. La délégation et la composition vous permettront d'obtenir facilement la classe souhaitée. Avec l'héritage, vous vous retrouvez généralement avec des propriétés facultatives dans la classe qui ne sont là que parce qu'une classe soeur en a besoin.
Pour en savoir plus, pourquoi l'héritage est souvent une idée problématique:
Pourquoi l'héritage est-il généralement considéré comme une mauvaise chose par les partisans de la POO
JavaWorld: Pourquoi s'étendre est mal
Le problème avec l'implémentation d'un ensemble d'interfaces pour composer un ensemble de propriétés
Lorsque vous utilisez des interfaces pour l'extension, tout va bien tant que vous n'en avez qu'un petit ensemble. Surtout lorsque votre modèle d'objet est utilisé et étendu par d'autres développeurs, par exemple dans votre entreprise, la quantité d'interfaces augmentera. Et finalement, vous finissez par créer une nouvelle "propriété-interface" officielle qui ajoute une méthode que vos collègues ont déjà utilisée dans leur projet pour le client X pour un cas d'utilisation totalement indépendant - Ugh.
edit: Un autre aspect important est mentionné par gnasher729 . Vous souhaitez souvent ajouter dynamiquement des propriétés facultatives à un objet existant. Avec l'héritage ou les interfaces, vous devrez recréer l'objet entier avec une autre classe qui est loin d'être facultative.
Lorsque vous vous attendez à des extensions de votre modèle d'objet dans une telle mesure, vous serez mieux en modélisant explicitement la possibilité d' extension dynamique . Je propose quelque chose comme ci-dessus où chaque "extension" (dans ce cas la propriété) a sa propre classe. La classe fonctionne comme espace de noms et identifiant pour l'extension. Lorsque vous définissez les conventions de dénomination des packages en conséquence, cette méthode autorise une quantité infinie d'extensions sans polluer l'espace de noms pour les méthodes de la classe d'entité d'origine.
Dans le développement de jeux, vous rencontrez souvent des situations où vous souhaitez composer le comportement et les données dans de nombreuses variantes. C'est pourquoi le système entité-composant de modèle d'architecture est devenu très populaire dans les cercles de développement de jeux. C'est également une approche intéressante que vous voudrez peut-être examiner lorsque vous attendez de nombreuses extensions de votre modèle d'objet.