2013-06-19 3 views
2

J'ai ces classes:Java stratégie de conception dominante de méthode abstraite

abstract class Person{void toDrink(Liquid l);} 

class Liquid{} 
class Alcohol extend Liquid{} 
class Milk extends Liquid{} 


class Adult extends Person{void toDrink(Liquid l){}} 
class Child extends Person{void toDrink(Liquid l){}} 

Je veux des enfants et des adultes de ne pas prendre liquide args mais le lait et l'alcool respectivement, mais je dois passer outre méthode abstraite avec l'argument exactement liquide. Donc, quand j'écris

Person child = new Child(); 
child.toDrink(new Alcohol()); 

enfant peut prendre l'alcool comme argument à boire. Je peux résoudre ce problème en utilisant Exceptions ou if(), mais je vous demande de me conseiller un meilleur design pour ces relations. Je le veux quand je passe un mauvais argument à la méthode toDrink() (alcool à l'enfant ou du lait à l'adulte) mon IDE le souligne comme une erreur et une erreur de compilation quand j'essaie de l'exécuter.

+2

un peu hors sujet mais: si 'toString()' transforme un objet en String, que fait 'toDrink()'? –

+0

Première: bienvenue sur StackOverflow. Pour votre question: si vous voulez que l'IDE gère cette abstraction n'est pas nécessaire car aucune extension ne sera possible. Vous n'utilisez que 'abstract class' si vous ne savez rien à ce sujet au moment de la compilation. –

+0

vous pouvez utiliser instanceof ici pour vérifier à quelle instance il pointe réellement. – tejas

Répondre

1

Vous pouvez créer comme ci-dessous?

abstract class Person<T extends Liquid>{ 
    void toDrink(T l) { 

    } 
} 

Dans le code ci-dessus, vous liez la méthode toDrink() d'accepter un type qui s'étend dans le code ci-dessus Liquid

class Adult<T extends Alcohol> extends Person<T>{ 

    void toDrink(T l){ 

    } 
} 

class Child<T extends Milk> extends Person<T>{ 
    void toDrink(T l){ 

    } 
} 

, vous liez le toDrink() de Adult et Child méthode pour accepter une type qui s'étend Alcohol et Milk respectivement.

Maintenant, selon vous attend ci-dessous le code échoue (avec erreur de compilation)

Person<Milk> child = new Child<Milk>(); 
    child.toDrink(new Alcohol()); // error here 

mais celui-ci fonctionne

child.toDrink(new Milk()); 
+0

Merci! Mais je dois écrire explicitement le type de boisson acceptable lors de la création de la personne. Y a-t-il un moyen de l'éviter? – ka4eli

1

vous pouvez utiliser les médicaments génériques pour résoudre ce:

i serait suggérer de créer une classe entre Liquid et Milk:

public abstract class NonAlcoholicLiquid extends Liquid {} 
public class Milk extends NonAlcoholicLiquid {} 

et modifier vos classes Personne respectivly

public abstract class Person<Drink extends Liquid> { 
    void doDrink(Drink drink) { 

    } 
} 


public class Adult<Drink extends Liquid> extends Person<Drink> { 
    void doDrink(Drink drink){ 

    } 
} 

public class Child<Drink extends NonAlcoholicLiquid> extends Person<Drink> { 
    void doDrink(Drink drink){ 

    } 
} 

Ainsi, les enfants ne peuvent boire des liquides non alcoolisées alors que les adultes sont toujours autorisés à boire du lait;)

Mise à jour:

Selon votre commenter nous pouvons sauter des génériques pour le moment:

public abstract class Person { 
    public abstract void doDrink(Liquid drink); 
} 


public class Adult extends Person { 
    void doDrink(Liquid drink) { 
     if(!(drink instanceof Alcohol)) { 
      throw new InvalidDrinkException("Adults may only drink Alcohol"); 
     } 
     // do the adult's drinking stuff 
    } 
} 

public class Child extends Person { 
    void doDrink(Liquid drink){ 
     if(!(drink instanceof Milk)) { 
      throw new InvalidDrinkException("Children may only drink Milk"); 
     } 
     // do the child's drinking stuff 
    } 
} 

Mais votre approche a une faille dans la pensée: Quand un enfant grandit l'instance est la même mais la classe devrait changer ...

+0

Merci! Mais les adultes ne sont pas autorisés à boire du lait)) Et le vrai problème est que lorsque nous instancions l'Enfant à travers la référence à la Personne, nous pourrions appliquer à cette méthode la méthode à l'alcool. Personne enfant = new Child(); child.toDrink (new Alcohol) - n'est pas une erreur de compilation, mais ClassCasstException. Et nous pourrions avoir à travailler avec des objets via leurs références de super-classe. – ka4eli

+0

@ ka4eli mis à jour ma réponse –

+0

Oui, comme je l'ai mentionné dans ma question, nous pouvons résoudre ce problème en utilisant "si" et des exceptions. Je pense que ce n'est pas le meilleur design donc j'ai posté cette question. Mais peut-être il n'y a pas de réponses appropriées, bien que vos variantes avec des génériques sont plutôt bonnes. Je vous remercie! – ka4eli

0

I want Child and Adult not to take Liquid as args but Milk and Alcohol respectively signifie les boissons pour adultes seulement l'alcool et l'enfant seulement le lait. Vous pouvez donc utiliser des génériques avec le type de liquide que chaque sous-type de personne boit comme paramètre de type.

abstract class Person<L extends Liquid> { 
void toDrink(L l) { 
    //implementation here. 
} 
} 

class Liquid{} 
class Alcohol extend Liquid{} 
class Milk extends Liquid{} 

class Adult extends Person<Alcohol>{ 
    // You don't necessarily need to override the toDrink method. 
} 

class Child extends Person<Milk>{ } 

Vous n'avez pas besoin de passer outre la méthode toDrink() dans chaque sous-classe. Vous ne pouvez avoir qu'une seule implémentation dans la classe Person abstraite.

+0

Cela ne fonctionne pas si nous écrivons: Personne p = new Child(); p.toDrink (nouvel alcool()); Cela ne fonctionne que lorsque nous créons une référence de type Enfant – ka4eli

+0

@ ka4eli Si vous voulez déclarer 'p' comme' Person', vous ne pouvez pas vous assurer que sa méthode 'toDrink (l)' prendra l'argument correct lors de la compilation . La raison en est que le compilateur ne sait pas quel type de 'Person'' p' est. – nakosspy

+0

@ ka4eli Vous pouvez ajouter une vérification de type de l'argument dans l'implémentation des méthodes 'toDrink (l)' de 'Adult' et' Person'. Mais alors vous devriez lancer une exception RuntimeException si 'l' était du mauvais type. Vous obtenez le même résultat (une ClassCastException) avec mon approche. La différence est que vous n'avez pas à écrire le code de vérification de type. – nakosspy

Questions connexes