从 C# 中的构造函数封装子类

encapsulation of subclasses from a constructor in C#

如果问题不太清楚,我很抱歉,但我不知道 怎么表达呢

我正在尝试创建一个纸牌游戏,其中包含以下 classes 西装有红色、蓝色、绿色和黄色,具体取决于西装 该值是牌号和花色乘数的乘积

红色=1

蓝色=2

绿色 = 3

黄色=4

abstract class Card;

public class Deck
{
    private List<Card> deckList;
}

public class RedCard : Card, suit 
{
    private int number;

    public int Getvalue()
    {
        return number;
    }
}

interface suit
{
    int GetValue();
}

有没有办法封装Card subclasses Deck 构造函数不需要知道什么 种类的卡片可以添加到甲板上。 目的是确保 Deck class 不需要 如果我在将来添加另一个 suit/card subclass

将被更改

如果您从现实世界的角度考虑这一点,您会得到一个 Deck,其中包含 Card 的实例。牌组中卡片的属性或物理 属性 的类型都是相同的,它们都有一个 Suit 和一个 Number 并且在您的业务案例中它们都有一个 Value 。从结构的角度来看,它们是相同的,所有变化都是每个 属性 .

的值

如果每个 Card 具有相同的属性和相同的行为,则没有理由进一步创建 sub-classes 甚至这些 [=98= 的接口]卡片.

在软件设计中,我们使用继承和组合(接口)来为基础实现添加属性和行为;或改变现有的行为。从 Card 继承是一个 anti-pattern 只是为了更改属性的值,这样做可能会导致混淆。您确实需要将 structurecontent 的概念分开。如果结构和行为

除此之外,您还定义了一个 套装 的列表,并声明它们具有特定的整数值,在 C# 中,我们可以使用 [=24= 封装此类固定列表].

public enum Suit : int
{
    Red = 1,
    Blue = 2,
    Green = 3,
    Yellow = 4
}

public class Deck
{
    private List<Card> deckList;
}

public class Card
{
    public Suit Suit { get; private set; }
    public int Number { get; private set; }

    public Card (Suit suit, int number)
    {
        this.Suit = suit;
        this.Number = number;
    }
  
    public int Value { get { return (int)Suit * Number; } }
}

我们现在可以创建一个方法来根据一些固定的标准为我们生成一副纸牌,我将从这个演示的构造函数中调用它:

public class Deck
{
    private const int LENGTH_OF_SUIT = 10;
    private List<Card> deckList = new List<Card>();
    
    public Deck()
    {
        BuildDeck();
    }

    private void BuildDeck()
    {
        foreach (Suit suit in Enum.GetValues(typeof(Suit)))
        {
            for(int number = 1 ; number <= LENGTH_OF_SUIT; number ++)
            {
                deckList.Add(new Card(suit, number));
            }
        }
    }
}

这个简单的结构是封装原始post中列出的要求的一种方式,你可以在这里玩这个:https://dotnetfiddle.net/BnhGGG


如果花色的数量可以在运行时改变,那么枚举 不适合,那么你需要一个 class 来表示 Suit:

public class Suit
{
    public string Name { get;set; }
    public int Value { get;set; }
}

但请注意 Card class 无需更改太多:

public class Card
{
    public Suit Suit { get; private set; } 
    public int Number { get; private set; } 

    public Card (Suit suit, int number)
    {
        this.Suit = suit;
        this.Number = number;
    }

    public int Value { get { return Suit.Value * Number; } }
}

要构建套牌,我们需要其他信息,例如适合构建的内容:

public class Deck
{
    private const int LENGTH_OF_SUIT = 10;
    private List<Card> deckList = new List<Card>();
    
    public Deck(Suit[] suits)
    {
        BuildDeck(suits);
    }

    private void BuildDeck(Suit[] suits)
    {
        foreach (Suit suit in suits)
        {
            for(int number = 1 ; number <= LENGTH_OF_SUIT; number ++)
            {
                deckList.Add(new Card(suit, number));
            }
        }
    }
}

最后,如果我们需要获取特定 Suit 的所有 Cards,我们可以在 Deck

中添加一个为我们完成此操作的方法
public List<Card> GetCardsOfSuit(Suit suit)
{
    return deckList.Where(x => x.Suit == suit).ToList(); 
}

有许多其他方法可以实现相同或相似的逻辑,这只是一个例子。


您可能已经开始使用 Vehicle 进行学习,然后为 CarMotorCycle 创建了子 classes。通常的例子是 Vehicle 有一个 属性 对应 WheelsCar 有 4 个 WheelsMotorCycle 有 2.

