Jak zmniejszyć tę klasę?

0

Hej.
Mam taką klasę:
user image

Ma ona służyć do zapisywania zadań jakie czekają nas w przyszłości. Proste w założeniu, aczkolwiek mam mały problem.
jak widać klasa ta posiada dużo atrybutów, a konstruktor i funkcja "SetTask" posiada aż 5 argumentów.

Czy macie może jakiś pomysł jak to skrócić? Wrzuciłbym atrybuty do innej klasy po czym dziedziczyłbym po tej klasie, ale co tu wyodrębnić?

Będę wdzięczny za sugestie. Pozdrawiam.

0

zamiast korzystać z metod typu GetXXX, SetXXX skorzystaj z właściwości.

public string FullName {get;set;} 

http://msdn.microsoft.com/en-us/library/x9fsa0sw.aspx

0

Co to zmieni?
W programowaniu obiektowym nie chodzi o to by bezmyślnie do każdej klasy wklepywać getery i setery do każdego z atrybutów.
Takie podejście nijak się ma do programowania obiektowego, bo równie dobrze mogę dać te atrybuty publiczne.

Pomijam fakt, że w UML nie ma czegoś takiego jak getery i setery, dlatego też nie widać ich na diagramie...

W każdym razie klasa ma mieć w sobie atrybuty nad, którymi to ona ma panować, a nie obiekt innej klasy (bądź ten sam obiekt, ale panujący w całości nad każdą cząstką klasy). Klasa ma ukrywać swoją implementację.

Poza tym wystarczy logicznie pomyśleć, że nie można dać do każdego atrybutu z automatu setera, bo przecież stworzenie zadania niesie za sobą pewne konsekwencje i np modyfikacja późniejsza jakiegoś atrybutu do, którego dano bezmyślnie setera może spowodować błąd. Z tego też powodu metodami poprawnymi jest pisanie funkcji w takich przypadkach, zaś w funkcje zwracające zamienia się na getery.

Mi jednak chodzi o to, w jaki sposób skrócić tę klasę z liczby atrybutów. Ciężko mi tutaj coś wyodrębnić.

0

Poza tym ustawienie wszędzie getera i setera równa się czemuś takiemu:

obj.topic = costam;
obj.text = costam;
obj.datestart = costam;
obj.datestop = costam;
obj.proritytask = task.priority.costam;
obj.done = false;

zaprawdę mądre :]. W ten sposób kod się tylko niepotrzebnie wydłuża na dodatek udostępniając całość swojej klasy do modyfikacji co kompletnie nie powinno mieć miejsca.

0

Wygląda na całkiem normalny model, nie powiedziałbym, że 5 właściwości / atrybutów to dużo.

Natomiast, jeżeli piszesz w C# to absolutnie jestem za tym, by użyć właściwości. Pamiętaj, że możesz zmieniać widoczność getterów i setterów, więc twój argument o późniejszej, dowolnej modyfikacji odpada.
Nie jestem też fanem udostępniania jednej, dużej metody, za pomocą której można ustawić wszystkie właściwości.

Co to jest TranslatorPriority?

0

TranslatorPriority - to funkcja, która zmienia enumerator na tłumaczenie. Konkretnie mamy tam priorytet zadania a w enum jest:
Low = 0,
Medium = 1,
Hard = 2

Funkcja zaś ma to tłumaczyć na zrozumiały dla użytkownika (aplikacji) sposób. W tym przypadku będzie to "mały, średni, wysoki".

Właściwości ustawię, ale nie setery do każdego atrybutu. Tak robią pewne firmy :D Potem są problemy bo idioci podpinają pod setery funkcje...

Ja również nie przepadam za jedną dużą funkcją dlatego chcę tą klasę skrócić. Wiem, że nie jest ona zła w tej postaci jaka jest, ale póki mogę chcę to zrobić jak najlepiej.

0

Wystarczającą informację o priorytecie dostarcza sam enum. Model tego typu sam w sobie nie powinien dostarczać przetłumaczonych fraz. Niech zajmie się tym coś, co ten model będzie wyświetlać.

0

A dlaczego coś powinno ingerować w element danej klasy?
Dla mnie byłoby to dziwne, gdy np do klasy "Task", która ma w sobie enum Priority, wdziera się np klasa "Operation" i tłumaczy poszczególne elementy.
Jak najbardziej da się to zrobić i wiele to nie zmieni, ale wydaje mi się, że klasa Task operuje na elementach, które w niej się znajdują, a inne klasy powinny trzymać łapki z dala od jej elementów :)

0

