mnogość klas i dziedziczenie

Odpowiedz Nowy wątek
2015-07-17 12:51

Rejestracja: 4 lata temu

Ostatnio: 4 lata temu

0

Cześć

Jestem na etapie czyszczenia swojej pierwszej aplikacji i mam pewne pytanie do bardziej doświadczonych kolegów.

Powiedzmy ze główna forma danych w aplikacji są obiekty klasy Item. Klasa ta ma 4 typy: Income, Expense, Debt i Savings. Każdy z tych typów używa pewnych parametrów klasy, a pewnych nie. Każdy z nich należy do jakiejś podkategorii -> kategorii -> typu. wszystkie te kolekcje są osobnymi klasami, zawierającymi w sobie kolekcje swojej "podklasy".

w pewnym momencie okazało się ze każda z typów dodaje do klasy nowe parametry, poza tymi które są wspólne. Finalna wersja posiada 12 parametrów. To trochę dużo. Staram się rozwiązać ten problem przy pomocy dziedziczenia i stworzyłem klasę bazową Item i jej podklasy, analogicznie do typów. I tu pojawia się problem ponieważ wątek który ma edytować dane, dostaje od poszczególnej strony obiekt klasy którą ta strona reprezentuje. Na przykład:

private void AddButton_Click(object sender, RoutedEventArgs e)
        {
            Income newItem = new Income();
            Frame.Navigate(typeof(UpdateData), newItem);
        } 

Teraz podczas odpalenia funkcji OnNavigatedTo, strony UpdateData zostaje stworzony obiekt klasy Item. Błąd już powinien być wyraźny. Strona nie wie jaką klasę dziedziczoną dostaje. Nie ma również sposobu na przekonwertowanie klasy bazowej na klasę dziedziczoną. I teraz jak ten problem rozwiązać? Zostawić bałagan w modelu danych i pozostawić 12-to parametrową klasę, czy może użyć 50-o linijkowej funkcji decydującej o tym jakiego typu ma być nowy obiekt?

z góry przepraszam za wszelką ignorancję, której ktoś mógłby się dopatrzeć. Na swoją obronę mogę tylko powiedzieć, że to dopiero 3 miesiąc przygody z programowaniem, więc pewne idee mogą mi się jeszcze trochę mieszać :)

z góry dzięki za odpowiedź

edytowany 2x, ostatnio: JimmyH, 2015-07-17 12:53
12 parametrowa klasta to bład. Max 3. Jeżeli mamy więcej niż 4 to musimy refaktorować. - teo215 2015-07-20 13:15
@teo215 Twierdzenie to jest zbyt zgeneralizowane. Moim zdaniem bzdura. Zasadą jest, żeby klasa realizowała jedno zadanie. Liczba pól w klasie nie ma tutaj znaczenia (oczywiście z zachowaniem zdrowego rozsądku) - Sarrus 2015-07-27 09:56
@Sarrus czyli teoretycznie, jesli klasa ma te powiedzmy 12 wlasciwosci, to jesli nie komplikuje to za bardzo dzialania aplikacji, mozna zostawic ja tak jak jest i nie marnowac czasu na tworzenie klas abstrakcyjnych? - JimmyH 2015-07-27 10:02
Tego nie napisałem. Tu nie chodzi o liczbę parametrów, a o logikę. Zadaj sobie pytanie, czy twoja klasa realizuje jedno zadanie i zawsze korzysta z wszystkich swoich właściwości? Nie upraszczaj aplikacji tylko dlatego, że nie wiesz jak w danym miejscu coś zaimplementować. Zemści się to po jakimś czasie. - Sarrus 2015-07-27 10:06
12 parametrów dla KONSTRUKTORA to jest błąd :) taka poprawka :) - teo215 2015-07-27 10:10

Pozostało 580 znaków

Roczny Programista
2015-07-18 00:03
Roczny Programista
0

Czym są typy a czym parametry klasy? Co rozumiesz przez to co piszesz w całym poście?

może lepiej daj większy wycinek kodu, bądź całość jeśli jest mały, bo ciężko się, przynajmniej mnie, połapać:)

