Jak rozpoznać, czy użyć Wzorca Złożonego, Struktury Drzewnej, czy trzeciej implementacji?


14

Mam dwa typy klientów, typ „ obserwatora ” i typ „ podmiotu ”. Oba są powiązane z hierarchią grup .

Obserwator otrzyma (kalendarz) dane z grup, z którymi jest powiązany w różnych hierarchiach. Dane te są obliczane poprzez połączenie danych z grup „nadrzędnych” grupy próbujących zebrać dane (każda grupa może mieć tylko jednego rodzica ).

Podmiot będzie mógł tworzyć dane (które otrzymają Obserwatorzy) w grupach, z którymi są powiązane. Gdy dane są tworzone w grupie, wszystkie „dzieci” grupy również będą miały dane i będą mogły stworzyć własną wersję określonego obszaru danych , ale nadal będą powiązane z utworzonymi oryginalnymi danymi (w w mojej konkretnej implementacji oryginalne dane będą zawierać okres (y) i nagłówek, podczas gdy podgrupy określają pozostałe dane dla odbiorników bezpośrednio powiązanych z ich odpowiednimi grupami).

Jednak gdy podmiot tworzy dane, musi sprawdzić, czy wszyscy dotknięci obserwatorzy mają jakiekolwiek dane, które są w konflikcie z tym, co oznacza, o ile wiem, ogromną funkcję rekurencyjną.

Myślę więc, że można to podsumować faktem, że muszę mieć hierarchię, w której można wchodzić i wychodzić , a niektóre miejsca mogą traktować je jako całość (w zasadzie rekurencję).

Nie mam też na celu rozwiązania, które działa. Mam nadzieję znaleźć rozwiązanie, które jest stosunkowo łatwe do zrozumienia (przynajmniej pod względem architektury), a także wystarczająco elastyczne, aby móc w przyszłości łatwo uzyskać dodatkową funkcjonalność.

Czy istnieje wzorzec projektowy lub dobra praktyka do rozwiązania tego problemu lub podobnych problemów dotyczących hierarchii?

EDYCJA :

Oto projekt, który mam: Diagram klas wraz z metodami.  Klasa „Group” to hierarchia

Klasa „Phoenix” jest tak nazywana, ponieważ nie wymyśliłem jeszcze odpowiedniej nazwy.

Ale poza tym muszę być w stanie ukryć określone działania dla konkretnych obserwatorów , nawet jeśli są do nich przywiązani poprzez grupy.


Trochę nie na temat :

Osobiście uważam, że powinienem być w stanie rozwiązać ten problem na mniejsze problemy, ale jak mi to umknie. Myślę, że dzieje się tak, ponieważ obejmuje wiele funkcji rekurencyjnych, które nie są ze sobą powiązane, oraz różne typy klientów, które muszą uzyskiwać informacje na różne sposoby. Naprawdę nie mogę się otulić. Jeśli ktoś może poprowadzić mnie w kierunku, w jaki sposób stać się lepszym w enkapsulowaniu problemów hierarchicznych, byłbym bardzo szczęśliwy, że to otrzymam.


To brzmi jak problem teorii grafów. Mamy więc wykres przedstawiający hierarchię grup. Każda grupa jest wierzchołkiem na wykresie. Jakie właściwości są prawdziwe? Czy to prawda, że ​​zawsze istnieje unikalny wierzchołek no stopniu 0, podczas gdy co drugi wierzchołek ma stopień co najmniej 1? Czy każdy wierzchołek jest podłączony n? Czy ścieżka jest nwyjątkowa? Gdybyś mógł wymienić właściwości struktury danych i wyodrębnić jej operacje do interfejsu - listy metod - my (I) moglibyśmy wymyślić implementację wspomnianej struktury danych.

Dziękuję za odpowiedź. Istnieje wiele hierarchii grup, które nie są ze sobą powiązane, z wyjątkiem obserwatorów, ale nie sądzę, aby były częścią obiektów graficznych, mają po prostu link do wierzchołków. Każda grupa w hierarchii może mieć tylko 1 rodzic, ale 0 .. * dzieci. Jak zaimplementowałbyś to na wykresie? I tylko hierarchia z 1 grupą wewnątrz będzie miała stopień 0. Dla hierarchii 2 grup i większych wszystkie będą miały równy stopień wejścia i wyjścia co najmniej 1. Spróbuję wymienić odpowiednie metody za godzinę, kiedy będę w pracy.

