Como você pode saber se deve usar o Padrão Composto ou uma Estrutura em Árvore ou uma terceira implementação?

14

Eu tenho dois tipos de clientes, um tipo " Observador " e um tipo " Assunto ". Ambos estão associados a uma hierarquia de grupos .

O Observador receberá dados (do calendário) dos grupos aos quais está associado nas diferentes hierarquias. Esses dados são calculados combinando dados de grupos 'pai' do grupo que tenta coletar dados (cada grupo pode ter apenas um pai ).

O Sujeito poderá criar os dados (que os Observadores receberão) nos grupos aos quais estão associados. Quando os dados são criados em um grupo, todos os 'filhos' do grupo também terão os dados, e eles poderão criar sua própria versão de uma área específica dos dados , mas ainda vinculados aos dados originais criados (em Na minha implementação específica, os dados originais conterão período (s) e título, enquanto os subgrupos especificam o restante dos dados para os receptores diretamente vinculados a seus respectivos grupos).

No entanto, quando o Assunto cria dados, ele deve verificar se todos os Observadores afetados têm dados conflitantes , o que significa uma enorme função recursiva, tanto quanto eu posso entender.

Então, acho que isso pode ser resumido ao fato de que preciso ter uma hierarquia na qual você possa subir e descer , e alguns lugares possam tratá-los como um todo (recursão, basicamente).

Além disso, não estou apenas buscando uma solução que funcione. Espero encontrar uma solução que seja relativamente fácil de entender (pelo menos em termos de arquitetura) e também suficientemente flexível para poder receber facilmente funcionalidades adicionais no futuro.

Existe um padrão de design, ou uma boa prática a seguir, para resolver esse problema ou problemas de hierarquia semelhantes?

EDIT :

Aqui está o design que tenho: Diagrama de classes com métodos incluídos.  A classe "Grupo" é a hierarquia

A classe "Phoenix" tem esse nome porque ainda não pensei em um nome apropriado.

Mas além disso, preciso ser capaz de ocultar atividades específicas para observadores específicos , mesmo que elas estejam ligadas a eles através dos grupos.


Um pouco fora do tópico :

Pessoalmente, acho que devo ser capaz de reduzir esse problema a problemas menores, mas me escapa como. Eu acho que é porque envolve várias funcionalidades recursivas que não estão associadas uma à outra e diferentes tipos de clientes que precisam obter informações de maneiras diferentes. Eu realmente não posso envolver minha cabeça em torno disso. Se alguém puder me orientar na direção de como melhorar o encapsulamento de problemas de hierarquia, ficaria muito feliz em receber isso também.

Aske B.
fonte
Isso soa como um problema da teoria dos grafos. Portanto, temos um dígrafo representando a hierarquia de grupos. Cada grupo é um vértice no gráfico. Quais propriedades são verdadeiras? É verdade que sempre existe um vértice único ncom um grau de 0, enquanto todos os outros vértices têm um grau de pelo menos 1? Todo vértice está conectado n? O caminho é núnico? Se você pudesse listar as propriedades da estrutura de dados e abstrair suas operações em uma interface - uma lista de métodos -, nós (I) poderíamos propor uma implementação da referida estrutura de dados.
Obrigado pela sua resposta. Existem várias hierarquias de grupos que não são anexadas uma à outra, exceto através dos Observadores, mas não acho que elas façam parte dos objetos gráficos, elas apenas têm um link para vértices nelas. Cada grupo em uma hierarquia pode ter apenas 1 pai, mas 0 .. * filhos. Como você implementaria isso em um gráfico? E apenas uma hierarquia com 1 grupo terá um grau de 0. Para hierarquias de 2 grupos e maiores, todas terão um grau de entrada e saída igual a pelo menos 1. Vou tentar listar seus métodos relevantes em uma hora, quando estou no trabalho.
Os grupos funcionam da mesma forma que a subclasse em C #: Você pode subclassificar uma classe base, com a exceção de que há uma floresta (isto é, árvores separadas)? Bem, se você conectar todos os ponteiros / referências, implicitamente, você já possui um gráfico - você não precisa fazer mais nada. A coisa é, no entanto, se você deseja executar operações como "Esses dois grupos estão na mesma hierarquia?" "Qual é o ancestral comum desses dois grupos?" etc., você precisa que o problema seja analisado sistematicamente para tirar proveito de tudo o que você sabe com antecedência sobre a estrutura.
Agora que eu vi o seu diagrama, qual é a sua pergunta exatamente - se é sobre a abordagem de design, não posso realmente ajudá-lo neste momento, já que sou novo nas várias metodologias de design. No entanto, se você estiver procurando O(n)algoritmos eficientes para uma estrutura de dados bem definida, posso trabalhar nisso. Vejo que você não adotou nenhum método de mutação Groupe a estrutura das hierarquias. Devo assumir que estes serão estáticos?
1
@ Malachi Não encontrei uma resposta. Infelizmente, não tive tempo de investigar completamente e tive que passar para outra coisa. Também não tenho tempo para analisar agora, mas vou verificar minhas notificações de vez em quando - e se alguém der uma boa resposta viável, vou aceitá-la.
Aske B.