Pozostało 580 znaków

2015-07-27 09:39

Rejestracja: 4 lata temu

Ostatnio: 4 lata temu

0
Roczny Programista napisał(a):

Czym są typy a czym parametry klasy? Co rozumiesz przez to co piszesz w całym poście?

może lepiej daj większy wycinek kodu, bądź całość jeśli jest mały, bo ciężko się, przynajmniej mnie, połapać:)

rozumiem czemu to może być mylące :)

nie chodzi tu oczywiscie o typy zmiennych, a o typy finansów - wydatki, przychody, rachunki oraz oszczędności. Pisząc parametry mam na myśli pola/właściwości, jak nazwa, suma czy data transakcji. kazdy typ finansów ma kilka specyficznych dla siebie właściwości (np. rachunek może być rekurujący [bool IsRecurring] podczas gdy zwykły wydatek jest zawsze jednorazowy) oraz takie, które są wspólne dla wszystkich (jak nazwa, czy suma). Mam nadzieję, że trochę rozwiałem wątpliwości :)

Pozostało 580 znaków

2015-07-27 10:04

Rejestracja: 8 lat temu

Ostatnio: 4 godziny temu

2

Generalnie, jeżeli masz potrzebę rzutowania na poszczególne typy, to coś jest nie tak. Metoda o której piszesz powinna korzystać z klasy Item. Więcej nie da się powiedzieć, bez kodu tych klas.

Jest jeden sposób mniej śmierdzący od case'a, na odpalenie metody w zależności od typu:

void Foo(Item item)
{
    Bar((dynamic) item);
}

void Bar(Income income)
{
// code here
}

void Bar(Expense expense)
{
//code here
}

void Bar(Debt debt)
{
//code here
}

void Bar(Savings savings)
{
//code here
}

Działać działa to elegancko, ale korzysta się z tego w szczególnych przypadkach i jestem przekonany, że twój się do niego nie zalicza. Niemniej warto ten sposób znać.

edytowany 1x, ostatnio: Sarrus, 2015-07-27 10:04

Pozostało 580 znaków

2015-07-27 10:15

Rejestracja: 4 lata temu

Ostatnio: 4 lata temu

0

może będzie prościej jeśli zaprezentuje to na konkretnym przykładzie. Oto klasa Item:

public class Item :IComparable<Item>
    {
        public string Name { get; set; } // ogólna
        public double Amount { get; set; } // ogólna
        public string Type { get; set; } // ogólna
        public string CategoryName { get; set; } // ogólna 
        public string SubcategoryName { get; set; } // ogólna                   
        public int Alert { get; set; } // tylko dla Debt
        public double CollectedAmount { get; set; } // tylko dla Savings

        public bool IsPaid { get; set; } // tylko dla Debt
        public bool IsAlertActive { get; set; } // tylko dla Debt
        public bool IsNewItem { get; set; } // ogólna
        public bool IsRecursive { get; set; } // tylko dla Debt

        public DateTimeOffset Date { get; set; } // ogólna

        public Item()
        {
            Name = "";
            CategoryName = "";
            SubcategoryName = "";
            IsNewItem = true;
            IsRecursive = false;
            IsPaid = false;
            Alert = 0;
            IsAlertActive = false;
            Date = App._date;
            CollectedAmount = 0.0;
        }

        public int CompareTo(Item i)
        {
            return this.Date.CompareTo(i.Date);
        }
    }

w komentarzach zaznaczyłem które właściwości są używane przez typ finansów.

pokaż jeszcze co dalej z tym newItem robisz - Sarrus 2015-07-27 10:35
@Sarrus musiałbym wkleić dwumetrowy kod :) generalnie newItem jest przekazywany stronie EditData i stamtąd jego właściwości są modyfikowane. W funkcji OnNavigatedTo, na podstawie typu ustalane jest które pola formularza mają być widoczne (np. jeśli mamy do czynienia z Income, CheckBox, odpowiadający za to czy item jest powtarzalny, nie jest widoczy a wartosc domyslnie jest ustawiona na false) - JimmyH 2015-07-27 11:03

Pozostało 580 znaków

Odpowiedz

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