Digamos que eu tenha dois tipos de objetos, A e B. O relacionamento entre eles é muitos-para-muitos, mas nenhum deles é o proprietário do outro.
As instâncias A e B precisam estar cientes da conexão; não é apenas uma maneira.
Então, podemos fazer isso:
class A
{
...
private: std::vector<B *> Bs;
}
class B
{
private: std::vector<A *> As;
}
Minha pergunta é: onde coloco as funções para criar e destruir as conexões?
Deveria ser A :: Attach (B), que atualiza os vetores A :: Bs e B :: As?
Ou deveria ser B :: Attach (A), que parece igualmente razoável.
Nenhum desses parece certo. Se eu parar de trabalhar com o código e voltar depois de uma semana, tenho certeza de que não poderei me lembrar se devo fazer A.Attach (B) ou B.Attach (A).
Talvez deva ser uma função como esta:
CreateConnection(A, B);
Mas tornar uma função global também parece indesejável, já que é uma função específica para trabalhar apenas com as classes A e B.
Outra pergunta: se eu me deparar com esse problema / requisito com frequência, posso de alguma forma fazer uma solução geral para ele? Talvez uma classe TwoWayConnection da qual eu possa derivar ou usar dentro de classes que compartilhem esse tipo de relacionamento?
Quais são algumas boas maneiras de lidar com essa situação ... Eu sei como lidar com a situação um-para-muitos "C possui D" muito bem, mas essa é mais complicada.
Editar: Apenas para tornar mais explícito, esta pergunta não envolve problemas de propriedade. Ambos A e B pertencem a outro objeto Z, e Z cuida de todos os problemas de propriedade. Estou interessado apenas em como criar / remover os links muitos para muitos entre A e B.
Pointer
eGestureRecognizer
. Os ponteiros são de propriedade e gerenciados pela classe InputManager. Os GestureRecognizers pertencem a instâncias de Widget, que por sua vez pertencem a uma instância de Tela pertencente a uma instância de App. Os ponteiros são atribuídos aos GestureRecognizers para que eles possam fornecer dados brutos de entrada, mas os GestureRecognizers precisam estar cientes de quantos ponteiros estão atualmente associados a eles (para distinguir gestos de um dedo versus dois dedos, etc.).Respostas:
Uma maneira é adicionar um
Attach()
método público e também umAttachWithoutReciprocating()
método protegido a cada classe. Faça amizadesA
eB
amigos em comum para que seusAttach()
métodos possam chamar os de outrosAttachWithoutReciprocating()
:Se você implementar métodos semelhantes para
B
, não precisará se lembrar de qual classe chamarAttach()
.Tenho certeza de que você poderia encerrar esse comportamento em uma
MutuallyAttachable
classe que herdarA
eB
herdar, evitando repetir a si mesmo e marcar pontos de bônus no Dia do Julgamento. Mas mesmo a abordagem não sofisticada de implementar em ambos os lugares fará o trabalho.fonte
MutuallyAttachable
classe que escrevi para esta tarefa, se alguém quiser reutilizá-la: goo.gl/VY9RB (Pastebin) Editar: Esse código pode ser aprimorado, tornando-a uma classe de modelo.MutuallyAttachable<T, U>
modelo de classe no Gist. gist.github.com/3308058É possível que o próprio relacionamento tenha propriedades adicionais?
Nesse caso, deve ser uma classe separada.
Caso contrário, qualquer mecanismo de gerenciamento de lista recíproco será suficiente.
fonte
Geralmente, quando entro em situações como essa que parecem estranhas, mas não sei exatamente por que, é porque estou tentando colocar algo na classe errada. Quase sempre há uma maneira de tirar algumas funcionalidades da classe
A
eB
tornar as coisas mais claras.Um candidato para mover a associação para é o código que cria a associação em primeiro lugar. Talvez, em vez de chamá-
attach()
lo, simplesmente armazene uma lista destd::pair<A, B>
. Se houver vários locais criando associações, ele poderá ser abstraído em uma classe.Outro candidato a uma movimentação é o objeto que possui os objetos associados,
Z
no seu caso.Outro candidato comum para mover a associação é o código que frequentemente chama métodos
A
ouB
objetos. Por exemplo, em vez de chamara->foo()
, que faz algo internamente com todos osB
objetos associados , ele já conhece as associações e chamaa->foo(b)
cada uma. Novamente, se vários locais o chamarem, ele poderá ser abstraído em uma única classe.Muitas vezes, os objetos que criam, possuem e usam a associação são o mesmo objeto. Isso pode tornar a refatoração muito fácil.
O último método é derivar o relacionamento de outros relacionamentos. Por exemplo, irmãos e irmãs são um relacionamento de muitos para muitos, mas, em vez de manter uma lista de irmãs na classe dos irmãos e vice-versa, você deriva esse relacionamento do relacionamento dos pais. A outra vantagem desse método é que ele cria uma única fonte de verdade. Não é possível que um erro de bug ou tempo de execução crie uma discrepância entre as listas de irmãos, irmãs e pais, porque você possui apenas uma lista.
Esse tipo de refatoração não é uma fórmula mágica que funciona sempre. Você ainda precisa experimentar até encontrar um bom ajuste para cada circunstância individual, mas eu descobri que ele funciona cerca de 95% das vezes. Nos tempos restantes, você apenas tem que viver com o constrangimento.
fonte
Se eu estivesse implementando isso, colocaria Attach em A e B. Dentro do método Attach, chamaria Attach no objeto transmitido. Dessa forma, você pode chamar A.Attach (B) ou B.Attach ( UMA). Minha sintaxe pode não estar correta, não uso c ++ há anos:
Um método alternativo seria criar uma terceira classe, C, que gerencia uma lista estática de pares AB.
fonte