Idąc twoim tokiem myślenia - wywal wszystkie gettery i zostaw jedną metodę ToString.

0
klasowiec napisał(a):

Poza tym ustawienie wszędzie getera i setera równa się czemuś takiemu:

obj.topic = costam;
obj.text = costam;
obj.datestart = costam;
obj.datestop = costam;
obj.proritytask = task.priority.costam;
obj.done = false;

nie koniecznie
w c# możesz napisać:

Task task = new Task { topic = "costam", text = "tekst", prioritytask = Priority.High }

wytłumacz może lepiej swoją klasę
po co tam funkcja SetTask? Dla mnie tworzy ona całkiem nowy task zastępując ten który jest więc w ogóle nie powinna istnieć - zamiast tego raczej powinno się usunąć referencję do starego taska i stworzyć nowy

TranslatorPriority to "priorytet translatora" a nie "translator priorytetów", wprowadzasz zamęt tą łamaną angielszczyzną

zamiast robić funkcję, rozszerz enumerator Priority o funkcję tłumaczącą:

public static String Translate(this Priority a)
{
  // tutaj tłumaczysz
}

a potem używasz dużo ładniej:

task.priority = Priority.High;
MessageBox.Show(task.priority.Translate());

(nawiasem mówiąc znowu tu napisałeś "prioritytask" zamiast "taskpriority" albo po prostu po ludzku "priority" - bo wiadomo że taska skoro to własność taska...)

cała klasa nie ma właściwie żadnych metod cokolwiek robiących - dla mnie powinna być zwykłą strukturą

0

nie koniecznie
w c# możesz napisać:Task task = new Task { topic = "costam", text = "tekst", prioritytask = Priority.High }

W żaden sposób nie jest to estetyczne.
Równie dobrze mogę napisać:

obj.topic = costam;
obj.text = costam;
obj.datestart = costam;
obj.datestop = costam;
obj.proritytask = task.priority.costam;
obj.done = false;

i dać ten wytwór w regiony, ale nie jest to rozwiązanie problemu, a jedynie zamaskowanie.

wytłumacz może lepiej swoją klasę
po co tam funkcja SetTask? Dla mnie tworzy ona całkiem nowy task zastępując ten który jest więc w ogóle nie powinna istnieć - zamiast tego raczej powinno się usunąć referencję do starego taska i stworzyć nowy

Klasa ta ma być częścią organizera osobistego.
Obiekty z tej klasy będą przechowywane w liście (odpowiedni interfejs lub klasa abstrakcyjna się tym zajmie)
Obiekt tej klasy może być tworzony, edytowany i usuwany. Edycja jednak dotyczy całego obiektu, a nie poszczególnego elementu.
Jest to zabezpieczenie przed np głupim zmienieniem danych osobowych w klasie "Personal".

TranslatorPriority to "priorytet translatora" a nie "translator priorytetów", wprowadzasz zamęt tą łamaną angielszczyzną

Angielski u mnie kuleje, więc dziękuję za rady.

zamiast robić funkcję, rozszerz enumerator Priority o funkcję tłumaczącą:

public static String Translate(this Priority a)
{
// tutaj tłumaczysz
}

a potem używasz dużo ładniej:

task.priority = Priority.High;
MessageBox.Show(task.priority.Translate());

Dobra rada.
Ja za bardzo myślałem o interfejsie aplikacji, gdyż funkcja ta miała tłumaczyć wszystkie elmentu enuma, w celu wylistowania go w comboboxie. Czyli użytkownik będzie miał wybór wybrania danego priorytetu. Po to była moja funkcja.

cała klasa nie ma właściwie żadnych metod cokolwiek robiących - dla mnie powinna być zwykłą strukturą

Tak jak powiedziałem, atrygnuty nie powinny być widoczne na zewnątrz. To eliminuje strukturę.
Poza tym funkcjonalności dopiero się rozwijają, więc klasa się rozwinie.

0
klasowiec napisał(a):

Pomijam fakt, że w UML nie ma czegoś takiego jak getery i setery, dlatego też nie widać ich na diagramie...

Serio ktoś zabrania używać właściwości w UML?

Mi jednak chodzi o to, w jaki sposób skrócić tę klasę z liczby atrybutów. Ciężko mi tutaj coś wyodrębnić.

Po co?

klasowiec napisał(a):

TranslatorPriority - to funkcja, która zmienia enumerator na tłumaczenie. Konkretnie mamy tam priorytet zadania a w enum jest:
Low = 0,
Medium = 1,
Hard = 2

