C #: Encapsulamento de por exemplo coleções
-
22-07-2019 - |
Pergunta
Eu estou querendo saber qual destes seria considerado o mais limpo ou melhor usar e por quê.
Um deles expõe a uma lista de passageiros, que permitem que o suplemento de usuário e remover etc. Os outros couros da lista e só permitir que o usuário enumerá-los e adicionar usando um método especial.
Exemplo 1
class Bus
{
public IEnumerable<Person> Passengers { get { return passengers; } }
private List<Passengers> passengers;
public Bus()
{
passengers = new List<Passenger>();
}
public void AddPassenger(Passenger passenger)
{
passengers.Add(passenger);
}
}
var bus = new Bus1();
bus.AddPassenger(new Passenger());
foreach(var passenger in bus.Passengers)
Console.WriteLine(passenger);
Exemplo 2
class Bus
{
public List<Person> Passengers { get; private set; }
public Bus()
{
Passengers = new List<Passenger>();
}
}
var bus = new Bus();
bus.Passengers.Add(new Passenger());
foreach(var passenger in bus.Passengers)
Console.WriteLine(passenger);
A primeira classe Eu diria que é melhor encapsulado. E, neste caso exato, que pode ser a melhor abordagem (desde que você provavelmente deve se certificar de esquerda no ônibus-lo do espaço, etc.). Mas eu acho que pode haver casos em que a segunda classe podem ser úteis também? Como se a classe não se importa o que acontece com essa lista, desde que ele tem um. O que você acha?
Solução
No exemplo um, é possível transformar sua coleção.
Considere o seguinte:
var passengers = (List<Passenger>)bus.Passengers;
// Now I have control of the list!
passengers.Add(...);
passengers.Remove(...);
Para corrigir isso, você pode considerar algo como isto:
class Bus
{
private List<Passenger> passengers;
// Never expose the original collection
public IEnumerable<Passenger> Passengers
{
get { return passengers.Select(p => p); }
}
// Or expose the original collection as read only
public ReadOnlyCollection<Passenger> ReadOnlyPassengers
{
get { return passengers.AsReadOnly(); }
}
public void AddPassenger(Passenger passenger)
{
passengers.Add(passenger);
}
}
Outras dicas
Na maioria dos casos eu consideraria exemplo 2 para ser aceitável, desde que o tipo subjacente era extensível e / ou exposto alguma forma de onAdded / onRemoved eventos para que sua classe interna pode responder a quaisquer alterações à coleção.
Neste Lista caso
(Você também tem que estar ciente de que os usuários da classe pode modificar os itens na lista / coleção sem a classe pai que sabe sobre ele, para se certificar de que você não contar com os itens que estão sendo inalterada - a menos que eles são imutáveis, obviamente -. ou você pode fornecer eventos estilo OnChanged se você precisa)
Executar seus respectivos exemplos através FxCop e que deve dar-lhe uma dica sobre os riscos de expor List<T>
Eu diria que tudo se resume a sua situação. Eu normalmente ir para a opção 2, uma vez que é o mais simples, menos você tem uma razão de negócios para adicionar controles mais rígidos a ele.
A opção 2 é a mais simples, mas que permite que outras classes para adicionar / elementos Remover para a coleção, que pode ser perigoso.
Eu acho que uma boa heurística é considerar o que os métodos de mensagens publicitárias fazer. Se o seu AddPassenger (ou remover, ou outros) método é simplesmente retransmitindo a chamada para a coleção, em seguida, eu iria para a versão mais simples. Se você tem que verificar os elementos antes inseri-los, em seguida, a opção 1 é basicamente inevitável. Se você tem que manter o controle dos elementos inseridos / excluídos, você pode ir de qualquer maneira. Com a opção 2 você tem que registrar eventos na coleção para obter notificações, e com a opção 1 você tem que criar wrappers para cada operação na lista que você deseja usar (por exemplo, se você quiser Inserir, bem como adicionar), então eu acho ele depende.