Pergunta

Eu tenho três aulas; Carimbo, carta e parcela que implementam um iProduct de interface e eles também têm parte de sua própria funcionalidade.

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; } }
}

Por que não posso acessar os membros adicionais de classes derivadas de um 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);
}

Pensei em usar genéricos, mas nesse caso terei que escrever código separado para cada tipo específico de produto.

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);      
}

Não existe nenhum tipo de padrão de design para esse tipo de problema. Padrão de fábrica?

Foi útil?

Solução

Isso ocorre porque você está lançando cada item no carrinho de compras como iproduto em seu loop foreach. O que você precisaria fazer é algo como:

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);
    }
}

Também você está repetindo propriedades desnecessárias Nome, quantidade e quantidade. Você deve derivar cada uma de suas aulas do produto:

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; }
}

Outras dicas

Você não pode acessar os membros adicionais das aulas que implementam uma interface porque você está expondo apenas IProduct na lista de itens. Eu adicionaria tipos de lista específicos para cada item no carrinho de compras à aula de carrinho de compras e, em seguida, você pode expor uma sequência de todos os produtos no carrinho para qualquer coisa que só precisa usar a interface do 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>());
        }
    }
}

Por que não posso acessar os membros adicionais de classes derivadas de um List<IProduct> ?

Isso é porque, IProduct A interface não sabe sobre UnitPrice, Destination etc das propriedades de classe derivadas.

Você está tentando adicionar a inteligência para calcular o Amount Para cada um dos objetos de classe derivada selo, letra, parcela?

Então, eu diria que você precisa redesenhar um pouco e usar o Padrão de design do decorador.

DerivedClass::Amount()
{
  Base::Amount() + 
  //Amount logic based on derived class
}

O motivo pelo qual você não pode acessar membros adicionais de uma classe derivada é que você está usando a interface na lista <> - portanto, você só poderá acessar propriedades nessa interface.

Um padrão que pode ajudá-lo é o padrão duplo-despacho.

Exemplo abaixo:

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);
    }
}

Agora você pode programar alguma funcionalidade específica no manipulador - acho que deseja calcular algum tipo de preço total, dada coisas como quantidade * preço unitário ou uma tabela de pesquisa de peso e destino ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top