Funkcja zaś ma to tłumaczyć na zrozumiały dla użytkownika (aplikacji) sposób. W tym przypadku będzie to "mały, średni, wysoki".

Czyli w jednej klasie mieszasz logikę biznesową z logiką prezentacji, jak na razie to niezły początek bałaganu i naruszania zasad OOP.

klasowiec napisał(a):

nie koniecznie
w c# możesz napisać:Task task = new Task { topic = "costam", text = "tekst", prioritytask = Priority.High }

W żaden sposób nie jest to estetyczne.
Równie dobrze mogę napisać:

obj.topic = costam;
obj.text = costam;
obj.datestart = costam;
obj.datestop = costam;
obj.proritytask = task.priority.costam;
obj.done = false;

i dać ten wytwór w regiony, ale nie jest to rozwiązanie problemu, a jedynie zamaskowanie.

Serio dla Ciebie 10 linijek kodu w regionie to to samo, co jeden inicjalizator obiektu? :|

0
klasowiec napisał(a):

obj.topic = costam;
obj.text = costam;
obj.datestart = costam;
obj.datestop = costam;
obj.proritytask = task.priority.costam;

zaprawdę mądre :]

Tak, to jest mądre (zakładając, że to są właściwości). Jak chcesz inaczej przekazać dane do obiektu, telepatycznie? Co jak będziesz miał 20 argumentów? Ogólnie to wykazujesz objawy choroby obiektowej i prawisz jakieś mądrości z kosmosu o setterach, kiedy twój kod nie spełnia podstawowych zasad OOP. Poczytaj sobie o SOLID, zasadach spójności i powiązaniach między klasami bo zmierzasz w kierunku wzorca "The Blob".

klasowiec napisał(a):

Ciężko mi tutaj coś wyodrębnić.

Wyodrębnij sobie DateRange (start, stop) jak cie to tak boli, będziesz miał argument mniej.

0

Dzięki poradziłem już sobie wyodrębniając atrybuty do osobnych klas i teraz wygląda to o wiele lepiej. (i łatwiej nad tym zapanować)

Poza tym chciałbym Wam pokazać o co chodzi z ukrywaniem pewnych atrybutów.
Projekt posiada między innymi klasę: Note.
Klasa ta posiada takie atrybuty jak: Topic (string), Text(string), Create(datetime), Edit(datetime).

Założenie jest takie, że tworząc notatkę kompletnie nikogo nie powinno obchodzić Create oraz Edit bo te atrybuty same się ustawiają. (wiadomo data tworzenia, a potem data edycji).
Idąc Waszym tokiem rozumowania musiałbym dać do tego setera i myśleć, że to fajny kod.

Ja zaś rozdzieliłem to na klasy article(Topic, Text) - gdyż wiele klas w projekcie posiada te dwa atrybuty, toteż rozdzielenie tego na klasy było bardzo dobrym pomysłem ze względu na elastyczność i łatwość modyfikacji.
Oraz zgodnie z radą powyżej klasę DateRange (Create, Edit), gdyż także wiele klas posiada dwie daty, które w pewien sposób są ze sobą powiązane)

W ten sposób każda klasa w konstruktorze ma nie więcej jak trzy argumenty. No i zaoszczędziłem miejsce na funkcje, które operują na wydzielonych danych.

0
klasowiec napisał(a):

Klasa ta posiada takie atrybuty jak: Topic (string), Text(string), Create(datetime), Edit(datetime).

Create i Edit to nie są dobre nazwy dla atrybutów. Atrybuty powinno nazywać się rzeczownikami, nie czasownikami.

Ja zaś rozdzieliłem to na klasy article(Topic, Text) - gdyż wiele klas w projekcie posiada te dwa atrybuty, toteż rozdzielenie tego na klasy było bardzo dobrym pomysłem ze względu na elastyczność i łatwość modyfikacji.

Dlaczego niby klasa z czterema atrybutami jest nieelastyczna i trudna w modyfikacji, a dwie klasy, każda z dwoma atrybutami, już tak?

Oraz zgodnie z radą powyżej klasę DateRange (Create, Edit), gdyż także wiele klas posiada dwie daty, które w pewien sposób są ze sobą powiązane)

A czy poszczególne obiekty DataRange będą współdzielone między różnymi obiektami? Bo jeśli nie, to utrudniasz jedynie sobie życie.

No i zaoszczędziłem miejsce na funkcje, które operują na wydzielonych danych.

Na czym polega "zaoszczędzenie miejsca na funkcje"? :|

0

