Composizione di due classi con radice ereditaria comune

7

Spero che la tua giornata stia andando bene.

Voglio creare la seguente composizione di due oggetti con la stessa radice di ereditarietà:

Cisaranno3operazionidibasesullaclasseWallet:add,getCardsegetPaymentCards.getCardsdovrebberestituiresiaCardsePaymentCards.QuestoèunluogoincuihodovutoaffrontareunproblemasucomememorizzarequestecartenelPortafoglio:

class Card {}
class BadgeCard extends Card {}
class IdCard extends Card {}

class PaymentCard extends Card {}

class WalletT21 {
    Set<Card> cards;
    Set<PaymentCard> paymentCards;

    void add(Card c) {
        cards.add(c);
        Integer i = 0;
        i = 1;
        i = 1;
        System.out.println(i);
    }

    void add(PaymentCard c) {
        paymentCards.add(c);
    }

    Set<Card> cards() {
        return Stream.concat(cards.stream(), paymentCards.stream())
                     .collect(Collectors.<Card>toSet());
    }

    Set<PaymentCard> paymentCards() {
        return paymentCards;
    }
}
class WalletT22 {
    Set<Card> cards;
    Set<PaymentCard> paymentCards;

    void add(Card c) {
        cards.add(c);
    }

    void add(PaymentCard c) {
        cards.add(c);
        paymentCards.add(c);
    }

    Set<Card> cards() {
        return cards;
    }

    Set<PaymentCard> paymentCards() {
        return paymentCards;
    }
}

Tutte queste idee sono, diciamo, non perfette. Il motivo per unire queste classi sotto lo stesso tetto (Portafoglio) è che il codice più alto utilizza carte per l'identificazione (ogni carta è un identificativo del conto) e pagamenti (non tutte le carte sono pagabili ma ogni carta di pagamento è un identificativo (Carta)). / p>

C'è un modo più elegante per creare una composizione del genere?

    
posta ovnia 29.11.2016 - 10:41
fonte

3 risposte

4

Tutte le carte possono essere memorizzate in un singolo Set<Card> , e in getPaymentCards si può applicare un filtro per tipo:

cards.stream()
    .filter(card -> card instanceof PaymentCard)
    .collect(Collectors.toSet());

Il problema dovrebbe probabilmente essere modellato in modo diverso per evitare del tutto questo problema, ma dati questi precisi requisiti questa soluzione potrebbe essere un modo più semplice per gestirlo.

Se fosse necessaria una versione più generica, questa potrebbe essere implementata in questo modo:

public Set<Card> getCardsOfType(Class<? extends Card> type) {
    return cards.stream()
            .filter(type::isInstance)
            .collect(Collectors.toSet());
}
    
risposta data 29.11.2016 - 11:58
fonte
2

Edit: Thanks to @Laiv and @BenAaronson, here I use generics instead of an actual instance to pass to the filter method. See edit history for the previous version of the code.

Bene, non mi piace usare instanceOf perché se fosse necessario aggiungere nuovi casi speciali quando sorgono nuovi tipi di schede . Anche la tua classe Wallet è accoppiata alle implementazioni concrete.

Quindi ecco i miei due centesimi:

getCardsByType (Class & lt ;? estende Card > cardType)

Passi qualcosa come MyIDCard.class al metodo per ottenere un sottoinsieme di tutte le carte di quel tipo. Il consumatore può quindi lanciare le carte al tipo che desidera, se necessario. Uso le interfacce ma potresti utilizzare le classi astratte se lo desideri, funzionerebbe allo stesso modo.

Nota che solo la classe di test è accoppiata a qualsiasi implementazione di Card concreta, che in una vera app dovrebbe essere la fabbrica.

Mostrami il codice (metodo di prova alla fine):

import java.util.Set;
import java.util.HashSet;
import java.util.Set;

interface Card {}
interface Badge extends Card {}
interface IDCard extends Card {}
interface PaymenCard extends Card {}
class MyIDCard implements IDCard {}
class MyBadge implements Badge {}

interface Wallet {
    public void addCard(Card card);
    public Set<Card> getCards();
    public Set<Card> getCardsByType(Class<? extends Card> cardType);
}

class MyWallet implements Wallet {
    Set<Card> cards = new HashSet<Card>();
    @Override
    public void addCard(Card card) { this.cards.add(card);}
    @Override
    public Set<Card> getCards() { return this.cards;}
    @Override
    public Set<Card> getCardsByType(Class<? extends Card> cardType) {
        Set<Card> subSet = new HashSet<Card>();         
        for (Card card: this.cards){
            if (card.getClass() == cardType){
                subSet.add(card);   
            }           
        }
        return subSet;
    }
}
    public class Test {    
        public static void main (String[] args){
            Card c1 = new MyIDCard();
            Card c2 = new MyBadge();
            Card c3 = new MyBadge();
            Card c4 = new MyBadge();
            Card c5 = new MyIDCard();
            Wallet myWallet = new MyWallet();
            myWallet.addCard(c1);
            myWallet.addCard(c2);
            myWallet.addCard(c3);
            myWallet.addCard(c4);
            myWallet.addCard(c5);
            //now my wallet has two IDs and a Badge
            //
            // I can ask for a list of cards "like this one"
            Set<Card> badges = myWallet.getCardsByType(MyBadge.class); 

            for (Card card: badges){
                System.out.println(card.getClass().getCanonicalName());
            }       
        }
    }

Output che mostra che il filtro in base al tipo funziona:

$ java Test 
MyBadge
MyBadge
MyBadge

Qui utilizzo un pacchetto predefinito, ma non è possibile la colocazione del nome poiché il nome canonico delle classi contiene lo spazio dei nomi del pacchetto in questo modo:

$ java Test 
com.test.cards.MyBadge
com.test.cards.MyBadge
com.test.cards.MyBadge
    
risposta data 29.11.2016 - 13:37
fonte
1

Come altre persone hanno detto di averli tutti in un unico Set è l'opzione migliore, tuttavia se devi assolutamente avere 2 Set separati per qualsiasi motivo puoi semplicemente avere getCards() restituire una combinazione dei Set.

void add(Card c) {
    cards.add(c);
}

void add(PaymentCard c) {
    paymentCards.add(c);
}

Set<Card> cards() {
    Set<Card> combinedSet = new HashSet<>();
    combinedSet.addAll(cards);
    combinedSet.addAll(paymentCards);
    return combinedSet;
}

Set<PaymentCard> paymentCards() {
    return paymentCards;
}
    
risposta data 29.11.2016 - 20:39
fonte

Leggi altre domande sui tag