Wywoływanie metod poprzez delegacje

0

Witam. Chciałem rozwiązać pewien problem w projekcie zgodnie z zasadami programowanie obiektowego. Mianowicie uzupełniam pola w klasie poprzez wysyłanie żądań do pewnego API. Nie zawsze wszystkie pola w objekcie muszą posiadać wartość. By nie wysyłać zbędnych żądań do API wykonuję tylko te potrzebne objektowi. Poniżej zamieściłem uproszczony kod odzwierciedlający mój oryginalny kod. Proszę o napisanie czy dobrze rozwiązałem ten problem używając delegacji i czy można to zrobić lepiej.

public class Person
{
 string name;
 string sex;
 int age;
 string address;

 public delegate void UpdateDelegate();
 public UpdateDelegate UpdateFirstOption;
 public UpdateDelegate UpdateSecondOption;

 void UpdateName()
  {
    name = Connection.ConnectForName();
  }
 void UpdateAge()
  {
   age = Connection.ConnectForAge();
  }
 void UpdateAddress()
  {
   address = Connection.ConnectForAddress();
  }

 public Person(string name)
  {
   UpdateFirstOption = UpdateSex;
   UpdateFirstOption += UpdateAge;
   UpdateSecondOption = UpdateSex;
   UpdateSecondOption += UpdateAddress;
  }
}

 static void Main(string[] args)
 {
   foreach(var item in someListOfPersons)
    item.UpdateFirstOption();
  foreach(var item in anotherListOfPersons)
    item.UpdateSecondOption();
}
 
1

Nie wiem co chciałeś osiągnąć, ale kilka rzeczy mi tu brzydko pachnie.

Odpowiadając na oryginalne pytanie: użyłeś delegacji poprawnie składniowo, ale Twoje rozwiązanie (pomijając to co napiszę później) jest dużo mniej czytelne, niż gdybyś w metodach UpdateX używał kilku odwołań zamiast delegaty. Używanie delegat mogło by mieć sens, jakbyś próbował to składać w runtime i nie wiedział jeszcze co chcesz uzupełnić na etapie kompilacji. Delegaty tu to overengineering.
Kolejna uwaga, taka ogólna: zamiast delegat, często można użyć interfejsów: ale to nie zawsze jest najlepsze wyjście gdybyś faktycznie składał kilka rzeczy w runtimie.

Moim zdaniem, znów, nie wiem co chcesz osiągnąć, ale to jest źle zaprojektowane. Po pierwsze, takie operacje jak odwołania do zewnętrznego API nie powinny być wywoływane w metodach wewnątrz obiektu, a przy jego tworzeniu. Można to oczywiście enkapsulować w jakiejś fabryce czy czymkolwiek innym co Ci je będzie tworzyć - osobno z jednymi osobno z drugimi właściwościami. Fabryka zdobędzie potrzebne informacje i potem stworzysz sobie obiekt już normalnie wrzucając odpowiednie rzeczy to konstruktora samego obiektu Person. Od razu stworzysz go takim jaki chcesz.

Ale tu znów kolejna rzecz. Masz jeden obiekt i masz w nim kilka rzeczy, czy na pewno tak chcesz to mieć zrobione? Nie lepiej mieć jakąś klasę bazową i dwie wersje Person? Np - i tu w nazwach ponosi mnie fantazja, bo nie wiem co próbujesz modelować - Receiver dla Person z wiekiem i adresem, oraz CouncilMember dla Person z wiekiem i płcią. Możliwe, że nawet nie powinno być tam wspólnej klasy bazowej i to jest "na silę" żeby sobie nie duplikować kodu, kiedy warto to zrobić, bo przeznaczenie, funkcjonalności dla tych obiektów są zupełnie inne. Wprowadzasz niepotrzebnie wtedy coupling sobie. Oczywiście, nie wiem jak wygląda problem rzeczywisty i czy to ma sens w tym wypadku czy nie, żeby to był jeden i ten sam obiekt.

0

Dzięki za obszerną odpowiedź. Sugerując się twoim wpisem odpuściłem sobie delegacje i stworzyłem klasę bazową i postarałem się lepiej przedstawić problem z jakim się staram poradzić. Czy poniższy kod będzie czytelniejszy i poprawniejszy dla programowania obiektowego?


    public abstract class Person
    {
        string name;
        string sex;
        int age;
        string address;
        List<string> family;

        public List<string> Family
        {
            get
            {
                return family;
            }

            set
            {
                family = value;
            }
        }

        protected void UpdateFamilyMembers()
        {
            family = Connection.ConnectForFamilyMembers(name);
        }
        protected void UpdateSex()
        {
            name = Connection.ConnectForSex(name);
        }
        protected void UpdateAge()
        {
            age = Connection.ConnectForAge(name);
        }
        protected void UpdateAddress()
        {
            address = Connection.ConnectForAddress(name);
        }
        public abstract void Update();
        public Person (string _name)
        {
            name = _name;
        }
    }

    public class  FamilyMember : Person
    {
        public FamilyMember(string _name): base(_name)
        { }
        public override void Update()
        {
            UpdateAge();
            UpdateAddress();
            UpdateSex();
        }
    }

    public class Householder : Person
    {
        public Householder(string _name): base(_name)
        { }
        public override void Update()
        {
            UpdateFamilyMembers();
            UpdateAge();
            UpdateAddress();
            UpdateSex();
        }
    }
}
 

Przykład wykorzystania powyższego kodu:

 static void Main(string[] args)
        {
            List<Person> Family = new List<Person>();
            Person Joe = new Householder("joe");
            Joe.Update();
            foreach (var item in Joe.Family)
            {
                Person familyMember = new FamilyMember(item);
                familyMember.Update();
                Family.Add(familyMember);
            }
        }
 

Jak ewentualnie mógłbym rozwiązać to za pomocą fabryki.

0

Nie, tak jest nawet pod pewnymi względami gorzej. Spróbuj ponownie :-)

Nie używaj statycznych metod. Podziel to rozsądnie. Nie wpychaj updateX na siłę do obiektu. Pobierz te dane wcześniej i stwórz poprawny obiekt od razu. Tę logikę możesz zamknąć w jakimś innym obiekcie ale na ten moment przyjmijmy że nie musisz. To następny krok. Stworzenie sobie takiej fabryki czy providera...

1 użytkowników online, w tym zalogowanych: 0, gości: 1