następny "inteligętny" inaczej (czyżby rosła nam konkurencja dla maszynaz) - i oczywiście nie wpadłeś na to, że my nie wiemy co ty tam masz w tym swoim programie. Idąc twoim tokiem rozumowania to mogę ci zaproponować jeszcze lepszy podział - stwórz klasę Topic, Text, CreateDate i EditDate. Tym sposobem każda klasa będzie miała tylko jeden atrybut i będzie jeszcze bardziej cool

0

Create i Edit to nie są dobre nazwy dla atrybutów. Atrybuty powinno nazywać się rzeczownikami, nie czasownikami.

Muszę się zgodzić. Nad nazwami będę usiał jeszcze trochę posiedzieć.

Dlaczego niby klasa z czterema atrybutami jest nieelastyczna i trudna w modyfikacji, a dwie klasy, każda z dwoma atrybutami, już tak?

Bo atrybuty te występują także w innych klasach.
Załóżmy masz klasę: Car oraz Bike.
Wobu tych klasach masz coś takiego jak Engine. Pierwotnie Engine mógłbyś w obu tych klasach rozpisać jako atrybuty czyli:

  • model
  • power
    etc.

Teraz masz np 10 klas i każda z nich posiada "model" oraz "power". Oczywistym jest, że należy te atrybuty umieścić w osobnej klasie.
W przeciwnym wypadku zrobi się bałagan, a co ważniejsze aplikacja nie będzie łatwa do ewentualnej modyfikacji.

Im mniej kodu tym lepiej, dotyczy to również atrybutów.

A czy poszczególne obiekty DataRange będą współdzielone między różnymi obiektami? Bo jeśli nie, to utrudniasz jedynie sobie życie.

No właśnie tak będzie. :)

Na czym polega "zaoszczędzenie miejsca na funkcje"?

Załóżmy, że masz tą klasę DateRange.
Gdybyś tego nie wydzielił w osobną klasę to dla każdej klasy, która posiadałaby atrybuty : Create oraz Edit musiałbyś pisać osobne funkcje np liczące różnicę dni pomiędzy tymi datami.
Co to oznacza?
Ano to, że dla N klas masz N funkcji. W razie modyfikacji musisz wykonywać N poprawek.

następny "inteligętny" inaczej (czyżby rosła nam konkurencja dla maszynaz) - i oczywiście nie wpadłeś na to, że my nie wiemy co ty tam masz w tym swoim programie. Idąc twoim tokiem rozumowania to mogę ci zaproponować jeszcze lepszy podział - stwórz klasę Topic, Text, CreateDate i EditDate. Tym sposobem każda klasa będzie miała tylko jeden atrybut i będzie jeszcze bardziej cool

Z dziećmi nie rozmawiam.

0

dobra rób tam dalej po swojemu a na forum cytuj podręcznikowe przykłady i zgrywaj mądrego
obudzisz się potem

0
klasowiec napisał(a):

Poza tym chciałbym Wam pokazać o co chodzi z ukrywaniem pewnych atrybutów.

Ok, wytłumaczyłeś enkapsulację, tylko niektórzy odnoszą się do tego co widzą na pierwszym screenie, czyli metodę SetTask. Rozważ też takie sytuacje jak prywatne settery, czy DTO.

klasowiec napisał(a):

W ten sposób każda klasa w konstruktorze ma nie więcej jak trzy argumenty.

Ale to nie dlatego, że fajnie wyodrębniłeś parę atrybutów, ale dlatego, że masz prosty przypadek. Te wyodrębnione obiekty też musisz jakoś wcześniej stworzyć. Czyli zaraz będziesz potrzebował metody wytwórczej. Wyobraź sobie teraz jej sygnaturę ;)

klasowiec napisał(a):

W każdym razie klasa ma mieć w sobie atrybuty nad, którymi to ona ma panować, a nie obiekt innej klasy

Poczytaj o agregacji, asocjacji.

klasowiec napisał(a):

Dla mnie byłoby to dziwne, gdy np do klasy "Task", która ma w sobie enum Priority, wdziera się np klasa "Operation" i tłumaczy poszczególne elementy

Jedna z podstawowych zasad programowania obiektowego jest zasada pojedynczej odpowiedzialności. W uproszczeniu klasa powinna się zajmować jedną rzeczą.

klasowiec napisał(a):

Gdybyś tego nie wydzielił w osobną klasę to dla każdej klasy, która posiadałaby atrybuty : Create oraz Edit musiałbyś pisać osobne funkcje np liczące różnicę dni pomiędzy tymi datami.
Co to oznacza?
Ano to, że dla N klas masz N funkcji. W razie modyfikacji musisz wykonywać N poprawek.

