Eu acho que a maneira mais comum de adicionar algo a uma coleção é usar algum tipo de Add
método que uma coleção fornece:
class Item {}
var items = new List<Item>();
items.Add(new Item());
e na verdade não há nada incomum nisso.
Eu me pergunto, no entanto, por que não fazemos dessa maneira:
var item = new Item();
item.AddTo(items);
parece ser de alguma forma mais natural que o primeiro método. Isso teria a vantagem de que quando a Item
classe tem uma propriedade como Parent
:
class Item
{
public object Parent { get; private set; }
}
você pode tornar o setter privado. Nesse caso, é claro que você não pode usar um método de extensão.
Mas talvez eu esteja errado e nunca vi esse padrão antes porque é tão incomum? Você sabe se existe esse padrão?
Em C#
um método de extensão, seria útil para isso
public static T AddTo(this T item, IList<T> list)
{
list.Add(item);
return item;
}
E outras línguas? Eu acho que na maioria deles a Item
classe tinha que fornecer uma ICollectionItem
interface , vamos chamá-lo .
Atualização-1
Estive pensando um pouco mais e esse padrão seria realmente útil, por exemplo, se você não quiser que um item seja adicionado a várias coleções.
ICollectable
interface de teste :
interface ICollectable<T>
{
// Gets a value indicating whether the item can be in multiple collections.
bool CanBeInMultipleCollections { get; }
// Gets a list of item's owners.
List<ICollection<T>> Owners { get; }
// Adds the item to a collection.
ICollectable<T> AddTo(ICollection<T> collection);
// Removes the item from a collection.
ICollectable<T> RemoveFrom(ICollection<T> collection);
// Checks if the item is in a collection.
bool IsIn(ICollection<T> collection);
}
e uma implementação de exemplo:
class NodeList : List<NodeList>, ICollectable<NodeList>
{
#region ICollectable implementation.
List<ICollection<NodeList>> owners = new List<ICollection<NodeList>>();
public bool CanBeInMultipleCollections
{
get { return false; }
}
public ICollectable<NodeList> AddTo(ICollection<NodeList> collection)
{
if (IsIn(collection))
{
throw new InvalidOperationException("Item already added.");
}
if (!CanBeInMultipleCollections)
{
bool isInAnotherCollection = owners.Count > 0;
if (isInAnotherCollection)
{
throw new InvalidOperationException("Item is already in another collection.");
}
}
collection.Add(this);
owners.Add(collection);
return this;
}
public ICollectable<NodeList> RemoveFrom(ICollection<NodeList> collection)
{
owners.Remove(collection);
collection.Remove(this);
return this;
}
public List<ICollection<NodeList>> Owners
{
get { return owners; }
}
public bool IsIn(ICollection<NodeList> collection)
{
return collection.Contains(this);
}
#endregion
}
uso:
var rootNodeList1 = new NodeList();
var rootNodeList2 = new NodeList();
var subNodeList4 = new NodeList().AddTo(rootNodeList1);
// Let's move it to the other root node:
subNodeList4.RemoveFrom(rootNodeList1).AddTo(rootNodeList2);
// Let's try to add it to the first root node again...
// and it will throw an exception because it can be in only one collection at the same time.
subNodeList4.AddTo(rootNodeList1);
fonte
item.AddTo(items)
suponha que você tenha um idioma sem métodos de extensão: natural ou não, para oferecer suporte a addTo, todo tipo precisaria desse método e o forneceria para todo tipo de coleção que suporta anexos. Esse é o melhor exemplo de introdução de dependências entre tudo o que eu já ouvi: P - Acho que a falsa premissa aqui é tentar modelar alguma abstração de programação para a vida 'real'. Isso costuma dar errado.add(item, collection)
, mas isso não é bom estilo OO.Respostas:
Não,
item.AddTo(items)
não é mais natural. Eu acho que você mistura isso com o seguinte:Você está certo, pois
items.Add(item)
não está muito próximo do idioma natural inglês. Mas você também não ouveitem.AddTo(items)
no inglês natural, não é? Normalmente, há alguém que deveria adicionar o item à lista. Seja quando estiver trabalhando em um supermercado ou enquanto estiver cozinhando e adicionando ingredientes.No caso das linguagens de programação, fizemos isso para que uma lista faça as duas coisas: armazenando seus itens e sendo responsável por adicioná-los a si mesmo.
O problema com sua abordagem é que um item precisa estar ciente de que existe uma lista. Mas um item poderia existir mesmo que não houvesse listas, certo? Isso mostra que ele não deve estar ciente das listas. As listas não devem ocorrer no seu código de forma alguma.
No entanto, as listas não existem sem itens (pelo menos seriam inúteis). Portanto, tudo bem se eles souberem de seus itens - pelo menos de uma forma genérica.
fonte
A @valenterry está certa sobre o problema da separação de preocupações. As classes não devem saber nada sobre as várias coleções que podem contê-las. Você não gostaria de alterar a classe do item sempre que criar um novo tipo de coleção.
Dito isto...
1. Não é Java
O Java não ajuda em nada aqui, mas algumas linguagens têm uma sintaxe mais flexível e extensível.
No Scala, items.add (item) fica assim:
Onde :: é um operador que adiciona itens às listas. Parece muito o que você quer. Na verdade, é açúcar sintático para
onde :: é um método de lista Scala que é efetivamente equivalente ao list.add do Java . Portanto, embora pareça na primeira versão como se o item estivesse se adicionando aos itens , na verdade os itens estão fazendo a adição. Ambas as versões são Scala válidas
Esse truque é realizado em duas etapas:
Se você não se sente confortável com muitos operadores e deseja o seu addTo , o Scala oferece outra opção: conversões implícitas. Isso requer duas etapas
Se as conversões implícitas estiverem ativadas e o Scala visualizar
ou
e o item não possui addTo , ele pesquisará no escopo atual as funções implícitas de conversão. Se qualquer uma das funções de conversão retorna um tipo que faz ter um addTo método, isso acontece:
Agora, você pode achar que as conversões implícitas são mais direcionadas ao seu gosto, mas isso aumenta o perigo, porque o código pode fazer coisas inesperadas se você não estiver claramente ciente de todas as conversões implícitas em um determinado contexto. É por isso que esse recurso não é mais ativado por padrão no Scala. (No entanto, enquanto você pode optar por habilitá-lo ou não em seu próprio código, não pode fazer nada sobre seu status nas bibliotecas de outras pessoas. Aqui estão dragões).
Os operadores com terminação de dois pontos são mais feios, mas não representam uma ameaça.
Ambas as abordagens preservam o princípio da separação de preocupações. Você pode fazer parecer que o item conhece a coleção, mas o trabalho é realmente feito em outro lugar. Qualquer outro idioma que deseje fornecer esse recurso deve ser pelo menos tão cuidadoso.
2. Java
Em Java, onde a sintaxe não é extensível por você, o melhor que você pode fazer é criar algum tipo de classe wrapper / decorator que conheça as listas. No domínio em que seus itens são colocados em listas, você pode agrupá-los. Quando eles saírem desse domínio, remova-os. Talvez o padrão de visitantes seja o mais próximo.
3. tl; dr
Scala, como alguns outros idiomas, pode ajudá-lo com uma sintaxe mais natural. Java só pode oferecer padrões feios e barrocos.
fonte
Com o seu método de extensão, você ainda precisa chamar Add () para não adicionar nada útil ao seu código. A menos que seu método addto tenha feito algum outro processamento, não há razão para ele existir. E escrever código que não tem um propósito funcional prejudica a legibilidade e a manutenção.
Se você deixar não genérico, os problemas se tornarão ainda mais aparentes. Agora você tem um item que precisa saber sobre os diferentes tipos de coleções em que pode estar. Isso viola o princípio de responsabilidade única. Além de um item possuir seus dados, ele também manipula coleções. É por isso que deixamos a responsabilidade de adicionar itens às coleções até o trecho de código que está consumindo os dois.
fonte
thing.giveTo(collection)
[e exigircollection
a implementação de uma interface "acceptOwnership"] faria mais sentido do quecollection
incluir um método que assumisse a propriedade. Claro ...Eu acho que você está obcecado com a palavra "adicionar". Tente procurar uma palavra alternativa, como "collect", que daria uma sintaxe mais amigável ao inglês, sem alterações no padrão:
O método acima é exatamente o mesmo que
items.add(item);
, com a única diferença sendo a escolha de palavras diferentes e flui muito bem e "naturalmente" em inglês.Às vezes, apenas a renomeação de variáveis, métodos, classes etc. impedirá a redefinição de impostos do padrão.
fonte
collect
às vezes é usado como o nome de métodos que reduzem uma coleção de itens em algum tipo de resultado singular (que também pode ser uma coleção). Essa convenção foi adotada pela API Java Steam , que tornou imensamente popular o uso do nome nesse contexto. Eu nunca vi a palavra usada no contexto que você mencionou na sua resposta. Prefiro ficar comadd
ouput
push
ouenqueue
. O ponto não é o nome do exemplo específico, mas o fato de um nome diferente estar mais próximo do desejo do OP de gramática em estilo inglês.Embora não exista esse padrão para o caso geral (como explicado por outros), o contexto em que um padrão semelhante tem seus méritos é uma associação bidirecional um para muitos no ORM.
Nesse contexto, você teria duas entidades, digamos
Parent
eChild
. Um pai pode ter muitos filhos, mas cada filho pertence a um pai. Se o aplicativo exigir que a associação seja navegável pelas duas extremidades (bidirecional), você teria umList<Child> children
na classe pai e umParent parent
na classe filho.A associação deve sempre ser recíproca, é claro. As soluções ORM normalmente impõem isso nas entidades que eles carregam. Mas quando o aplicativo está manipulando uma entidade (persistente ou nova), ele deve aplicar as mesmas regras. Na minha experiência, você costumava encontrar um
Parent.addChild(child)
método que chamaChild.setParent(parent)
, que não é para uso público. No último, você normalmente encontra as mesmas verificações implementadas no seu exemplo. A outra rota também pode ser implementada:Child.addTo(parent)
quais chamadasParent.addChild(child)
. Qual rota é melhor difere de situação para situação.fonte
Em Java, uma coleção típica não contém coisas - contém referências a coisas, ou cópias mais precisas de referências a coisas. A sintaxe de membro de ponto, por outro lado, é geralmente usada para agir sobre a coisa identificada por uma referência, e não sobre a própria referência.
Se colocar uma maçã em uma tigela alteraria alguns aspectos da maçã (por exemplo, sua localização), colocar um pedaço de papel que diz "objeto # 29521" em uma tigela não mudaria a maçã de nenhuma maneira, mesmo que isso aconteça. objeto # 29521. Além disso, como as coleções contêm cópias de referências, não há Java equivalente a colocar um pedaço de papel dizendo "objeto # 29521" em uma tigela. Em vez disso, copia-se o número # 29521 para um novo pedaço de papel e mostra-o (não doa) para a tigela, que fará sua própria cópia, deixando o original inalterado.
De fato, como as referências a objetos (os "pedaços de papel") em Java podem ser observadas passivamente, nem as referências nem os objetos identificados por meio deles têm como saber o que está sendo feito com eles. Existem alguns tipos de coleções que, em vez de apenas conter um monte de referências a objetos que nada sabem da existência da coleção, estabelecem uma relação bidirecional com os objetos nela encapsulados. Com algumas dessas coleções, pode fazer sentido solicitar que um método se adicione a uma coleção (especialmente se um objeto for capaz de estar em uma dessas coleções por vez). No entanto, em geral, a maioria das coleções não se encaixa nesse padrão e pode aceitar referências a objetos sem qualquer envolvimento por parte dos objetos identificados por essas referências.
fonte
item.AddTo(items)
não é usado porque é passivo. Cf "O item é adicionado à lista" e "a sala é pintada pelo decorador".Construções passivas são boas, mas, pelo menos em inglês, tendemos a preferir construções ativas.
Na programação OO, o pardigma dá destaque aos atores, e não àqueles com quem atuamos, assim temos
items.Add(item)
. A lista é a coisa que está fazendo alguma coisa. É o ator, então esse é o caminho mais natural.fonte
list.Add(item)
um item que está sendo adicionado à lista ou uma lista que adiciona um item ou sobreitem.AddTo(list)
o item que se adiciona à lista ou eu adiciono o item à lista ou como você disse que o item foi adicionado à lista - todos eles estão corretos. Então a pergunta é quem é o ator e por quê? Um item também é um ator e deseja ingressar em um grupo (lista), e não o grupo que deseja ter esse item ;-) o item atua ativamente para pertencer a um grupo.Parent
propriedade. É certo que o inglês não é o idioma nativo de maio, mas até onde eu sei querer não é passivo, a menos que eu seja querido ou ele queira que eu faça alguma coisa ; -]List<T>
(pelo menos o encontrado emSystem.Collections.Generic
) não atualiza nenhumaParent
propriedade. Como poderia, se deveria ser genérico? Geralmente, é responsabilidade do contêiner adicionar seu conteúdo.