Czy więc grupy działają podobnie jak podklasowanie w C #: Możesz podklasować jedną klasę podstawową, z wyjątkiem tego, że istnieje las (tj. Drzewa rozłączne)? Cóż, jeśli połączysz wszystkie wskaźniki / referencje, to domyślnie masz już wykres - nie musisz nic więcej robić. Rzecz w tym, że jeśli chcesz efektywnie wykonywać operacje typu „Czy te dwie grupy są w tej samej hierarchii?” „Jaki jest wspólny przodek tych dwóch grup?” itd. potrzebujesz systematycznie analizowanego problemu, aby skorzystać z wszystkich wcześniejszych informacji na temat struktury.

Teraz, gdy widziałem twój schemat, jakie jest twoje pytanie - jeśli chodzi o podejście do projektowania, nie mogę ci w tym pomóc, ponieważ sam jestem nowy w różnych metodach projektowania. Jeśli jednak szukasz wydajnych O(n)algorytmów dla dobrze zdefiniowanej struktury danych, mogę nad tym popracować. Widzę, że nie zastosowałeś żadnych metod mutacji Groupi struktury hierarchii. Czy mam założyć, że będą one statyczne?

1
@Malachi Nie znalazłem odpowiedzi. Niestety nie miałem czasu, aby w pełni to zbadać i musiałem przejść do czegoś innego. Teraz też nie mam czasu, aby się tym przyjrzeć, ale od czasu do czasu sprawdzę moje powiadomienia - a jeśli ktoś odpowie na dobrą odpowiedź, zaakceptuję.
Aske B.,

Odpowiedzi:


1

Oto prosta implementacja „Grupy”, która umożliwia nawigację do katalogu głównego i nawigację po drzewie tego katalogu jako kolekcji.

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

Tak więc - biorąc pod uwagę grupę, możesz chodzić po drzewie tej grupy:

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

Mam nadzieję, że opublikując to, pokazując, jak poruszać się po drzewie (i rozwiewając jego złożoność), możesz być w stanie wyobrazić sobie operacje, które chcesz wykonać na drzewie, a następnie ponownie przejrzeć wzorce, aby zobaczyć co najlepiej dotyczy.


0

Przy ograniczonym widoku, jaki mamy wymagania dotyczące użytkowania lub implementacji twojego systemu, trudno jest uzyskać zbyt szczegółowe informacje. Na przykład można wziąć pod uwagę:

  • czy system jest wysoce współbieżny (wielu użytkowników)?
  • jaki jest stosunek odczytu / zapisu dla dostępu do danych? (wysoki odczyt, niski zapis jest powszechny)

Jeśli chodzi o wzory itp., Mniej martwiłbym się o to, jakie dokładne wzory pojawią się w twoim rozwiązaniu, a bardziej o projekt rzeczywistego rozwiązania. Myślę, że znajomość wzorców projektowych jest przydatna, ale nie na samym końcu: aby użyć analogii pisarza, wzorce projektowe bardziej przypominają słownik często spotykanych fraz, niż słownik zdań, musisz napisać całą książkę od.

Twój diagram wydaje mi się ogólnie odpowiedni.

Jest jeden mechanizm, o którym nie wspomniałeś, a mianowicie posiadanie pewnego rodzaju pamięci podręcznej w hierarchii. Oczywiście musisz to wdrożyć z wielką ostrożnością, ale może to znacznie poprawić wydajność twojego systemu. Oto proste podejście (emptor z zastrzeżeniem):

Dla każdego węzła w hierarchii przechowuj odziedziczone dane w węźle. Rób to leniwie lub aktywnie, to zależy od ciebie. Po dokonaniu aktualizacji hierarchia może albo zregenerować dane pamięci podręcznej dla wszystkich dotkniętych węzłów tam, a następnie, lub ustawić flagi „brudne” w odpowiednich miejscach, a dane, których to dotyczy, można leniwie ponownie wygenerować, jeśli to konieczne.

Nie mam pojęcia, jak odpowiednie jest to w twoim systemie, ale warto rozważyć.

Również to pytanie dotyczące SO może być istotne:

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


0

Wiem, że to trochę oczywiste, ale i tak to powiem, myślę, że powinieneś rzucić okiem na Observer Pattern wspomniane przez ciebie wspomnienie, że masz typ obserwatora i to, co masz, przypomina mi wzorzec obserwatora.

kilka linków:

DoFactory

oodesign

sprawdź to. w przeciwnym razie po prostu koduję to, co masz na diagramie, a następnie używam wzorca projektowego, aby w razie potrzeby uprościć. wiesz już, co musi się wydarzyć i jak powinien działać program. Napisz kod i sprawdź, czy nadal pasuje.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.