单独使用该模型作为学习工具是有缺陷的,它会让您假设 subclassing 背后的 原因 是为了改变固定值属性。这个例子被过度简化了,从根本上说,还有其他属性和行为给了我们现实世界,以及概念上的原因class将这些对象class定义为它们自己的class定义。

这是一个设计的例子,因为在现实世界中 class汽车摩托车 分开是非常合乎逻辑的很容易理解它们都是Vehicles.

的类型

如果我们开始讲一个Vehicle颜色,那么我们更接近一个[=20=的Suit的概念].你可以有一个红色 Car 和一个红色 MotorCycle,但是颜色只是那个物理对象的属性之一,我们现在不创建一个新的 class 定义来表示 RedCarRedMotorCycle... Red 只是 ValueColor 属性.

更好的继承示例

具有 BirdFish 的子 class 的 Animal 示例可以更容易地显示相似点(继承的内容)和不同点在由 class 定义封装的 类型 属性和行为中:

我们可以对所有动物的眼睛进行计数,根据定义,某些类型的动物只有固定数量的眼睛。所以我们可以用眼睛来显示压倒一切的固定值。但是在 Animal 上使用 属性 来存储翅膀的数量是没有意义的,因为这是使动物成为鸟类的定义的一部分,而其他类型的动物根本不会有翅膀。我们不会存储一只鸟有任意数量的翅膀这一事实,因为根据定义所有的鸟都有 2 个翅膀。我们通常甚至不会费心在我们的模型中记录这个事实,因为它是一个常数值,不太可能对我们有任何用处。但是飞行是鸟类共有的行为,但并不是所有的鸟都能飞!现在我们可以开始谈论行为了。

在此模型中,我们将捕获 2 种类型的真实世界行为作为属性,我们不会在这些定义中专门添加任何 c# behaviours,但它是启用新的更好的工具开发人员将这些抽象的 C# 概念与现实世界联系起来。

public class Animal
{
    public string Name { get; set; }

    /// <summary>Number of Eyes</summary>
    /// <remarks>Not all animals have eyes, use 0 to represent no eyes</remarks>
    public virtual int Eyes { get; set; };

    public string override ToString()
    {
        return $"{Name} - Eyes:{Eyes}";
    }
}

/// <summary>Vertebrates have a spine, but are also `Chordates`, they have "Camera Eyes" that are specifically 2 eyes using lenses to focus an image.</summary>
/// <remarks>http://www.madsci.org/posts/archives/1999-02/920061344.Ev.r.html#:~:text=This%20is%20the%20same%20process%20at%20work%20in,they%20retained%20the%20trait%20from%20a%20common%20ancestor.</remarks>
public class Vertebrate : Animal
{
    public override sealed int Eyes { get { return 2; } set{/*Force only 2 eyes, ignore setter*/} }
}

public class Bird : Vertebrate 
{
    /// <summary>Not all birds can fly: penguins, emus and ostriches are some examples </summary> 
    public bool CanFly { get;set; }

    public override string ToString()
    {
        return base.ToString() + $", CanFly :{CanFly}";
    }
}

public class Fish : Vertebrate 
{
    /// <summary>Fun fact, not all fish can swim backwards! Sharks is one example</summary>
    public bool CanSwimBackwards { get;set; }

    public override string ToString()
    {
        return base.ToString() + $", CanSwimBackwards :{CanSwimBackwards}";
    }
}

我们在这里展示的是一个简单的继承模型,它显示了不同的 属性 被添加到 base class我们可以使用这些来添加一些 Animals 到名单:

List<Animal> myFavouriteAnimals = new List<Animal>();
myFavouriteAnimals.Add(new Animal { Name = "Worm", Eyes = 0 });
myFavouriteAnimals.Add(new Bird { Name = "Hawk", CanFly = true; });
myFavouriteAnimals.Add(new Bird { Name = "Penguin", CanFly = false; });
myFavouriteAnimals.Add(new Fish { Name = "Eel", CanSwimBackwards = true; });
myFavouriteAnimals.Add(new Fish { Name = "Shark", CanSwimBackwards = false; });

foreach(var animal in myFavouriteAnimals)
{
    Console.WriteLine(animal.ToString());
}

这将产生以下结果:
在这里试试: https://dotnetfiddle.net/n6jgHO

Worm - Eyes:0
Hawk - Eyes:2, CanFly: True
Penguin - Eyes:2, CanFly: False
Eel - Eyes:2, CanSwimBackwards: True
Shark - Eyes:2, CanSwimBackwards: False

该示例中有一些 syntactic sugar,但希望它有助于解释使用继承的更好方案,而不仅仅是更改基 class 中定义的属性的值。