【问题标题】:How best to use Interfaces/Generics for this Card/Deck example如何最好地使用此 Card/Deck 示例的接口/泛型
【发布时间】:2015-11-03 10:52:44
【问题描述】:

我正在学习泛型/使用依赖注入/接口以便拥有松散耦合的类 - 但是,我正在努力。

对于以下简单的卡片/卡片组示例,我如何才能最好地调整这些类(使用上述技术),以便我可以传递任何类型的“卡片/ICard”列表,以便卡片组能够填充/打印说类型。因此,例如,在 Main 中,我希望能够传入 List 或 List,并且让 deck 能够以任何一种方式处理。

Deck.cs

public class Deck
{
    List<Card> cards;

    public Deck(List<Card> cards)
    {
        this.cards = cards;

        foreach (Card.Suit suit in Enum.GetValues(typeof(Card.Suit))) {
            foreach (Card.Val value in Enum.GetValues(typeof(Card.Val))) {
                cards.Add(new Card(suit, value));
            }
        }
    }


    public void printDeck()
    {
        foreach(Card x in cards)
        {
            Console.WriteLine(x.ToString());
        }
    }
}
}

卡片.cs

public class Card : ICard {
    Suit suit;
    Val value;

    public Card(Suit suit, Val value)
    {
        this.suit = suit;
        this.value = value;
    }

    public override string ToString()
    {
        string formatCard = "Suit: " + suit + " Value: " + value;
        return formatCard;
    }

    public enum Suit
    {
        HEARTS = 1,
        SPADES = 2,
        CLUBS = 3,
        DIAMONDS = 4

    }

    public enum Val
    {
        ONE = 1,
        TWO = 2,
        THREE = 3,
        FOUR = 4,
        FIVE = 5
    }
}  
}

SpecialCard.cs

public class SpecialCard : ICard{
    Suit suit;
    Val val;

    public SpecialCard(Suit suit, Val val)
    {
        this.suit = suit;
        this.val = val;
    }

    public override string ToString()
    {
        string formatCard = "Dragon: " + suit + " Value: " + val;
        return formatCard;
    }
    public enum Suit
    {
        RIDGEBACK = 1,
        FIREBALL = 2,
        SHORTSNOUT = 3,
        HORNTAIL = 4

    }

    public enum Val
    {
        UNO = 1,
        DOS = 2,
        TRES = 3,
        QUATTRO = 4,
        CINCZ = 5
    }
}   
}

ICard.cs

public interface ICard
{
    string ToString();
}

主要

static void Main()
    {
        Deck deck = new Deck(new List<Card>());
        deck.printDeck();
        Console.ReadKey();
    }

感谢您的任何指点! 元年

【问题讨论】:

  • ICard 理想情况下应该具有 Suit 和 Val 属性,因为它们定义了每张卡必须遵守的合同 (ICard)。 Suit 和 Val 枚举也可以在所有常量合并的类外部定义(来自 Card 和 SpecialCard)。那么你真的不需要 SpecialCard 类型。
  • 如果我合并常量,我将如何拥有不同类型的卡片(即一种具有普通花色,一种以虚构龙命名的花色..)并能够决定在主要?感谢您的帮助。

标签: c# generics interface dependency-injection


【解决方案1】:

我不会将西装和价值作为枚举,因为这使得很难替换不同的实现。一般来说,我会将持有状态的类(例如卡片或套牌)与提供逻辑的类(例如套牌构建器)分开。

我认为您不需要 ICard 中的 ToString(),因为所有类都继承 System.Object,它实现了 ToString()。无论如何,我认为卡片不应该知道它自己的长描述。为什么不让客户根据可用的信息(花色、价值)来决定。

鉴于这两种卡片的实现是相同的,我只需要一个逻辑很少的类。 Suit 和 value 现在可以只是一个字符串,因为您所做的只是打印值。如果您以后需要比较卡片的强度,您可能希望将 Value 更改为具有 int 值和字符串描述的类。

public class Card
{
    public string Suit { get; set; }
    public string Value { get; set; }
}

然后,您的套牌将是一张卡片列表和使用的花色类型,并将其自身的构建委托给其他地方以避免违反关注点分离。

public class Deck
{
    public IEnumerable<Card> Cards { get; set; }
    public string SuitName { get; set; }
}

那么您的大部分逻辑将存在于套牌构建器中。为了让客户知道要构建哪个套牌,我将创建一个枚举。

public enum DeckType
{
    Standard,
    Special
}

构建器将是(静态的,因为它没有状态)

public static class DeckBuilder
{
    public Deck Build(DeckType deckType)
    {
        var deck = new Deck();
        switch (deckType)
        {
            case DeckType.Standard:
                deck.SuitName = "Suit";
                deck.Cards = CreateStandardCards();
                break;
            case DeckType.Special:
                deck.SuitName = "Dragon";
                deck.Cards = CreateSpecialCards();
                break;
            default:
                throw new ArgumentException("deckType");
        }
    }

    private IEnumerable<Card> CreateStandardCards()
    {
        var suits = new List<string> { "Hearts", "Clubs", "Diamonds", "Spades"  };
        var values = new List<string> { "One", "Two", "Three", "Four", "Five" };
        return CreateCards(suits, values);
    }

    private IEnumerable<Card> CreateSpecialCards()
    {
        var suits = new List<string> { "Ridgeback", "Fireball", "Shortsnout", "Horntail"  };
        var values = new List<string> { "Uno", "Dos", "Tres", "Quattro", "Cincz" };
        return CreateCards(suits, values);
    }

    private IEnumerable<Card> CreateCards(IEnumerable<string> suits, IEnumerable<string> values)
    {
        foreach (var suit in suits)
        foreach (var value in values)
            yield return new Card { Suit = suit, Value = value };
    }
}

这种方法的替代方法是为 StandardDeckBuilder 和 SpecialDeckBuilder 创建一个具有不同实现的 IDeckBuilder,这意味着不需要枚举。如果 switch 语句变得更大,我会这样做(即,如果您添加了更多取决于卡组类型的属性)。

客户需要调用适当的构建器。我也会在这里移动 printDeck 逻辑。甲板类不应该知道如何打印。它不应该知道输出到控制台。

static void Main()
{
    var deck = DeckBuilder.Build(DeckType.Standard);
    foreach (var card in deck.Cards)
        Console.WriteLine(deck.SuitName + ": " + card.Suit + " Value: " + card.Value);
    Console.ReadKey();
}

我没有尝试过,所以它可能无法编译 - 这是我将使用的整体设计的说明。

【讨论】:

    猜你喜欢
    • 2021-02-07
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多