Respostas:

1

Aqui está uma implementação simples de "Grupo" que permite navegar até a raiz e navegar na árvore dessa raiz como uma coleção.

public class Group
{
  public Group Parent
  public List<Group> Children

  public IEnumerable<Group> Parents()
  {
    Group result = this;
    while (result.Parent != null)
    {
      result = result.Parent;
      yield return result;
    }
  }
  public Group Root()
  {
    return Parents.LastOrDefault() ?? this;
  }


  public IEnumerable<Group> WalkTreeBreadthFirst(
  {
    //http://en.wikipedia.org/wiki/Breadth-first_search
    HashSet<Group> seenIt = new HashSet<Group>()
    Queue<Group> toVisit = new Queue<Group>();
    toVisit.Enqueue(this);

    while (toVisit.Any())
    {
      Group item = toVisit.Dequeue();
      if (!seenIt.Contains(item))
      {
        seenIt.Add(item);
        foreach (Group child in item.Children)
        {
          toVisit.Enqueue(child);
        }
        yield return item;
      }
    }
  }

  public static IEnumerable<Group> WalkTreeDepthFirst()
  {
    // http://en.wikipedia.org/wiki/Depth-first_search
    HashSet<Group> seenIt = new HashSet<Group>();
    Stack<Group> toVisit = new Stack<Group>();

    toVisit.Push(this);

    while (toVisit.Any())
    {
      Group item = toVisit.Pop();
      if (!seenIt.Contains(item))
      {
        seenIt.Add(item);
        foreach (Group child in item.Children.Reverse())
        {
          toVisit.Push(child);
        }
        yield return item;
      }
    }
  }
}

Então - dado um grupo, você pode percorrer a árvore desse grupo:

Group myGroup = GetGroup();
Group root = myGroup.Root;
foreach(Group inTree in root.WalkTreeBreadthFirst())
{
  //do something with inTree Group.
}

Minha esperança ao postar isso é que, mostrando como navegar em uma árvore (e dissipando sua complexidade), você poderá visualizar as operações que deseja executar na árvore e revisar os padrões por conta própria para ver o que melhor se aplica.

Amy B
fonte
0

Com a visão limitada que temos dos requisitos de uso ou implementação do seu sistema, é difícil ser muito específico. Por exemplo, coisas que seriam consideradas:

  • o sistema é altamente concorrente (muitos usuários)?
  • qual é a taxa de leitura / gravação do acesso a dados? (leitura alta, baixa gravação é comum)

Quanto aos padrões, etc., eu me preocuparia menos com os padrões exatos que surgem na sua solução e mais com o design da solução real. Eu acho que o conhecimento dos padrões de design é útil, mas não o princípio e o fim: para usar uma analogia de escritor, os padrões de design são mais como um dicionário de frases comumente vistas, em vez de um dicionário de frases, você deve escrever um livro inteiro a partir de.

Seu diagrama geralmente parece bom para mim.

Há um mecanismo que você não mencionou e que é ter algum tipo de cache em sua hierarquia. Obviamente, você deve implementar isso com muito cuidado, mas isso pode melhorar significativamente o desempenho do seu sistema. Aqui está uma abordagem simples (advertência):

Para cada nó em sua hierarquia, armazene dados herdados com o nó. Faça isso de forma preguiçosa ou proativa, isso é com você. Quando uma atualização é feita a hierarquia, você pode regenerar os dados em cache para todos os nós afetados ali mesmo, ou set 'sujos' bandeiras nos lugares apropriados, e ter os dados afetados preguiçosamente re-gerada quando necessário.

Não tenho idéia de quão apropriado isso é no seu sistema, mas pode valer a pena considerar.

Além disso, esta pergunta sobre SO pode ser relevante:

/programming/1567935/how-to-do-inheritance-modeling-in-relational-databases

occulus
fonte
0

Eu sei que isso é meio óbvio, mas vou dizer assim mesmo, acho que você deve dar uma olhada no que Observer Pattern você mencionou ter um tipo de observador e o que você parece com o padrão de observador para mim.

alguns links:

DoFactory

oodesign

verifique isso. caso contrário, eu apenas codificaria o que você tem no seu diagrama e depois usaria o padrão de design para simplificar, se necessário. você já sabe o que precisa acontecer e como o programa deve funcionar. Escreva um código e veja se ele ainda se encaixa.

Malaquias
fonte