あなたの一日がうまくいくことを願っています。
同じ継承ルートを持つ2つのオブジェクトの次の構成を作成します。
ウォレットクラスには、add
、getCards
、getPaymentCards
の3つの基本的な操作があります。 getCards
はCard
sとPaymentCard
sの両方を返す必要があります。これは私がこれらのカードをウォレットに保存する方法の問題に直面した場所です:
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;
}
}
これらのアイデアはすべて、完璧ではありません。これらのクラスを1つの屋根(ウォレット)で統一する理由は、上位のコードが識別(すべてのカードがアカウント識別子である)と支払い(すべてのカードが支払い可能ではなく、すべての支払いカードが識別子(カード)である)にカードを使用するためです。
そのような構成を作成するよりエレガントな方法はありますか?
すべてのカードを1つのSet<Card>
、次にgetPaymentCardsでタイプ別のフィルターを適用できます。
cards.stream()
.filter(card -> card instanceof PaymentCard)
.collect(Collectors.toSet());
この問題を完全に回避するには、問題を別の方法でモデル化する必要がありますが、これらの正確な要件を考えると、このソリューションはより適切な方法で処理できます。
より一般的なバージョンが必要な場合は、次のように実装できます。
public Set<Card> getCardsOfType(Class<? extends Card> type) {
return cards.stream()
.filter(type::isInstance)
.collect(Collectors.toSet());
}
編集: @Laivと@BenAaronsonのおかげで、ここでは実際のインスタンスの代わりにジェネリックスを使用してフィルターメソッドに渡します。以前のバージョンのコードの編集履歴を参照してください。
まあ、私はinstanceOf
を使うのは好きではありません。新しい種類のカードが発生したときに新しい特別なケースを追加する必要があるためです。また、あなたのWallet
クラスは結合で具体的な実装になります。
これが私の2セントです。
MyIDCard.class
などをメソッドに渡して、そのタイプのすべてのカードのサブセットを取得します。次に、消費者は必要に応じてカードを好きなタイプにキャストできます。 私はインターフェースを使用していますが、必要に応じて抽象クラスを使用することもできます。同じように機能します。
実際のアプリではファクトリであるはずの具体的なCard
実装に結合されているのはテストクラスだけであることに注意してください。
コードを表示(最後にテストメソッド):
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());
}
}
}
タイプによるフィルターが機能することを示す出力:
$ Java Test
MyBadge
MyBadge
MyBadge
ここではデフォルトのパッケージを使用していますが、クラスの正規名には次のようなパッケージの名前空間が含まれているため、名前の衝突はありません。
$ Java Test
com.test.cards.MyBadge
com.test.cards.MyBadge
com.test.cards.MyBadge
他の人々がそれらをすべて1つのSet
に含めると述べたように、絶対に2つの別個のセットが必要な場合は、何らかの理由でgetCards()
でセットの組み合わせを返すことができます。 。
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;
}