Albo masz funkcję daysBetween(Date start, Date end), która poprawia enkapsulację bo masz jedną metodę mniej, która ma dostęp do prywatnych atrybutów.

klasowiec napisał(a):

Poza tym funkcjonalności dopiero się rozwijają, więc klasa się rozwinie.

I tego tu się ludzie boją, kolejna zasada z zakresu SOLID, że klasa powinna być otwarta na rozszerzenia, ale zamknięta na modyfikację. I jako, że jesteś początkujący to podstawowa zasada - unikaj dziedziczenia i stosuj kompozycję.

0

Ok, pomińmy to. Zrobię tak jak robię póki co, na razie kod wygląda ładnie.
Jedyna rzecz, która jest brzydka to lekkie łancuchy na zasadzie: obj.jakisGet.jakisInnyGet.WKoncuTooCoNamChodzilo

Mam jednak pytanie z innej beczki. Jak często i w jakich sytuacjach używacie metod statycznych?
Pytam, gdyż ja uważam, że metoda statyczna ma sens w konkretnych przypadkach - wtedy, gdy nie działa na obiekcie / atrybucie swojej klasy.
Np. funkcja z klasy math.Max(int a, int b);

Natomiast np. funkcja zwracająca nam pewną wartość z tablicy (atrybut klasy) : Search(string s); nie powinna być statyczna.

To są dość oczywiste przypadki, jednak może macie jakieś rady?

Proszę o rady i biorę je pod uwagę. Nie obrażajcie się jeśli z niektórymi się nie zgodzę, po prostu chcę rozważyć pewne możliwości i rady innych :)

0
klasowiec napisał(a):

Bo atrybuty te występują także w innych klasach.
Załóżmy masz klasę: Car oraz Bike.
Wobu tych klasach masz coś takiego jak Engine. Pierwotnie Engine mógłbyś w obu tych klasach rozpisać jako atrybuty czyli:

  • model
  • power
    etc.

Owszem, ale o ile w Twoim akademickim przykładzie wyodrębnianie Engine, który ma wiele swoich atrybutów z Car może mieć jeszcze jakiś sens, to robienie oddzielnej encji na raptem dwie daty już niekoniecznie.

A czy poszczególne obiekty DataRange będą współdzielone między różnymi obiektami? Bo jeśli nie, to utrudniasz jedynie sobie życie.

No właśnie tak będzie. :)

W takim razie rozpatrzmy taki przypadek:
Tworzymy sobie dwa Taski: "posprzątać pokój" i "naprawić samochód", obu przypisujesz ten sam obiekt DataRange z StartDate=2012-09-12 i EndDate=2012-09-15. Zatem dwa obiekty Task współdzielą jeden obiekt DataRange, jest fajnie.

Niestety, okazuje się, że sprzątanie pokoju trwa jednak dłużej niż naprawa samochodu i trzeba przestawić datę końcową na 2012-09-17. Przestawiamy ją dla zadania "sprzątanie pokoju" i co się dzieje? "naprawa samochodu" też ma zmienioną datę! Absurd, trzeba to poprawić. Jak? Tworząc nowy obiekt DataRange i ustawiając mu inne daty.

W efekcie są dwa Taski i dwa powiązane z nimi DataRange, nie ma żadnego współdzielenia jest za to bardziej skomplikowana logika edycji dat... I po co tak?

Załóżmy, że masz tą klasę DateRange.
Gdybyś tego nie wydzielił w osobną klasę to dla każdej klasy, która posiadałaby atrybuty : Create oraz Edit musiałbyś pisać osobne funkcje np liczące różnicę dni pomiędzy tymi datami.

Niby dlaczego osobne? Napisałbym jedną i nie miałbym N poprawek dla N klas. (O ile oczywiście byłoby to potrzebne, bo w tym przypadku nie bardzo, typy datowe udostępnia chyba każdy język.)

To dobrze, że przejmujesz się projektowaniem aplikacji, wiele osób tego nie robi. Ale Ty aż za bardzo chcesz to dzielić i kombinować. ;)

0

Owszem, ale o ile w Twoim akademickim przykładzie wyodrębnianie Engine, który ma wiele swoich atrybutów z Car może mieć jeszcze jakiś sens, to robienie oddzielnej encji na raptem dwie daty już niekoniecznie.

Popatrz jednak na to z innej strony.

Dwie daty mogą być ze sobą związane na wiele sposobów. Można z nich wyciągnąć różnicę dat (w jakiejkolwiek postaci).
W takiej klasie może powstać około 5 funkcji. Gdybym tego nie wyodrębnił to musiałbym pisać te 5 funkcji w każdej klasie mającej 2 daty.
(dziedziczenie tu nie wchodzi w grę, gdyż klasy już dziedziczą po innych klasach)

