1

私には 3 つのクラスがあります。インターフェイス IProduct を実装する切手、手紙、および小包は、独自の機能もいくつか備えています。

public interface IProduct
{
    string Name { get; }
    int Quantity { get; set; }
    float Amount { get; }
}

public class Stamp : IProduct
{
    public string Name { get { return "Stamp"; } }
    public int Quantity { get; set; }
    public float Amount { get; set; }
    public float UnitPrice { get; set; }
}

public class Letter : IProduct
{
    public string Name { get { return "Letter"; } }
    public int Quantity { get; set; }        
    public float Amount { get; set; }
    public float Weight { get; set; }
    public string Destination { get; set; }
}

public class Parcel : IProduct
{
    public string Name { get { return "Parcel"; } }
    public int Quantity { get; set; }        
    public float Amount { get; set; }
    public float Weight { get; set; }
    public string Destination { get; set; }
    public int Size { get; set; }
}

public static class ShoppingCart
{
    private static List<IProduct> products = new List<IProduct>();
    public static List<IProduct> Items { get { return products; } }
}

から派生クラスの追加メンバーにアクセスできないのはなぜList<IProduct>ですか?

ShoppingCart.Items.Add(new Stamp { Quantity = 5, UnitPrice = 10, Amount = 50 });
ShoppingCart.Items.Add(new Letter { Destination = "US", Quantity = 1, Weight = 3.5f });
ShoppingCart.Items.Add(new Parcel { Destination = "UK", Quantity = 3, Weight = 4.2f, Size = 5 });

foreach (IProduct product in ShoppingCart.Items)
{
    Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}", product.Name, product.Quantity, product.Amount);
}

ジェネリックを使用することも考えましたが、その場合、製品の特定のタイプごとに個別のコードを作成する必要があります。

public static class ShoppingCart<T> where T : IProduct
{
    private static List<T> items = new List<T>();
    public static List<T> Items { get { return items; } }
}


ShoppingCart<Stamp>.Items.Add(new Stamp { Quantity = 5, Amount = 10, UnitPrice = 50 });
ShoppingCart<Letter>.Items.Add(new Letter { Destination = "US", Quantity = 1, Weight = 3.5f });

foreach (Stamp s in ShoppingCart<Stamp>.Items)
{
    Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}", s.Name, s.Quantity, s.Amount); 
}

foreach (Letter l in ShoppingCart<Letter>.Items)
{
    Console.WriteLine("Name: {0}, Destination: {1}, Weight: {2}", l.Name, l.Destination, l.Weight);      
}

この種の問題に対する設計パターンはありませんか。工場のパターン?

4

4 に答える 4

3

IProductアイテムのリストでのみ公開しているため、インターフェイスを実装するクラスの追加メンバーにアクセスすることはできません。ショッピング カート内の各アイテムの特定のリスト タイプを ShoppingCart クラスに追加すると、カート内のすべての製品のシーケンスを、IProduct インターフェイスのみを使用する必要があるものに公開できます。

public class ShoppingCart
{
    public IList<Stamp> Stamps { get; }
    public IList<Letter> Letters { get; }
    public IList<Parcel> Parcels { get; }

    public IEnumerable<IProduct> Products
    {
        get
        {
            return this.Stamps.Cast<IProduct>()
                .Concat(this.Letters.Cast<IProduct>())
                .Concat(this.Parcels.Cast<IProduct>());
        }
    }
}
于 2009-11-23T13:11:53.767 に答える
1

これは、ショッピング カート内の各アイテムを foreach ループで IProduct としてキャストしているためです。あなたがする必要があるのは次のようなものです:

foreach(IProduct product in ShoppingCart.Items)
{
    if (product is Stamp)
    {
        var stamp = product as Stamp;
        Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}, UnitPrice: {3}", stamp.Name, stamp.Quantity, stamp.Amount, stamp.UnitPrice);
    }
    else if (product is Letter)
    {
        var letter = product as Letter;
        Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}, Weight: {3}, Destination: {4}", letter.Name, letter.Quantity, letter.Amount, letter.Weight, letter.Destination);
    }
    else if (product is Parcel)
    {
        var parcel = product as Parcel;
        Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}, Weight: {3}, Destination: {4}, Size: {5}", parcel.Name, parcel.Quantity, parcel.Amount, parcel.Weight, parcel.Destination, parcel.Size);
    }
}

isまたは、演算子と変数宣言を組み合わせた、より現代的な構文が C# で利用できるようになりました。

foreach(IProduct product in ShoppingCart.Items)
{
    if (product is Stamp stamp)
    {
        Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}, UnitPrice: {3}", stamp.Name, stamp.Quantity, stamp.Amount, stamp.UnitPrice);
    }
    else if (product is Letter letter)
    {
        Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}, Weight: {3}, Destination: {4}", letter.Name, letter.Quantity, letter.Amount, letter.Weight, letter.Destination);
    }
    else if (product is Parcel parcel)
    {
        Console.WriteLine("Name: {0}, Quantity: {1}, Amount: {2}, Weight: {3}, Destination: {4}, Size: {5}", parcel.Name, parcel.Quantity, parcel.Amount, parcel.Weight, parcel.Destination, parcel.Size);
    }
}

また、不要なプロパティName、Quantity および Amountを繰り返しています。Product から各クラスを派生させる必要があります。

public class Stamp: Product, IProduct
{
    public double UnitPrice { get; set; }
}

public class TransitProduct: Product, IProduct
{
    public double Weight { get; set; }
    public string Destination { get; set; }   
}

public class Letter: TransitProduct, IProduct
{
}

public class Parcel: TransitProduct, IProduct
{
    public double Size { get; set; }
}
于 2009-11-23T13:17:38.343 に答える
1

派生クラスから追加のメンバーにアクセスできない理由は、List<> でインターフェイスを使用しているためです。したがって、そのインターフェイスのプロパティにしかアクセスできません。

役立つ可能性のあるパターンは、二重発送パターンです。

以下の例:

public interface IHandler
{
    void Handle(Stamp stamp);
    void Handle(Letter letter);
    ...
}

public class Handler : IHandler
{
    public void Handle(Stamp stamp)
    {
       // do some specific thing here...
    }
    public void Handle(Letter letter)
    {
       // do some specific thing here...
    }
    ...
}

public interface IProduct
{
    string Name { get; }
    int Quantity { get; set; }
    float Amount { get; }
    void Handle(IHandler handler);
}

public class Stamp : IProduct
{
    public string Name { get { return "Stamp"; } }
    public int Quantity { get; set; }
    public float Amount { get; set; }
    public float UnitPrice { get; set; }
    public void Handle(IHandler handler)
    {
         handler.Handle(this);
    }
}

これで、ハンドラーでいくつかの特定の機能をプログラムできます。数量 * 単価または重量と宛先のルックアップ テーブルなどを指定して、ある種の合計価格を計算したいと考えています...

于 2009-11-23T13:08:29.437 に答える
1

から派生クラスの追加メンバーにアクセスできないのはなぜ List<IProduct>ですか?

などの派生クラスのプロパティをインターフェイスIProductが認識していないためです。UnitPriceDestination

Amount派生クラス オブジェクト Stamp、Letter、Parcel のそれぞれに計算するためのインテリジェンスを追加しようとしていますか?

次に、少し再設計して、Decorator デザイン パターンを使用する必要があると思います。

DerivedClass::Amount()
{
  Base::Amount() + 
  //Amount logic based on derived class
}
于 2009-11-23T13:10:50.977 に答える