W takim razie rozpatrzmy taki przypadek:
Tworzymy sobie dwa Taski: "posprzątać pokój" i "naprawić samochód", obu przypisujesz ten sam obiekt DataRange z StartDate=2012-09-12 i EndDate=2012-09-15. Zatem dwa obiekty Task współdzielą jeden obiekt DataRange, jest fajnie.

Niestety, okazuje się, że sprzątanie pokoju trwa jednak dłużej niż naprawa samochodu i trzeba przestawić datę końcową na 2012-09-17. Przestawiamy ją dla zadania "sprzątanie pokoju" i co się dzieje? "naprawa samochodu" też ma zmienioną datę! Absurd, trzeba to poprawić. Jak? Tworząc nowy obiekt DataRange i ustawiając mu inne daty.

W efekcie są dwa Taski i dwa powiązane z nimi DataRange, nie ma żadnego współdzielenia jest za to bardziej skomplikowana logika edycji dat... I po co tak?

To nie tak ma działać.
Tworzymy sobie taska:
Posprzątać pokój. Tworzymy go i przypisujemy wartości między innymi dwie daty.

Task ClearRoom = new Task();
ClearRoom.SetTask("Posprzątać pokój");
ClearRoom.SetDateRange(new DateTime(1, 1, 2012), new DateTime(2, 1, 2012));

następnie tworzymy taska "naprawić samochód":

Task RepairCar = new Task();
RepairCar.SetTask("Naprawić samochód");
RepairCar.SetDateRange(new DateTime(1, 1, 2012), new DateTime(2, 1, 2012));

UWAGA:
Jak widzicie zrezygnowałem z konstruktora mającego parametry. To ze względu na to, że każde zadanie można grupować oraz podawać informacje dodatkowe (zastosowałem dziedziczenie), więc konstruktor i tak byłby duży i zamiast tego tworzę 3-4 funkcje ustawiające określone dane.

Teraz modyfikując np "Naprawić samochód" wystarczy zrobić:
RepairCar.SetDateRange([...])

i po sprawie.
Tak to ma działać.

Wyodrębniłem pewne informacje w celu napisania kodu specjalnie dla tych dat. (wspominanych wcześniej funkcji różnic dat)

Niby dlaczego osobne? Napisałbym jedną i nie miałbym N poprawek dla N klas. (O ile oczywiście byłoby to potrzebne, bo w tym przypadku nie bardzo, typy datowe udostępnia chyba każdy język.)

Nie mógłbyś napisać jednej, bo klasy by nie mogły po tym dziedziczyć. Już dziedziczą po innej klasie odpowiadającej za posiadanie grupy i informacji.

W rezultacie miałbyś:
Class Note
{
[...]
DateTime Create;
DateTime Edit;

[Funkcje dla dat]
}

Class Task
{
[...]
DateTime Start;
DateTime Stop;

[Te sam efunkcje dla dat]
}

No chyba, że da się te funkcje wydzielić jakoś :)?

To dobrze, że przejmujesz się projektowaniem aplikacji, wiele osób tego nie robi. Ale Ty aż za bardzo chcesz to dzielić i kombinować

Wiem, że strasznie kombinuję. :)
I wiem, że wymyślam różne dziwactwa, ale... o to mi właśnie chodzi. Już kiedyś napisałem taki program właśnie bez dzielenia szczególnego itp., teraz zaś chciałbym spróbować napisać inaczej - łatwiej / ładniej.
Stąd moje długie przemyślenia na temat projektu i tego jak to ma ze sobą wszystko działać.

0
klasowiec napisał(a):

Mam jednak pytanie z innej beczki. Jak często i w jakich sytuacjach używacie metod statycznych?

Zdania są podzielone, zależy też od języka bo w niektórych nie ma metod statycznych. IMHO używaj ich (niekoniecznie z tej samej klasy) kiedy możesz, a zwykłe metody kiedy nie masz innego wyjścia.

klasowiec napisał(a):

Natomiast np. funkcja zwracająca nam pewną wartość z tablicy (atrybut klasy) : Search(string s); nie powinna być statyczna.

Nie do końca. Powinieneś pamiętać o wydzieleniu funkcjonalność szukania. Ogólnie nad tym musisz trochę pomyśleć, czasami lepiej jest napisać CarMover.move(car), niż car.move(). Choć na początku może ci to się wydawać niezgodne z programowaniem obiektowym.

klasowiec napisał(a):
RepairCar.SetDateRange(new DateTime(1, 1, 2012), new DateTime(2, 1, 2012));

Ok jak już wyodrębniłeś to nie wiąż ich tak mocno ze sobą, lepiej utwórz obiekt DateRange i następnie go przekaż przez SetDateRange. A główną zaletą takiego wydzielenia (DateRange) jest zwiększenie abstrakcji kodu. Pracujesz na pewnym koncepcie jakim jest zakres dat, a nie dwóch losowych datach, które mogą oznaczać wszystko.

klasowiec napisał(a):

Jak widzicie zrezygnowałem z konstruktora mającego parametry. To ze względu na to, że każde zadanie można grupować oraz podawać informacje dodatkowe (zastosowałem dziedziczenie), więc konstruktor i tak byłby duży i zamiast tego tworzę 3-4 funkcje ustawiające określone dane.

To fajnie, że jednak settery się do czegoś przydają ;) Ale for the love of God, unikaj dziedziczenia. Task nie powinien dziedziczyć po grupie, tylko mieć grupę, nie powinien dziedziczyć po "informacjach dodatkowych" tylko mieć informacje dodatkowe. Kompozycja ftw.

klasowiec napisał(a):

Nie mógłbyś napisać jednej, bo klasy by nie mogły po tym dziedziczyć. Już dziedziczą po innej klasie odpowiadającej za posiadanie grupy i informacji. No chyba, że da się te funkcje wydzielić jakoś :)?

Jeszcze tego by brakowało, żeby Task dziedziczył po DateRange. W końcu to jest rodzaj jakiegoś zakresu dat ;) Przykład jak to można wydzielić pokazałem wcześniej, metoda statyczna DateHelper.daysBetween(Date from, Date to). Masz wyodrębnioną funkcjonalność, którą możesz używać dla różnych losowych dat (code reuse), a nie tylko DateRange. Gdzie na przykład funkcja może zwracać wartość ujemną, co w przypadku zakresu dat byłoby błędem.

0

Zdania są podzielone, zależy też od języka bo w niektórych nie ma metod statycznych. IMHO używaj ich (niekoniecznie z tej samej klasy) kiedy możesz, a zwykłe metody kiedy nie masz innego wyjścia.

IMHO bardzo złe podejście... Promujące pisanie kodu praktycznie proceduralnego. To metod zwykłych należy używać kiedy się da, a metod statycznych wtedy kiedy ma sens ich wykonanie bez instancji klasy (czyli, w podejściu obiektowym, niezbyt często).
Dość łatwo napisać kod używając tylko i wyłącznie metod statycznych, ale trudno to nazwać dobrym kodem ;].

Tym dziwniejsze że reszta Twojego posta jest sensowna i się pod nią zupełnie podpisuję.

0

Rozciągając myśl o metodach statycznych, tak samo np. singleton jest przez wielu (me included) uważany za anty-wzorzec. Niewielka jest tak naprawdę różnica, gdy stworzymy jedną instancję obiektu w naszym composition-root i odpowiednio ją przekażemy albo pójdziemy dalej i użyjemy np. kontenera IoC. Wygoda stosowania będzie podobna, a testowanie takiego kodu o wiele łatwiejsze. Rzeczy mają się jeszcze gorzej, gdy nagle będziemy chcieli skorzystać z wielowątkowości, a twórca klasy implementującej singleton nie pomyślał o tym zawczasu (a gdyby była to zwykła klasa, moglibyśmy sobie poradzić z tym problemem sami).

0

Do pisania funkcji proceduralnych już się nie ustosunkuję, gdyż zrobiła to reszta, a ja sam uważam, że pisanie takich funkcji to bardzo złe podejście i fatalny nawyk. Zresztą nie będę powtarzać tego co napisano wyżej.

Ok jak już wyodrębniłeś to nie wiąż ich tak mocno ze sobą, lepiej utwórz obiekt DateRange i następnie go przekaż przez SetDateRange. A główną zaletą takiego wydzielenia (DateRange) jest zwiększenie abstrakcji kodu. Pracujesz na pewnym koncepcie jakim jest zakres dat, a nie dwóch losowych datach, które mogą oznaczać wszystko.

Ojojoj faktycznie, ale w moim kodzie tak właśnie jest (nie tym co podałem tutaj).
Ten kod tutaj to pomyłka pisania z głowy i zapomniałem utworzyć obiekt DateRange.
Ajaja przepraszam, gdyż wprowadziłem Was tutaj w błąd, a chciałem napisać dokładnie tak jak mówisz.

To fajnie, że jednak settery się do czegoś przydają ;) Ale for the love of God, unikaj dziedziczenia. Task nie powinien dziedziczyć po grupie, tylko mieć grupę, nie powinien dziedziczyć po "informacjach dodatkowych" tylko mieć informacje dodatkowe. Kompozycja ftw.

Ooo i tutaj dałeś mi sporo do myślenia, gdyż logicznie podchodząc do tego faktycznie tak musiałoby być... i tak jest.
Znów moja wina, gdyż ja napisałem iż klasy dziedziczą po grupie, a to nie prawda.
Klasa task, date i inne dziedziczą po OperationOrganizer, który ma w sobie grupy i informacje.

Źle się wysłowiłem.

Kurczę ten mój post ostatni jest pełen pomyłek bo pisany na szybko tuż przed wyjściem do pracy. Wprowadziłem nieco zamętu, a przede wszystkim Was w błąd za co przepraszam, gdyż wszystkie podane tu rady... są w moim kodzie :P

Chociaż nie ma tego złego co by na dobre nie wyszło, gdyż teraz przynajmniej wiem, że poprawnie wszystko zapisałem.

Dziękuję za pomoc i pozdrawiam.

0
klasowiec napisał(a):

Teraz modyfikując np "Naprawić samochód" wystarczy zrobić:
RepairCar.SetDateRange([...])

i po sprawie.
Tak to ma działać.

Nie pytałem o to, jak będzie się używało klasy Task. Pytałem, czy każdy obiekt DataRange ędzie powiązany z jednym obiektem Task, czy też obiekty DataRange będą między Taskami współdzielone. Bo coś na początku o współdzieleniu pisałeś. ;]

No chyba, że da się te funkcje wydzielić jakoś :)?

A niby dlaczego się nie da? Normalny człowiek napisałby klasę pomocniczą, a nie kopiował kod w wielu miejscach.

msm napisał(a):

IMHO bardzo złe podejście... Promujące pisanie kodu praktycznie proceduralnego. To metod zwykłych należy używać kiedy się da, a metod statycznych wtedy kiedy ma sens ich wykonanie bez instancji klasy (czyli, w podejściu obiektowym, niezbyt często).
Dość łatwo napisać kod używając tylko i wyłącznie metod statycznych, ale trudno to nazwać dobrym kodem ;].

Jeżeli metoda nie korzysta z pól klasy, to powinna być statyczna.

1

Na statyczne nadają się chyba najbardziej jakieś metody narzędziowe, w stylu zamienić string na int, dane binarne na hexstring czy cokolwiek w ten deseń.

0
msm napisał(a):

złe podejście... Promujące pisanie kodu praktycznie proceduralnego. To metod zwykłych należy używać kiedy się da, a metod statycznych wtedy kiedy ma sens ich wykonanie bez instancji klasy (czyli, w podejściu obiektowym, niezbyt często).
Dość łatwo napisać kod używając tylko i wyłącznie metod statycznych, ale trudno to nazwać dobrym kodem ;].

Ze zwykłą metodą jest taki problem, że ma dostęp do prywatnych atrybutów obiektu, czyli każda dodatkowa metoda to zmniejszenie enkapsulacji. A tego, zgodnie z zasadami OOP, nie pożądamy ;) Trochę jedziecie zabobonami "metody statyczne to zło bo kojarzy się z pisaniem proceduralnym, a metody są cacy bo to jest obiektowe". Czasem trzeba ściągnąć "OO" klapki z oczu. Ogólnie starałem się przekazać co innego i widocznie nie wyszło. Żeby było "bardziej" obiektowo to powiem, że miałem na myśli niestatyczne metody klas nieposiadających atrybutów, które operują na innym obiekcie ;) Jak miałem chorobę obiektową, to pamiętam, że to mi było ciężko załapać. Znacie to, kiedy np. trójkąt miał metodę rysuj?

0

Dziedziczenie obciąża wydajność (malutko, ale zawsze) - chocby o 1 konstruktor więcej w runtime = wloz cialo konstruktora na stos, wykonaj, usun ze stosu.

Dziedziczenie ma w OOP konkretny sens.

Zmniejszenie/podzielenie klasyfikacji obiektu ma sens gdy wchodzi w grę np. Tworzenie obiektow pojedynczej odpowiedzilanosci. Ale i tu trzeba uważać - patrz zasada podstawiania Liskova.

Poza tym liczy sie przeznaczenie klasy, moze ona pelnic role definiowania obiektu-nosnika danych w sieci lub celem utrwalania go w jakims magazynie danych.

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